6YMDOZIB5LVYLFIDGN2WNT5JTHEAMS4TFPVDEZ3OWXWOKJOC5QDAC
BD5PC25AB5MKVIYDFSDGRZ4YGX4PKW4SMZ3YAYAPNA5HLDVJUR3QC
ATZ3BWSEFJBLVGDUZFESRNHVCIO6ZRZ3ALPANZSVGVO7A5BUAFQQC
CFNFIUJVWV2PHHZKAPBY6GLW4TCX2N66DYH2QCFU5X7D7KE5D4AQC
SXEYMYF7P4RZMZ46WPL4IZUTSQ2ATBWYZX7QNVMS3SGOYXYOHAGQC
VO5OQW4W2656DIYYRNZ3PO7TQ4JOKQ3GVWE5ALUTYVMX3WMXJOYQC
WZVCLZKY34KQBQU6YBGJLQCDADBQ67LQVDNRVCMQVY3O3C3EIWSQC
KQTD46KVVWMJ3W6O55BEJLCVTNTDLUH6QT46AEFT7OU2SELXG4IAC
I52XSRUH5RVHQBFWVMAQPTUSPAJ4KNVID2RMI3UGCVKFLYUO6WZAC
7A2TSC4PAKK3WOH3DMAJASCEC6D5JLJWNFWJTEEBE4CVS4K76PPQC
BXD3IQYNMKMI5BTANCF6NZHZP4CKPWADGJMBT2R3WTMKVKONT5QAC
HMMMKONLCRAXVT7SO2ITTFDOJIQKKVSRIZPXYVPDC34RCBHWMHVAC
KDF6FJRVF72L274BEUJCTUKRFMNL6BDZMTVKDPEYGFX4TC3YOVSQC
crate::apply::clean_obsolete_pseudo_edges(txn, channel, &mut ws.apply)?;
crate::apply::clean_obsolete_pseudo_edges(txn, channel, &mut ws.apply, change_id)?;
crate::apply::repair_cyclic_paths(txn, channel, &mut ws.apply)?;
while pos.pos <= new_vertex.end {
debug!("pos = {:?}", pos);
let vertex = if let Ok(v) = find_block(txn, channel, pos) {
v
} else {
// This means that the only edges to this block were
// removed from the graph, which can only happen if this
// block is the bottommost vertex in the graph.
if cfg!(debug_assertions) {
while pos.pos <= new_vertex.end {
assert!(find_block(txn, channel, pos).is_err());
pos.pos = pos.pos + 1;
}
}
break;
};
while let Ok(vertex) = find_block(txn, channel, pos) {
// Delete all in `del`.
for e in ws.del.drain(..) {
let (a, b) = if e.flag.contains(EdgeFlags::PARENT) {
impl Workspace {
fn perform_del<C: ChangeStore, T: MutTxnT>(
&mut self,
txn: &mut T,
channel: &mut Channel<T>,
vertex: Vertex<ChangeId>,
) -> Result<(), UnrecordError<C::Error, T::Error>> {
for e in self.del.drain(..) {
let (a, b) = if e.flag.is_parent() {
for e in iter_adjacent(txn, channel, a, EdgeFlags::empty(), EdgeFlags::all()).filter(|e| {
!e.flag.contains(EdgeFlags::PARENT) && e.dest == b.start_pos() && !e.introduced_by.is_root()
for e in iter_adj_all(txn, channel, a).filter(|e| {
!e.flag.contains(EdgeFlags::PARENT)
&& e.dest == b.start_pos()
&& !e.introduced_by.is_root()
&& e.introduced_by != current_id
// Optimisation to avoid opening change files
// in the vast majority of cases: if there is
// an edge `e` parallel to a -> b introduced
// by the change that introduced a or b, don't
// reinsert a -> b: that edge was removed by
// `e`.
if !cfg!(debug_assertions) && (a.change == intro_id || b.change == intro_id) {
// Optimisation to avoid opening change files in the vast
// majority of cases: if there is an edge `e` parallel to a ->
// b introduced by the change that introduced a or b, don't
// reinsert a -> b: that edge was removed by `e`.
if a.change == intro_id || b.change == intro_id {
stack.push(find_block(txn, channel, to)?);
visited.clear();
while let Some(v) = stack.pop() {
debug!("remove_zombies, v = {:?}", v);
if !visited.insert(v) {
continue;
}
for e in iter_adjacent(txn, channel, v, EdgeFlags::empty(), EdgeFlags::all()) {
debug!("e = {:?}", e);
// If the edge is a parent-non-block edge, go up.
let mut follow =
e.flag.contains(EdgeFlags::DELETED) && e.introduced_by != change_id;
follow |= e.flag & (EdgeFlags::BLOCK | EdgeFlags::PARENT) == EdgeFlags::PARENT;
if !follow && e.introduced_by != change_id {
continue;
}
if e.flag.contains(EdgeFlags::PARENT) {
stack.push(find_block_end(txn, channel, e.dest)?)
} else {
stack.push(find_block(txn, channel, e.dest)?)
}
if e.introduced_by == change_id {
del.push((v, e))
}
}
}
debug!("remove_zombies = {:#?}", del);
for (v, e) in del.drain(..) {
collect_zombies(txn, channel, change_id, to, ws)?;
debug!("remove_zombies = {:#?}", ws.del_edges);
for (v, mut e) in ws.del_edges.drain(..) {
del_graph_with_rev(
txn,
channel,
e.flag - EdgeFlags::PARENT,
u,
v,
e.introduced_by,
)
.map_err(UnrecordError::Txn)?;
if iter_adjacent(
txn,
channel,
u,
EdgeFlags::empty(),
EdgeFlags::all() - EdgeFlags::DELETED - EdgeFlags::PARENT,
)
.find(|e| e.dest == v.start_pos())
.is_none()
{
let f = if e.flag.contains(EdgeFlags::FOLDER) {
EdgeFlags::FOLDER
} else {
EdgeFlags::empty()
};
put_graph_with_rev(txn, channel, f, u, v, u.change)
.map_err(UnrecordError::Txn)?;
}
e.flag -= EdgeFlags::PARENT;
del_graph_with_rev(txn, channel, e.flag, u, v, e.introduced_by)
.map_err(UnrecordError::Txn)?;
fn collect_zombies<T: TxnT>(
txn: &mut T,
channel: &mut Channel<T>,
change_id: ChangeId,
to: Position<ChangeId>,
ws: &mut Workspace,
) -> Result<(), BlockError> {
ws.stack.push(find_block(txn, channel, to)?);
while let Some(v) = ws.stack.pop() {
debug!("remove_zombies, v = {:?}", v);
if !ws.parents.insert(v) {
continue;
}
for e in iter_adj_all(txn, channel, v) {
debug!("e = {:?}", e);
if !(e.introduced_by == change_id || e.flag & EdgeFlags::bp() == EdgeFlags::PARENT) {
continue;
}
if e.flag.contains(EdgeFlags::PARENT) {
ws.stack.push(find_block_end(txn, channel, e.dest)?)
} else {
ws.stack.push(find_block(txn, channel, e.dest)?)
}
if e.introduced_by == change_id {
ws.del_edges.push((v, e))
}
}
}
ws.stack.clear();
ws.parents.clear();
Ok(())
}
let e = NewEdge {
previous: e.flag,
flag: e.previous,
from: e.from,
to: e.to,
introduced_by: Some(change_hash),
};
let intro = internal(txn, &e.introduced_by, change_id).unwrap();
let intro_ext = e.introduced_by.unwrap_or(change_hash);
&"a\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\nx\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\ny\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\nz\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\nf\n"
&"a\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\nx\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\ny\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\nz\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\nf\n"
Ok(&">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\nx\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\ny\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\nz\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"[..])
Ok(&">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\nx\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\ny\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\nz\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n"[..])
let files_charlie = repo_charlie.list_files();
let mut files_charlie = repo_charlie.list_files();
files_charlie.sort();
// Two files with the same name (file), one of which also has another name (file3). This means that we don't know which one of the two names crate::output will pick, between "file3" and the conflicting name.
// This depends on which file gets output first.
assert_eq!(files_charlie[0], "file");
assert!(files_charlie[1] == "file3" || files_charlie[1].starts_with("file."));
repo_charlie.rename(&files_charlie[1], "file3")?;
txn_charlie.move_file(&files_charlie[1], "file3")?;
let _charlie_solution = record_all(
&mut repo_charlie,
&changes,
&mut txn_charlie,
&mut channel_charlie,
"",
)
.unwrap();
debug_to_file(&txn_charlie, &channel_charlie, "debug_charlie3")?;
output::output_repository_no_pending(
&mut repo_charlie,
&changes,
&mut txn_charlie,
&mut channel_charlie,
"",
true,
)?;
let mut files_charlie = repo_charlie.list_files();
files_charlie.sort();
assert_eq!(files_charlie, &["file", "file3"]);
assert_eq!(conflicts.len(), 2);
match (&conflicts[0], &conflicts[1]) {
(Conflict::ZombieFile { ref path }, Conflict::MultipleNames { .. })
| (Conflict::MultipleNames { .. }, Conflict::ZombieFile { ref path }) => {
assert!(path == "a/b/c/alice" || path == "a/b/c/file")
}
assert_eq!(conflicts.len(), 1);
match conflicts[0] {
Conflict::ZombieFile { ref path } => assert_eq!(path, "a/b/c/alice"),
pub(crate) fn iter_deleted_parents<'db, 'txn: 'db, T: TxnT>(
txn: &'txn T,
channel: &'db Channel<T>,
key: Vertex<ChangeId>,
) -> AdjacentIterator<'txn, T> {
iter_adjacent(
txn,
channel,
key,
EdgeFlags::DELETED | EdgeFlags::PARENT,
EdgeFlags::all(),
)
}
pub(crate) fn iter_adj_all<'db, 'txn: 'db, T: TxnT>(
txn: &'txn T,
channel: &'db Channel<T>,
key: Vertex<ChangeId>,
) -> AdjacentIterator<'txn, T> {
iter_adjacent(txn, channel, key, EdgeFlags::empty(), EdgeFlags::all())
}
/// Possible flags of edges.
#[derive(Serialize, Deserialize)]
pub struct EdgeFlags: u8 {
const BLOCK = 1;
/// A pseudo-edge, computed when applying the change to
/// restore connectivity, and/or mark conflicts.
const PSEUDO = 4;
/// An edge encoding file system hierarchy.
const FOLDER = 16;
/// A "reverse" edge (all edges in the graph have a reverse edge).
const PARENT = 32;
/// An edge whose target (if not also `PARENT`) or
/// source (if also `PARENT`) is marked as deleted.
const DELETED = 128;
/// Possible flags of edges.
#[derive(Serialize, Deserialize)]
pub struct EdgeFlags: u8 {
const BLOCK = 1;
/// A pseudo-edge, computed when applying the change to
/// restore connectivity, and/or mark conflicts.
const PSEUDO = 4;
/// An edge encoding file system hierarchy.
const FOLDER = 16;
/// A "reverse" edge (all edges in the graph have a reverse edge).
const PARENT = 32;
/// An edge whose target (if not also `PARENT`) or
/// source (if also `PARENT`) is marked as deleted.
const DELETED = 128;
}
impl EdgeFlags {
#[inline]
pub(crate) fn db() -> Self {
Self::DELETED | Self::BLOCK
}
#[inline]
pub(crate) fn bp() -> Self {
Self::BLOCK | Self::PARENT
}
#[inline]
pub(crate) fn pseudof() -> Self {
Self::PSEUDO | Self::FOLDER
}
#[inline]
pub(crate) fn alive_children() -> Self {
Self::BLOCK | Self::PSEUDO | Self::FOLDER
}
#[inline]
pub(crate) fn parent_folder() -> Self {
Self::PARENT | Self::FOLDER
}
#[inline]
pub(crate) fn is_deleted(&self) -> bool {
self.contains(EdgeFlags::DELETED)
}
#[inline]
pub(crate) fn is_parent(&self) -> bool {
self.contains(EdgeFlags::PARENT)
}
#[inline]
pub(crate) fn is_folder(&self) -> bool {
self.contains(EdgeFlags::FOLDER)
}
#[inline]
pub(crate) fn is_block(&self) -> bool {
self.contains(EdgeFlags::BLOCK)
}
for &ancestor in alive.iter() {
debug!("put_graph_with_rev {:?} -> {:?}", ancestor, d);
if ancestor == d {
info!(
"repair_missing_up_context, alive: {:?} == {:?}",
ancestor, d
);
continue;
}
debug!("repair_missing_up {:?} {:?}", ancestor, d);
put_graph_with_rev(
txn,
channel,
if d_is_folder {
EdgeFlags::PSEUDO | EdgeFlags::FOLDER
} else {
EdgeFlags::PSEUDO
},
ancestor,
d,
ChangeId::ROOT,
)
.map_err(MissingError::Txn)?;
}
if d_is_folder && c != d {
put_graph_with_rev(
txn,
channel,
EdgeFlags::PSEUDO | EdgeFlags::FOLDER,
c,
d,
ChangeId::ROOT,
)
.map_err(MissingError::Txn)?;
}
repair_regular_up(txn, channel, &alive, d, EdgeFlags::PSEUDO).map_err(MissingError::Txn)?;
files: &[(Vertex<ChangeId>, Edge)],
ws: &mut Workspace,
) -> Result<(), MissingError<T::Error>> {
for &(a, b) in files {
let b = if a.start == a.end {
find_block_end(txn, channel, b.dest)?
} else {
b.dest.inode_vertex()
};
debug!("put_graph_with_rev (files) {:?} -> {:?}", b, a);
assert_ne!(a, b);
put_graph_with_rev(
txn,
channel,
EdgeFlags::FOLDER | EdgeFlags::PSEUDO,
b,
a,
ChangeId::ROOT,
)
.map_err(MissingError::Txn)?;
ws.repaired.insert(a);
alive: &HashSet<Vertex<ChangeId>>,
d: Vertex<ChangeId>,
flag: EdgeFlags,
) -> Result<(), T::Error> {
for &ancestor in alive.iter() {
debug!("put_graph_with_rev {:?} -> {:?}", ancestor, d);
if ancestor == d {
info!(
"repair_missing_up_context, alive: {:?} == {:?}",
ancestor, d
);
continue;
}
debug!("repair_missing_up {:?} {:?}", ancestor, d);
put_graph_with_rev(txn, channel, flag, ancestor, d, ChangeId::ROOT)?;
debug!("repair_missing_down {:?} {:?}", d, descendant);
put_graph_with_rev(
txn,
channel,
EdgeFlags::PSEUDO,
d,
descendant,
ChangeId::ROOT,
)
.map_err(MissingError::Txn)?;
debug!("repair_missing_down {:?} {:?}", d, desc);
put_graph_with_rev(txn, channel, EdgeFlags::PSEUDO, d, desc, ChangeId::ROOT)
.map_err(MissingError::Txn)?;
let source = internal_pos(txn, &e.from, change_id)?;
let source = find_block_end(txn, &channel, source)?;
let target = internal_pos(txn, &e.to.start_pos(), change_id)?;
let target = find_block(txn, &channel, target)?;
debug!(
"repair_context_nondeleted: source = {:?}, target: {:?}",
source, target
);
// Fixing the connection from root to the source: if the source is
// deleted by an unknown patch, or if the target is deleted
// (necessarily by unknown patch), then `target` might be
// disconnected from the alive graph, so we need to fix the context.
let mut deleted_by_unknown = false;
let mut target_is_folder = false;
for v in iter_adjacent(txn, channel, source, EdgeFlags::empty(), EdgeFlags::all()) {
if !v.flag.contains(EdgeFlags::PARENT) {
target_is_folder = v.flag.contains(EdgeFlags::FOLDER);
continue;
}
if !v.flag.contains(EdgeFlags::DELETED) {
continue;
}
if v.introduced_by == change_id || v.dest.change.is_root() || v.introduced_by.is_root() {
continue;
}
// This unwrap is ok, since `v` is in the channel.
let intro = txn.get_external(v.introduced_by).unwrap();
if known(intro) {
// If a known change also delete the context, we're good.
deleted_by_unknown = false;
break;
} else {
// If an unknown change deletes the context, wait: maybe a
// known change will delete it too.
deleted_by_unknown = true;
}
if e.flag.contains(EdgeFlags::FOLDER) {
return Ok(());
debug!("deleted_by_unknown {:?}", deleted_by_unknown);
if deleted_by_unknown {
repair_missing_up_context(txn, channel, ws, inode, source, &[target], target_is_folder)?;
let source = find_block_end(txn, &channel, internal_pos(txn, &e.from, change_id)?)?;
let target = find_block(
txn,
&channel,
internal_pos(txn, &e.to.start_pos(), change_id)?,
)?;
if deleted_by_unknown(txn, channel, source, change_id, &mut known) {
repair_missing_up_context(txn, channel, ws, change_id, inode, source, &[target])?;
// Now, maybe ~source~ was deleted by known changes, but
// accessibility to ~target~ was provided by other edges that got
// deleted by unknown changes.
fn reconnect_target_up<T: MutTxnT>(
txn: &mut T,
channel: &mut Channel<T>,
ws: &mut Workspace,
inode: Position<Option<Hash>>,
target: Vertex<ChangeId>,
change_id: ChangeId,
) -> Result<(), MissingError<T::Error>> {
for v in iter_adjacent(txn, channel, target, EdgeFlags::empty(), EdgeFlags::all()) {
if !v.flag.contains(EdgeFlags::PARENT) || !v.flag.contains(EdgeFlags::DELETED) {
for v in iter_deleted_parents(txn, channel, target) {
if v.dest.change.is_root() || v.introduced_by.is_root() {
}
fn deleted_by_unknown<T: TxnT, K>(
txn: &T,
channel: &Channel<T>,
source: Vertex<ChangeId>,
change_id: ChangeId,
known: &mut K,
) -> bool
where
K: FnMut(Hash) -> bool,
{
let mut deleted_by_unknown = false;
for v in iter_deleted_parents(txn, channel, source) {
if v.dest.change.is_root() || v.introduced_by.is_root() {
continue;
}
if v.introduced_by == change_id || known(txn.get_external(v.introduced_by).unwrap()) {
// If a known change also delete the context, we're good.
return false;
} else {
// If an unknown change deletes the context, wait: maybe a
// known change will delete it too.
deleted_by_unknown = true;
}
}
deleted_by_unknown
#[derive(Debug, Default)]
pub(crate) struct Graphs(
pub HashMap<Position<Option<Hash>>, (Graph, HashMap<Vertex<ChangeId>, crate::alive::VertexId>)>,
);
impl Graphs {
pub(crate) fn get(
&self,
inode: Position<Option<Hash>>,
) -> Result<&(Graph, HashMap<Vertex<ChangeId>, VertexId>), InconsistentChange> {
if let Some(n) = self.0.get(&inode) {
Ok(n)
} else {
Err(InconsistentChange {})
}
}
pub fn split(
&mut self,
inode: Position<Option<Hash>>,
vertex: Vertex<ChangeId>,
mid: ChangePosition,
) {
if let Some((_, vids)) = self.0.get_mut(&inode) {
if let Some(vid) = vids.remove(&vertex) {
vids.insert(Vertex { end: mid, ..vertex }, vid);
vids.insert(
Vertex {
start: mid,
..vertex
},
vid,
);
}
}
}
}
for v in iter_adjacent(
txn,
channel,
dest_vertex,
EdgeFlags::empty(),
EdgeFlags::all() - EdgeFlags::DELETED - EdgeFlags::PARENT,
) {
for v in iter_alive_children(txn, channel, dest_vertex) {
if !edge.flag.contains(EdgeFlags::FOLDER) {
debug!("dest_vertex {:?}, p {:?}", dest_vertex, p);
put_graph_with_rev(
txn,
channel,
EdgeFlags::DELETED | EdgeFlags::BLOCK,
dest_vertex,
p,
change_id,
)
assert!(!edge.flag.contains(EdgeFlags::FOLDER));
debug!("dest_vertex {:?}, p {:?}", dest_vertex, p);
put_graph_with_rev(txn, channel, EdgeFlags::db(), dest_vertex, p, change_id)
let mut u = p;
while let Ok(v) = find_block(txn, channel, u.end_pos()) {
if u != v {
debug!("repair_children_of_deleted: {:?} -> {:?}", u, v);
put_graph_with_rev(
txn,
channel,
EdgeFlags::DELETED | EdgeFlags::BLOCK,
u,
v,
change_id,
)
let mut u = p;
while let Ok(v) = find_block(txn, channel, u.end_pos()) {
if u != v {
debug!("repair_children_of_deleted: {:?} -> {:?}", u, v);
put_graph_with_rev(txn, channel, EdgeFlags::db(), u, v, change_id)
if is_alive(txn, channel, p) || edge.flag.contains(EdgeFlags::FOLDER) {
let p_is_folder = edge.flag.contains(EdgeFlags::FOLDER);
repair_missing_up_context(txn, channel, ws, inode, dest_vertex, &[p], p_is_folder)?;
if is_alive(txn, channel, p) {
repair_missing_up_context(txn, channel, ws, change_id, inode, dest_vertex, &[p])?;
del_graph_with_rev(
txn,
channel,
e.flag - EdgeFlags::PARENT,
p,
dest_vertex,
e.introduced_by,
)
.map_err(MissingError::Txn)?;
e.flag -= EdgeFlags::PARENT;
del_graph_with_rev(txn, channel, e.flag, p, dest_vertex, e.introduced_by)
.map_err(MissingError::Txn)?;
let mut next_file = None;
for v in iter_adjacent(txn, &channel, vertex, EdgeFlags::PARENT, EdgeFlags::all()) {
let mut is_file = false;
let mut it = iter_adj_all(txn, &channel, vertex);
while let Some(v) = it.next() {
if !v.flag.contains(EdgeFlags::DELETED) {
if v.flag.contains(EdgeFlags::FOLDER) {
// check whether `vertex` is a "file" inode,
// i.e. if it has non-folder children. We're never
// in the case of an empty file if we call this
// function.
if next_file.is_none()
&& iter_adjacent(
txn,
&channel,
vertex,
EdgeFlags::empty(),
EdgeFlags::all(),
)
.any(|e| {
e.flag.contains(EdgeFlags::BLOCK)
&& !e.flag.contains(EdgeFlags::PARENT)
&& !e.flag.contains(EdgeFlags::FOLDER)
})
{
if v.flag & EdgeFlags::pseudof() == EdgeFlags::PSEUDO {
continue;
}
if !v.flag.is_deleted() {
if v.flag.is_folder() {
is_file |= it.any(|e| !e.flag.intersects(EdgeFlags::parent_folder()));
if is_file {
if v.flag.contains(EdgeFlags::FOLDER) {
let v_age = txn
.get_changeset(&channel.changes, v.introduced_by, None)
.unwrap();
if let Some((ref mut file, ref mut age)) = next_file {
if v_age > *age {
*age = v_age;
*file = (vertex, v)
}
} else {
next_file = Some(((vertex, v), v_age));
// If `vertex` is a "file" inode (as opposed to a
// "folder" inode), mark it "alive".
if iter_adjacent(txn, &channel, vertex, EdgeFlags::empty(), EdgeFlags::all())
.any(|e| {
!e.flag.contains(EdgeFlags::PARENT)
&& !e.flag.contains(EdgeFlags::FOLDER)
})
{
alive.insert(vertex);
}
if v.flag.is_folder() {
if is_file {
alive.insert(vertex);
files.insert(vertex);
put_down_context(txn, channel, ch, n.inode, ws, down)?;
if down.change == change {
return Err((InconsistentChange {}).into());
}
if put_down_context(txn, channel, ch, ws, down)? && !n.flag.contains(EdgeFlags::FOLDER) {
return Err(LocalApplyError::INCONSISTENT);
}
for (change, _) in ws.deleted_by.iter() {
let flag = n.flag | EdgeFlags::BLOCK | EdgeFlags::DELETED;
put_graph_with_rev(txn, channel, flag, up, vertex, *change)
for change in ws.deleted_by.iter() {
put_graph_with_rev(txn, channel, up_flag, up, vertex, *change)
debug!("put_graph_with_rev {:?} {:?}", up, vertex);
let flag = if n.flag.contains(EdgeFlags::FOLDER) || !vertex.is_empty() {
n.flag | EdgeFlags::BLOCK
} else {
n.flag - EdgeFlags::BLOCK
};
put_graph_with_rev(txn, channel, flag, up, vertex, change).map_err(LocalApplyError::Txn)?;
put_graph_with_rev(txn, channel, n.flag | EdgeFlags::BLOCK, up, vertex, change)
.map_err(LocalApplyError::Txn)?;
} else {
put_graph_with_rev(
txn,
channel,
n.flag - EdgeFlags::BLOCK,
vertex,
down,
change,
)
.map_err(LocalApplyError::Txn)?;
if n.flag.is_folder() {
ws.missing_context.files.insert(down);
if let Some((_, vids)) = ws.missing_context.graphs.get_mut(&inode) {
if let Some(vid) = vids.remove(&k) {
vids.insert(Vertex { end: up.pos, ..k }, vid);
vids.insert(Vertex { start: up.pos, ..k }, vid);
}
}
// The missing context "graphs" are only used at the
// DELETION stage, check that:
assert!(ws.missing_context.graphs.0.is_empty());
if let Some((_, vids)) = ws.missing_context.graphs.get_mut(&inode) {
if let Some(vid) = vids.remove(&k) {
vids.insert(Vertex { end: down.pos, ..k }, vid);
vids.insert(
Vertex {
start: down.pos,
..k
},
vid,
);
}
}
// The missing context "graphs" are only used at the
// DELETION stage, check that:
assert!(ws.missing_context.graphs.0.is_empty());
if let Some((_, vids)) = ws.missing_context.graphs.get_mut(&inode) {
if let Some(vid) = vids.remove(&target) {
vids.insert(
Vertex {
end: n.to.end,
..target
},
vid,
);
vids.insert(
Vertex {
start: n.to.end,
..target
},
vid,
);
}
}
ws.missing_context.graphs.split(inode, target, n.to.end);
debug!("deleting {:?} {:?}", n.previous, n.flag);
let del = del_graph_with_rev(txn, channel, n.previous, source, target, n_introduced_by)
del_graph_with_rev(txn, channel, n.previous, source, target, n_introduced_by)
if let Some((_, vids)) = ws.missing_context.graphs.get_mut(&inode) {
if let Some(vid) = vids.remove(&source) {
vids.insert(
Vertex {
end: from.pos,
..source
},
vid,
);
vids.insert(
Vertex {
start: from.pos,
..source
},
vid,
);
}
}
ws.missing_context.graphs.split(inode, source, from.pos);
if let Some((_, vids)) = ws.missing_context.graphs.get_mut(&inode) {
if let Some(vid) = vids.remove(&target) {
vids.insert(
Vertex {
end: to.start,
..target
},
vid,
);
vids.insert(
Vertex {
start: to.start,
..target
},
vid,
);
}
}
ws.missing_context.graphs.split(inode, target, to.start);
debug!(
"reconnect: parents.len() = {:?} to children.len() = {:?}",
ws.parents.len(),
ws.children.len()
);
debug!(
"reconnect: parents = {:#?} to children = {:#?}",
ws.parents, ws.children
);
if ws.parents.is_empty() {
ws.children.clear();
if ws.parents.is_empty() || ws.children.is_empty() {
for p in ws.parents.drain() {
for c in ws.children.iter() {
if p != *c && is_alive(txn, channel, p) && is_alive(txn, channel, *c) {
put_graph_with_rev(txn, channel, EdgeFlags::PSEUDO, p, *c, ChangeId::ROOT)
for &p in ws.parents.iter() {
debug_assert!(is_alive(txn, channel, p));
for &c in ws.children.iter() {
if p != c {
debug_assert!(is_alive(txn, channel, c));
put_graph_with_rev(txn, channel, EdgeFlags::PSEUDO, p, c, ChangeId::ROOT)
if !b_is_alive {
debug!("deleting {:?} -> {:?}", a, b);
if del_graph_with_rev(
txn,
channel,
p.flag - EdgeFlags::PARENT,
a,
b,
p.introduced_by,
)
.map_err(LocalApplyError::Txn)?
{
// repair down context.
crate::missing_context::repair_missing_down_context(
txn,
channel,
&mut ws.missing_context,
inode,
b,
&[a],
)
.map_err(LocalApplyError::from_missing)?
}
}
} else if b_is_alive {
debug!("deleting {:?} -> {:?}", a, b);
if del_graph_with_rev(
debug_assert!(!b_is_alive);
crate::missing_context::repair_missing_down_context(
.map_err(LocalApplyError::Txn)?
{
// repair up context.
crate::missing_context::repair_missing_up_context(
txn,
channel,
&mut ws.missing_context,
inode,
a,
&[b],
p.flag.contains(EdgeFlags::FOLDER),
)
.map_err(LocalApplyError::from_missing)?
}
} else {
del_graph_with_rev(
.map_err(LocalApplyError::from_missing)?
} else if !p.flag.is_folder() {
crate::missing_context::repair_missing_up_context(
debug!("repairing missing context for {:?}", vertex);
for up in n.up_context.iter() {
let up = find_block_end(txn, channel, internal_pos(txn, &up, change_id)?)?;
if !is_alive(txn, channel, up) {
debug!("repairing missing up context {:?} {:?}", up, vertex);
repair_missing_up_context(
txn,
channel,
&mut ws.missing_context,
n.inode,
up,
&[vertex],
n.flag.contains(EdgeFlags::FOLDER),
)
.map_err(LocalApplyError::from_missing)?
}
}
if !n.flag.contains(EdgeFlags::FOLDER) {
for down in n.down_context.iter() {
let down = find_block(txn, channel, internal_pos(txn, &down, change_id)?)?;
if iter_adjacent(
txn,
channel,
down,
EdgeFlags::PARENT,
EdgeFlags::all() - EdgeFlags::DELETED,
)
.find(|e| e.introduced_by != change_id)
.is_none()
{
debug!("repairing missing down context {:?} {:?}", down, vertex);
repair_missing_down_context(
txn,
channel,
&mut ws.missing_context,
n.inode,
down,
&[vertex],
)
.map_err(LocalApplyError::from_missing)?
}
}
}
debug!("done repairing contexts for {:?}", vertex);
repair_new_vertex_context_up(txn, channel, ws, change_id, n, vertex)?;
repair_new_vertex_context_down(txn, channel, ws, change_id, n, vertex)?;
for e in n.edges.iter() {
assert!(!e.flag.contains(EdgeFlags::PARENT));
if !e.flag.contains(EdgeFlags::DELETED) {
trace!("repairing context nondeleted {:?}", e);
repair_context_nondeleted(
txn,
channel,
&mut ws.missing_context,
n.inode,
change_id,
|h| change.knows(&h),
e,
)
.map_err(LocalApplyError::from_missing)?
}
}
repair_edge_context(txn, channel, ws, change_id, change, n)?;
for atom in change.changes.iter().flat_map(|r| r.iter()) {
if let Atom::EdgeMap(ref n) = atom {
for e in &n.edges {
if e.flag.contains(EdgeFlags::DELETED) {
trace!("repairing context deleted {:?}", e);
repair_context_deleted(
txn,
channel,
&mut ws.missing_context,
n.inode,
change_id,
|h| change.knows(&h),
e,
)
.map_err(LocalApplyError::from_missing)?
}
}
}
}
fn repair_cyclic_paths<T: MutTxnT>(
fn repair_new_vertex_context_up<T: MutTxnT>(
txn: &mut T,
channel: &mut Channel<T>,
ws: &mut Workspace,
change_id: ChangeId,
n: &NewVertex<Option<Hash>>,
vertex: Vertex<ChangeId>,
) -> Result<(), LocalApplyError<T::Error>> {
for up in n.up_context.iter() {
let up = find_block_end(txn, channel, internal_pos(txn, &up, change_id)?)?;
if !is_alive(txn, channel, up) {
debug!("repairing missing up context {:?} {:?}", up, vertex);
repair_missing_up_context(
txn,
channel,
&mut ws.missing_context,
change_id,
n.inode,
up,
&[vertex],
)
.map_err(LocalApplyError::from_missing)?
}
}
Ok(())
}
fn repair_new_vertex_context_down<T: MutTxnT>(
txn: &mut T,
channel: &mut Channel<T>,
ws: &mut Workspace,
change_id: ChangeId,
n: &NewVertex<Option<Hash>>,
vertex: Vertex<ChangeId>,
) -> Result<(), LocalApplyError<T::Error>> {
debug!("repairing missing context for {:?}", vertex);
if n.flag.contains(EdgeFlags::FOLDER) {
return Ok(());
}
for down in n.down_context.iter() {
let down = find_block(txn, channel, internal_pos(txn, &down, change_id)?)?;
if iter_adjacent(
txn,
channel,
down,
EdgeFlags::PARENT,
EdgeFlags::all() - EdgeFlags::DELETED,
)
.any(|e| e.introduced_by != change_id)
{
continue;
}
debug!("repairing missing down context {:?} {:?}", down, vertex);
repair_missing_down_context(
txn,
channel,
&mut ws.missing_context,
n.inode,
down,
&[vertex],
)
.map_err(LocalApplyError::from_missing)?
}
Ok(())
}
fn repair_edge_context<T: MutTxnT>(
n: &EdgeMap<Option<Hash>>,
) -> Result<(), LocalApplyError<T::Error>> {
for e in n.edges.iter() {
assert!(!e.flag.contains(EdgeFlags::PARENT));
if e.flag.contains(EdgeFlags::DELETED) {
trace!("repairing context deleted {:?}", e);
repair_context_deleted(
txn,
channel,
&mut ws.missing_context,
n.inode,
change_id,
|h| change.knows(&h),
e,
)
.map_err(LocalApplyError::from_missing)?
} else {
trace!("repairing context nondeleted {:?}", e);
repair_context_nondeleted(
txn,
channel,
&mut ws.missing_context,
n.inode,
change_id,
|h| change.knows(&h),
e,
)
.map_err(LocalApplyError::from_missing)?
}
}
Ok(())
}
pub(crate) fn repair_cyclic_paths<T: MutTxnT>(
txn: &mut T,
channel: &mut Channel<T>,
ws: &mut Workspace,
for atom in change.changes.iter().flat_map(|r| r.iter()) {
if let Atom::EdgeMap(ref n) = atom {
for e in n.edges.iter() {
assert!(!e.flag.contains(EdgeFlags::PARENT));
if e.flag.contains(EdgeFlags::FOLDER | EdgeFlags::DELETED) && e.to.len() > 0 {
repair_edge(txn, channel, e, change_id, ws)?;
let mut files = std::mem::replace(&mut ws.missing_context.files, HashSet::new());
for file in files.drain() {
if file.is_empty() {
if !is_rooted(txn, channel, file, ws)? {
repair_edge(txn, channel, file, ws)?
}
} else {
let f0 = EdgeFlags::FOLDER;
let f1 = EdgeFlags::FOLDER | EdgeFlags::BLOCK | EdgeFlags::PSEUDO;
if let Some(ee) = iter_adjacent(txn, channel, file, f0, f1).next() {
let dest = ee.dest.inode_vertex();
if !is_rooted(txn, channel, dest, ws)? {
repair_edge(txn, channel, dest, ws)?
// If we're deleting a name and not a whole file.
let h = if let Some(h) = e.to.change {
h
} else {
return Ok(());
};
let to = Vertex {
change: if let Some(h) = txn.get_internal(h) {
h
debug!("repair_edge {:?}", to0);
let mut stack = vec![(to0, true, true, true)];
ws.parents.clear();
while let Some((current, _, al, anc_al)) = stack.pop() {
if !ws.parents.insert(current) {
continue;
}
debug!("repair_cyclic {:?}", current);
if current != to0 {
stack.push((current, true, al, anc_al));
}
if current.is_root() {
debug!("root");
break;
}
if let Some(&true) = ws.rooted.get(¤t) {
debug!("rooted");
break;
}
let f = EdgeFlags::PARENT | EdgeFlags::FOLDER;
let len = stack.len();
for parent in iter_adjacent(txn, channel, current, f, EdgeFlags::all()) {
if parent.flag.is_parent() {
let anc = find_block_end(txn, channel, parent.dest)?;
debug!("is_rooted, parent = {:?}", parent);
let al = iter_adjacent(
txn,
channel,
anc,
f,
f | EdgeFlags::BLOCK | EdgeFlags::PSEUDO,
)
.next()
.is_some();
debug!("al = {:?}, flag = {:?}", al, parent.flag);
stack.push((anc, false, parent.flag.is_deleted(), al));
}
}
if stack.len() == len {
stack.pop();
return Err((crate::pristine::InconsistentChange {}).into());
},
start: e.to.start,
end: e.to.end,
};
// Check that the inode descendant of this name is rooted.
let mut unrooted = false;
let f0 = EdgeFlags::FOLDER;
let f1 = EdgeFlags::FOLDER | EdgeFlags::BLOCK | EdgeFlags::PSEUDO;
for ee in iter_adjacent(txn, channel, to, f0, f1) {
if !is_rooted(txn, channel, ee.dest.inode_vertex(), ws)? {
unrooted = true;
(&mut stack[len..]).sort_unstable_by(|a, b| a.3.cmp(&b.3))
// If not, repair.
if unrooted {
let from = find_block_end(txn, channel, internal_pos(txn, &e.from, change_id)?)?;
debug!("repairing unrooted: {:?} {:?}", from, to);
put_graph_with_rev(
txn,
channel,
EdgeFlags::FOLDER | EdgeFlags::PSEUDO,
from,
to,
ChangeId::ROOT,
)
.map_err(LocalApplyError::Txn)?;
let mut current = to0;
for (next, on_path, del, _) in stack {
if on_path {
if del {
put_graph_with_rev(
txn,
channel,
EdgeFlags::FOLDER | EdgeFlags::PSEUDO,
next,
current,
ChangeId::ROOT,
)
.map_err(LocalApplyError::Txn)?;
}
current = next
}
let mut alive = false;
assert!(v.is_empty());
for e in iter_adjacent(txn, channel, v, EdgeFlags::empty(), EdgeFlags::all()) {
if e.flag.contains(EdgeFlags::PARENT) {
if e.flag & (EdgeFlags::FOLDER | EdgeFlags::DELETED) == EdgeFlags::FOLDER {
alive = true;
break;
}
} else if !e.flag.is_deleted() {
alive = true;
break;
}
}
if !alive {
debug!("is_rooted, not alive");
return Ok(true);
}