1use crate::apply;
2use crate::change::*;
3use crate::changestore::*;
4use crate::pristine::*;
5use crate::working_copy::WorkingCopy;
6use std::collections::{HashMap, HashSet};
7
8mod working_copy;
9
10#[derive(Error)]
11pub enum UnrecordError<ChangestoreError: std::error::Error + 'static, T: GraphTxnT + TreeTxnT> {
12 #[error("Changestore error: {0}")]
13 Changestore(ChangestoreError),
14 #[error(transparent)]
15 Txn(#[from] TxnErr<T::GraphError>),
16 #[error(transparent)]
17 Tree(#[from] TreeErr<T::TreeError>),
18 #[error(transparent)]
19 Block(#[from] crate::pristine::BlockError<T::GraphError>),
20 #[error(transparent)]
21 InconsistentChange(#[from] crate::pristine::InconsistentChange<T::GraphError>),
22 #[error("Change not in channel: {}", hash.to_base32())]
23 ChangeNotInChannel { hash: ChangeId },
24 #[error("Change {} is depended upon by {}", change_id.to_base32(), dependent.to_base32())]
25 ChangeIsDependedUpon {
26 change_id: ChangeId,
27 dependent: ChangeId,
28 },
29 #[error(transparent)]
30 Missing(#[from] crate::missing_context::MissingError<T::GraphError>),
31 #[error(transparent)]
32 LocalApply(#[from] crate::apply::LocalApplyError<T>),
33 #[error(transparent)]
34 Apply(#[from] crate::apply::ApplyError<ChangestoreError, T>),
35 #[error(transparent)]
36 SmallStr(#[from] crate::small_string::Error),
37}
38
39impl<C: std::error::Error, T: GraphTxnT + TreeTxnT> std::fmt::Debug for UnrecordError<C, T> {
40 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
41 match self {
42 UnrecordError::Changestore(e) => std::fmt::Debug::fmt(e, fmt),
43 UnrecordError::Txn(e) => std::fmt::Debug::fmt(e, fmt),
44 UnrecordError::Tree(e) => std::fmt::Debug::fmt(e, fmt),
45 UnrecordError::Block(e) => std::fmt::Debug::fmt(e, fmt),
46 UnrecordError::InconsistentChange(e) => std::fmt::Debug::fmt(e, fmt),
47 UnrecordError::ChangeNotInChannel { hash } => {
48 write!(fmt, "Change not in channel: {}", hash.to_base32())
49 }
50 UnrecordError::ChangeIsDependedUpon {
51 change_id,
52 dependent,
53 } => write!(
54 fmt,
55 "Change {} is depended upon: {}",
56 change_id.to_base32(),
57 dependent.to_base32()
58 ),
59 UnrecordError::Missing(e) => std::fmt::Debug::fmt(e, fmt),
60 UnrecordError::LocalApply(e) => std::fmt::Debug::fmt(e, fmt),
61 UnrecordError::Apply(e) => std::fmt::Debug::fmt(e, fmt),
62 UnrecordError::SmallStr(e) => std::fmt::Debug::fmt(e, fmt),
63 }
64 }
65}
66
67pub type TouchedInodes = HashSet<(ChangeId, Position<Option<Hash>>)>;
68
69pub fn unrecord<T: MutTxnT, P: ChangeStore>(
70 txn: &mut T,
71 channel: &ChannelRef<T>,
72 changes: &P,
73 hash: &Hash,
74 salt: u64,
75 touched: &mut TouchedInodes,
76) -> Result<bool, UnrecordError<P::Error, T>> {
77 let change_id = if let Some(&h) = txn.get_internal(&hash.into())? {
78 h
79 } else {
80 return Ok(false);
81 };
82 let unused = unused_in_other_channels(txn, channel, change_id)?;
83 let mut channel = channel.write();
84
85 del_channel_changes::<T, P>(txn, &mut channel, change_id)?;
86
87 let change = changes
88 .get_change(hash)
89 .map_err(UnrecordError::Changestore)?;
90
91 unapply(
92 txn,
93 &mut channel,
94 changes,
95 change_id,
96 &change,
97 salt,
98 touched,
99 )?;
100
101 if unused {
102 assert!(txn.get_revdep(&change_id, None)?.is_none());
103 while txn.del_dep(&change_id, None)? {}
104 txn.del_external(&change_id, None)?;
105 txn.del_internal(&hash.into(), None)?;
106 for dep in change.dependencies.iter() {
107 let dep = *txn.get_internal(&dep.into())?.unwrap();
108 txn.del_revdep(&dep, Some(&change_id))?;
109 }
110 Ok(false)
111 } else {
112 Ok(true)
113 }
114}
115
116fn del_channel_changes<
117 T: ChannelMutTxnT + DepsTxnT<DepsError = <T as GraphTxnT>::GraphError> + TreeTxnT,
118 P: ChangeStore,
119>(
120 txn: &mut T,
121 channel: &mut T::Channel,
122 change_id: ChangeId,
123) -> Result<(), UnrecordError<P::Error, T>> {
124 let timestamp = if let Some(&ts) = txn.get_changeset(txn.changes(channel), &change_id)? {
125 ts
126 } else {
127 return Err(UnrecordError::ChangeNotInChannel { hash: change_id });
128 };
129 debug!("del_channel_changes {:?}", change_id);
130 for x in txn.iter_revdep(&change_id)? {
131 debug!("revdep {:?}", x);
132 let (p, d) = x?;
133 assert!(*p >= change_id);
134 if *p > change_id {
135 break;
136 }
137 if txn.get_changeset(txn.changes(channel), d)?.is_some() {
138 return Err(UnrecordError::ChangeIsDependedUpon {
139 change_id,
140 dependent: *d,
141 });
142 }
143 }
144
145 txn.del_changes(channel, change_id, timestamp.into())?;
146
147 let tags = txn.tags_mut(channel);
148 txn.del_tags(tags, timestamp.into())?;
149
150 Ok(())
151}
152
153fn unused_in_other_channels<T: TxnT>(
154 txn: &mut T,
155 channel: &ChannelRef<T>,
156 change_id: ChangeId,
157) -> Result<bool, TxnErr<T::GraphError>> {
158 let channel = channel.read();
159 for br in txn.channels(&crate::small_string::SmallString::default())? {
160 let br = br.read();
161 if txn.name(&br) == txn.name(&channel) {
162 continue;
163 }
164 if txn.get_changeset(txn.changes(&br), &change_id)?.is_some() {
165 return Ok(false);
166 }
167 }
168 Ok(true)
169}
170
171fn unapply<
172 T: ChannelMutTxnT + TreeMutTxnT<TreeError = <T as GraphTxnT>::GraphError>,
173 C: ChangeStore,
174>(
175 txn: &mut T,
176 channel: &mut T::Channel,
177 changes: &C,
178 change_id: ChangeId,
179 change: &Change,
180 salt: u64,
181 touched_inodes: &mut TouchedInodes,
182) -> Result<(), UnrecordError<C::Error, T>> {
183 let mut clean_inodes = HashSet::new();
186 let mut ws = Workspace::default();
187 for change_ in change.changes.iter().rev().flat_map(|r| r.rev_iter()) {
188 info!("unrecording {:?}", change_);
189 match *change_ {
190 Atom::EdgeMap(ref newedges) => {
191 touched_inodes.insert((change_id, newedges.inode));
192 unapply_edges(
193 changes,
194 txn,
195 T::graph_mut(channel),
196 change_id,
197 newedges,
198 &mut ws,
199 )?
200 }
201 Atom::NewVertex(ref newvertex) => {
202 touched_inodes.insert((change_id, newvertex.inode));
203 if clean_inodes.insert(newvertex.inode) {
204 crate::alive::remove_forward_edges(
205 txn,
206 T::graph_mut(channel),
207 internal_pos(txn, &newvertex.inode, change_id)?,
208 )?
209 }
210 unapply_newvertex::<T, C>(
211 txn,
212 T::graph_mut(channel),
213 change_id,
214 &mut ws,
215 newvertex,
216 )?;
217 }
218 }
219 }
220
221 for change in change.changes.iter().rev().flat_map(|r| r.rev_iter()) {
222 match change {
223 Atom::EdgeMap(n) => {
224 remove_zombies_edges::<_, C>(txn, T::graph_mut(channel), &mut ws, change_id, n)?;
228 }
229 Atom::NewVertex(_) => {}
230 }
231 }
232
233 for change_ in change.changes.iter().rev().flat_map(|r| r.rev_iter()) {
234 match *change_ {
235 Atom::EdgeMap(ref newedges) if newedges.edges.is_empty() => {}
236 Atom::EdgeMap(ref newedges) if newedges.edges[0].flag.contains(EdgeFlags::FOLDER) => {
237 if newedges.edges[0].flag.contains(EdgeFlags::DELETED) {
238 working_copy::undo_file_deletion(
239 txn, changes, channel, change_id, newedges, salt,
240 )?
241 } else {
242 working_copy::undo_file_reinsertion::<C, _>(txn, change_id, newedges)?
243 }
244 }
245 Atom::NewVertex(ref new_vertex)
246 if new_vertex.flag.contains(EdgeFlags::FOLDER)
247 && new_vertex.down_context.is_empty() =>
248 {
249 working_copy::undo_file_addition(txn, change_id, new_vertex)?;
250 }
251 _ => {}
252 }
253 }
254
255 debug!("touched {:?}", touched_inodes);
258 for (change_id_, inode) in touched_inodes.iter() {
259 if *change_id_ != change_id {
260 continue;
261 }
262 if let Ok(inode) = internal_pos(txn, inode, change_id) {
265 let channel = T::graph_mut(channel);
266 collect_zombies_pseudo(txn, channel, inode, &mut ws)?;
267 for (v, mut e) in ws.del_edges.drain(..) {
268 if e.flag().contains(EdgeFlags::PARENT) {
269 if let Ok(u) = txn.find_block_end(channel, e.dest()) {
270 e -= EdgeFlags::PARENT;
271 debug!("line {}, del {:?} {:?} {:?}", line!(), u, v, e);
272 del_graph_with_rev(txn, channel, e.flag(), *u, v, e.introduced_by())?;
273 }
274 } else {
275 if let Ok(w) = txn.find_block(channel, e.dest()) {
276 debug!("line {}, del {:?} {:?} {:?}", line!(), v, w, e);
277 del_graph_with_rev(txn, channel, e.flag(), v, *w, e.introduced_by())?;
278 }
279 }
280 }
281 let mut log = crate::apply::ZombieRepairLog::default();
282 crate::apply::repair_zombies(txn, channel, inode, Some((&mut log, change_id)))?;
283 remove_leftover_markings(txn, channel, change_id, &log)?;
284 }
285 }
286
287 let mut inodes = crate::apply::clean_obsolete_pseudo_edges(
288 txn,
289 T::graph_mut(channel),
290 &mut ws.apply,
291 change_id,
292 )?;
293 debug!("inodes = {:?}", inodes);
294 collect_missing_contexts(
295 txn,
296 txn.graph(channel),
297 &mut ws.apply,
298 change,
299 change_id,
300 &mut inodes,
301 )?;
302 for i in inodes {
303 debug!("inodes: repair zombie {:?}", i);
304 let mut log = crate::apply::ZombieRepairLog::default();
305 crate::apply::repair_zombies(txn, T::graph_mut(channel), i, Some((&mut log, change_id)))?;
306 remove_leftover_markings(txn, T::graph_mut(channel), change_id, &log)?;
307 }
308 crate::apply::repair_cyclic_paths(txn, T::graph_mut(channel), &mut ws.apply)?;
309 debug!("unapply done");
310 Ok(())
311}
312
313#[derive(Error)]
314pub enum TouchError<WorkingCopyError: std::error::Error + 'static, T: GraphTxnT + TreeTxnT> {
315 #[error(transparent)]
316 Txn(#[from] TxnErr<T::GraphError>),
317 #[error(transparent)]
318 Tree(#[from] TreeErr<T::TreeError>),
319 #[error(transparent)]
320 WorkingCopy(WorkingCopyError),
321}
322
323impl<W: std::error::Error, T: GraphTxnT + TreeTxnT> std::fmt::Debug for TouchError<W, T> {
324 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
325 match self {
326 TouchError::Txn(e) => std::fmt::Debug::fmt(e, fmt),
327 TouchError::Tree(e) => std::fmt::Debug::fmt(e, fmt),
328 TouchError::WorkingCopy(e) => std::fmt::Debug::fmt(e, fmt),
329 }
330 }
331}
332
333pub fn touch_inodes<
334 T: ChannelMutTxnT + TreeMutTxnT<TreeError = <T as GraphTxnT>::GraphError>,
335 W: WorkingCopy,
336>(
337 txn: &mut T,
338 working_copy: &W,
339 touched_inodes: &TouchedInodes,
340) -> Result<(), TouchError<W::Error, T>> {
341 let now = std::time::SystemTime::now();
342 for (change_id, inode) in touched_inodes.iter() {
343 if let Ok(inode) = internal_pos(txn, inode, *change_id)
344 && let Some(inode) = txn.get_revinodes(&inode, None)?
345 && let Some(name) = crate::fs::inode_filename(txn, *inode)?
346 {
347 debug!(
348 "touching file {:?} {:?}",
349 name,
350 now.duration_since(std::time::SystemTime::UNIX_EPOCH)
351 .unwrap()
352 .as_millis()
353 );
354 working_copy.touch(&name, now).unwrap_or(())
355 }
356 }
357 Ok(())
358}
359
360#[derive(Default)]
361struct Workspace {
362 up: HashMap<Vertex<ChangeId>, Position<Option<Hash>>>,
363 down: HashMap<Vertex<ChangeId>, Position<Option<Hash>>>,
364 parents: HashSet<Vertex<ChangeId>>,
365 del: Vec<SerializedEdge>,
366 apply: crate::apply::Workspace,
367 stack: Vec<Vertex<ChangeId>>,
368 del_edges: Vec<(Vertex<ChangeId>, SerializedEdge)>,
369 must_reintroduce: HashSet<(Vertex<ChangeId>, Vertex<ChangeId>, ChangeId)>,
370 zombies_stack: Vec<(Vertex<ChangeId>, bool, bool)>,
371}
372
373fn unapply_newvertex<T: GraphMutTxnT + TreeTxnT, C: ChangeStore>(
374 txn: &mut T,
375 channel: &mut T::Graph,
376 change_id: ChangeId,
377 ws: &mut Workspace,
378 new_vertex: &NewVertex<Option<Hash>>,
379) -> Result<(), UnrecordError<C::Error, T>> {
380 let mut pos = Position {
381 change: change_id,
382 pos: new_vertex.start,
383 };
384 debug!("unapply_newvertex = {:?}", new_vertex);
385 while let Ok(&vertex) = txn.find_block(channel, pos) {
386 debug!("vertex = {:?}", vertex);
387 for e in iter_adj_all(txn, channel, vertex)? {
388 let e = e?;
389 debug!("e = {:?}", e);
390 if !e.flag().is_deleted() {
391 if e.flag().is_parent() {
392 if !e.flag().is_folder() {
393 let up_v = txn.find_block_end(channel, e.dest())?;
394 ws.up.insert(*up_v, new_vertex.inode);
395 }
396 } else {
397 let down_v = txn.find_block(channel, e.dest())?;
398 ws.down.insert(*down_v, new_vertex.inode);
399 if e.flag().is_folder() {
400 ws.apply.missing_context.files.insert(*down_v);
401 }
402 }
403 }
404 ws.del.push(*e)
405 }
406 debug!("del = {:#?}", ws.del);
407 ws.up.remove(&vertex);
408 ws.down.remove(&vertex);
409 ws.perform_del::<C, T>(txn, channel, vertex)?;
410 if vertex.end < new_vertex.end {
411 pos.pos = vertex.end
412 }
413 }
414 Ok(())
415}
416
417impl Workspace {
418 fn perform_del<C: ChangeStore, T: GraphMutTxnT + TreeTxnT>(
419 &mut self,
420 txn: &mut T,
421 channel: &mut T::Graph,
422 vertex: Vertex<ChangeId>,
423 ) -> Result<(), UnrecordError<C::Error, T>> {
424 for e in self.del.drain(..) {
425 let (a, b) = if e.flag().is_parent() {
426 (*txn.find_block_end(channel, e.dest())?, vertex)
427 } else {
428 (vertex, *txn.find_block(channel, e.dest())?)
429 };
430 debug!("line {}, del {:?} {:?} {:?}", line!(), a, b, e);
431 del_graph_with_rev(
432 txn,
433 channel,
434 e.flag() - EdgeFlags::PARENT,
435 a,
436 b,
437 e.introduced_by(),
438 )?;
439 }
440 Ok(())
441 }
442}
443
444fn unapply_edges<T: GraphMutTxnT + TreeTxnT, P: ChangeStore>(
445 changes: &P,
446 txn: &mut T,
447 channel: &mut T::Graph,
448 change_id: ChangeId,
449 newedges: &EdgeMap<Option<Hash>>,
450 ws: &mut Workspace,
451) -> Result<(), UnrecordError<P::Error, T>> {
452 debug!("newedges = {:#?}", newedges);
453 let ext: Hash = txn.get_external(&change_id).optional()?.unwrap().into();
454 ws.must_reintroduce.clear();
455 for n in newedges.edges.iter() {
456 let mut source = crate::apply::edge::find_source_vertex(
457 txn,
458 channel,
459 &n.from,
460 change_id,
461 newedges.inode,
462 n.flag,
463 &mut ws.apply,
464 )?;
465 let mut target = crate::apply::edge::find_target_vertex(
466 txn,
467 channel,
468 &n.to,
469 change_id,
470 newedges.inode,
471 n.flag,
472 &mut ws.apply,
473 )?;
474 loop {
475 let intro_ext = n.introduced_by.unwrap_or(ext);
476 let intro = internal(txn, &n.introduced_by, change_id)?.unwrap();
477 if must_reintroduce::<_, _>(
478 txn, channel, changes, source, target, intro_ext, intro, change_id,
479 )? {
480 ws.must_reintroduce.insert((source, target, intro));
481 }
482 if target.end >= n.to.end {
483 break;
484 }
485 source = target;
486 target = *txn
487 .find_block(channel, target.end_pos())
488 .map_err(UnrecordError::from)?;
489 assert_ne!(source, target);
490 }
491 }
492 let reintro = std::mem::take(&mut ws.must_reintroduce);
493 for edge in newedges.edges.iter() {
494 let intro = internal(txn, &edge.introduced_by, change_id)?.unwrap();
495 apply::put_newedge(
496 txn,
497 channel,
498 &mut ws.apply,
499 intro,
500 newedges.inode,
501 &edge.reverse(Some(ext)),
502 |a, b| reintro.contains(&(a, b, intro)),
503 |h| {
504 if h == &ext {
505 return true;
506 }
507 if edge.previous.contains(EdgeFlags::DELETED) {
508 changes
512 .knows(edge.introduced_by.as_ref().unwrap_or(&ext), h)
513 .unwrap()
514 } else {
515 true
521 }
522 },
523 )?;
524 }
525
526 ws.must_reintroduce = reintro;
527 ws.must_reintroduce.clear();
528 Ok(())
529}
530
531#[allow(clippy::too_many_arguments)]
532fn must_reintroduce<T: GraphTxnT + TreeTxnT, C: ChangeStore>(
533 txn: &T,
534 channel: &T::Graph,
535 changes: &C,
536 a: Vertex<ChangeId>,
537 b: Vertex<ChangeId>,
538 intro: Hash,
539 intro_id: ChangeId,
540 current_id: ChangeId,
541) -> Result<bool, UnrecordError<C::Error, T>> {
542 debug!("a = {:?}, b = {:?}", a, b);
543 let b_ext = Position {
549 change: txn.get_external(&b.change).optional()?.map(From::from),
550 pos: b.start,
551 };
552 let mut stack = Vec::new();
553 for e in iter_adj_all(txn, channel, a)? {
554 let e = e?;
555 if e.flag().contains(EdgeFlags::PARENT)
556 || e.dest() != b.start_pos()
557 || e.introduced_by().is_root()
558 || e.introduced_by() == current_id
559 {
560 continue;
561 }
562 if !e.flag().is_deleted() && (a.change == intro_id || b.change == intro_id) {
572 return Ok(false);
573 }
574 stack.push(e.introduced_by())
575 }
576 edge_is_in_channel::<_, _>(txn, changes, b_ext, intro, &mut stack)
577}
578
579fn edge_is_in_channel<T: GraphTxnT + TreeTxnT, C: ChangeStore>(
580 txn: &T,
581 changes: &C,
582 pos: Position<Option<Hash>>,
583 introduced_by: Hash,
584 stack: &mut Vec<ChangeId>,
585) -> Result<bool, UnrecordError<C::Error, T>> {
586 let mut visited = HashSet::new();
587 while let Some(s) = stack.pop() {
588 if !visited.insert(s) {
589 continue;
590 }
591 debug!("stack: {:?}", s);
592 for next in changes
593 .change_deletes_position(|c| txn.get_external(&c).ok().map(From::from), s, pos)
594 .map_err(UnrecordError::Changestore)?
595 {
596 if next == introduced_by {
597 return Ok(false);
598 } else if let Some(i) = txn.get_internal(&next.into())? {
599 stack.push(*i)
600 }
601 }
602 }
603 Ok(true)
604}
605
606fn remove_zombies_edges<T: GraphMutTxnT + TreeTxnT, C: ChangeStore>(
609 txn: &mut T,
610 channel: &mut T::Graph,
611 ws: &mut Workspace,
612 change_id: ChangeId,
613 newedges: &EdgeMap<Option<Hash>>,
614) -> Result<(), UnrecordError<C::Error, T>> {
615 debug!("remove_zombies_edges, change_id = {:?}", change_id);
616 for edge in newedges.edges.iter() {
617 let mut to = internal_pos(txn, &edge.to.start_pos(), change_id)?;
618 loop {
619 let to_block = *txn.find_block(channel, to)?;
620 collect_zombies(txn, channel, change_id, to_block, ws)?;
621 collect_zombies_context(txn, channel, change_id, to_block, ws)?;
622 debug!("remove_zombies_edges = {:#?}", ws.del_edges);
623 if to_block.end < edge.to.end {
624 to = to_block.end_pos()
625 } else {
626 break;
627 }
628 }
629 let from = internal_pos(txn, &edge.from, change_id)?;
630 let from_block = *txn.find_block_end(channel, from)?;
631 collect_zombies(txn, channel, change_id, from_block, ws)?;
632 collect_zombies_context(txn, channel, change_id, from_block, ws)?;
633
634 for (v, mut e) in ws.del_edges.drain(..) {
635 if e.flag().contains(EdgeFlags::PARENT) {
636 let u = *txn.find_block_end(channel, e.dest())?;
637 e -= EdgeFlags::PARENT;
638 debug!("line {}, del {:?} {:?} {:?}", line!(), u, v, e);
639 del_graph_with_rev(txn, channel, e.flag(), u, v, e.introduced_by())?;
640 } else {
641 let w = *txn.find_block(channel, e.dest())?;
642 debug!("line {}, del {:?} {:?} {:?}", line!(), v, w, e);
643 del_graph_with_rev(txn, channel, e.flag(), v, w, e.introduced_by())?;
644 }
645 }
646 }
647 Ok(())
648}
649
650fn collect_zombies<T: GraphTxnT>(
653 txn: &T,
654 channel: &T::Graph,
655 change_id: ChangeId,
656 to_block: Vertex<ChangeId>,
657 ws: &mut Workspace,
658) -> Result<(), BlockError<T::GraphError>> {
659 ws.stack.push(to_block);
660 while let Some(v) = ws.stack.pop() {
661 debug!("collect_zombies, v = {:?}", v);
662 if !ws.parents.insert(v) {
663 debug!("already seen");
664 continue;
665 }
666 for e in iter_adj_all(txn, channel, v)? {
667 let e = e?;
668 debug!("e = {:?}", e);
669
670 if e.introduced_by() != change_id {
671 continue;
672 }
673 if e.flag().contains(EdgeFlags::PARENT) {
674 ws.stack.push(*txn.find_block_end(channel, e.dest())?)
675 } else {
676 ws.stack.push(*txn.find_block(channel, e.dest())?)
677 }
678 if e.introduced_by() == change_id {
679 ws.del_edges.push((v, *e))
680 } else {
681 }
683 }
684 }
685 ws.stack.clear();
686 ws.parents.clear();
687 debug!("zombies collected");
688 Ok(())
689}
690
691fn collect_zombies_context<T: GraphTxnT>(
701 txn: &T,
702 channel: &T::Graph,
703 change_id: ChangeId,
704 v: Vertex<ChangeId>,
705 ws: &mut Workspace,
706) -> Result<(), BlockError<T::GraphError>> {
707 let mut stack = vec![v];
708 let mut visited = HashSet::new();
709 while let Some(w) = stack.pop() {
710 if !visited.insert(w) {
711 continue;
712 }
713 for e in iter_adj_all(txn, channel, w)? {
714 let e = e?;
715 if e.flag().contains(EdgeFlags::FOLDER) {
716 continue;
717 }
718 if w != v
726 && e.introduced_by() == change_id
727 && e.flag().is_deleted()
728 && e.flag().contains(EdgeFlags::BLOCK)
729 {
730 ws.del_edges.push((w, *e));
731 }
732 if e.flag().contains(EdgeFlags::PARENT) && !e.flag().contains(EdgeFlags::BLOCK) {
737 stack.push(*txn.find_block_end(channel, e.dest())?);
738 }
739 }
740 }
741 Ok(())
742}
743
744fn collect_zombies_pseudo<T: GraphTxnT>(
747 txn: &T,
748 channel: &T::Graph,
749 to: Position<ChangeId>,
750 ws: &mut Workspace,
751) -> Result<(), BlockError<T::GraphError>> {
752 if let Ok(to) = txn.find_block(channel, to) {
754 ws.zombies_stack.push((*to, false, false))
755 }
756
757 while let Some((v, alive, on_path)) = ws.zombies_stack.pop() {
758 debug!("collect_zombies_pseudo {:?} {:?} {:?}", v, alive, on_path);
759 if on_path {
760 if !alive {
762 for e in iter_adj_all(txn, channel, v)? {
763 let e = e?;
764 if e.flag().contains(EdgeFlags::PSEUDO) {
765 ws.del_edges.push((v, *e))
766 }
767 }
768 if ws.zombies_stack.is_empty() {
769 debug_assert_eq!(v.start_pos(), to);
770 ws.parents.clear();
772 collect_zombies_up(txn, channel, to, ws)?
773 }
774 }
775 continue;
776 }
777
778 assert!(!alive);
780
781 if !ws.parents.insert(v) {
783 continue;
784 }
785
786 ws.zombies_stack.push((v, false, true));
787
788 for e in iter_alive_children(txn, channel, v)? {
792 let e = e?;
793 if e.flag().intersects(EdgeFlags::PARENT | EdgeFlags::DELETED) {
794 continue;
795 }
796 let x = txn.find_block(channel, e.dest())?;
797 if is_alive(txn, channel, x)? {
798 for (_, alive, on_path) in ws.zombies_stack.iter_mut() {
800 if *on_path {
801 *alive = true
802 }
803 }
804 } else {
805 ws.zombies_stack.push((*x, false, false))
806 }
807 }
808 }
809 ws.zombies_stack.clear();
810 ws.parents.clear();
811 Ok(())
812}
813
814fn collect_zombies_up<T: GraphTxnT>(
815 txn: &T,
816 channel: &T::Graph,
817 to: Position<ChangeId>,
818 ws: &mut Workspace,
819) -> Result<(), BlockError<T::GraphError>> {
820 if let Ok(&to) = txn.find_block(channel, to) {
821 ws.stack.push(to);
822 }
823
824 while let Some(v) = ws.stack.pop() {
825 debug!("remove_zombies, v = {:?}", v);
826 if !ws.parents.insert(v) {
827 continue;
828 }
829 let del_len = ws.del_edges.len();
830 let stack_len = ws.stack.len();
831 for e in iter_adj_all(txn, channel, v)? {
832 let e = e?;
833 debug!("e = {:?}", e);
834 if e.flag().contains(EdgeFlags::PARENT) {
835 assert!(e.flag().contains(EdgeFlags::FOLDER));
836
837 if !e.flag().intersects(EdgeFlags::PSEUDO | EdgeFlags::DELETED) {
838 ws.del_edges.truncate(del_len);
840 ws.stack.truncate(stack_len);
841 break;
842 }
843 if let Ok(x) = txn.find_block_end(channel, e.dest()) {
844 ws.stack.push(*x)
845 }
846 if e.flag().contains(EdgeFlags::PSEUDO) {
847 ws.del_edges.push((v, *e))
848 }
849 }
850 }
851 }
852 ws.stack.clear();
853 ws.parents.clear();
854 Ok(())
855}
856
857fn remove_leftover_markings<T: GraphMutTxnT + TreeTxnT>(
858 txn: &mut T,
859 channel: &mut T::Graph,
860 change_id: ChangeId,
861 log: &crate::apply::ZombieRepairLog,
862) -> Result<(), TxnErr<T::GraphError>> {
863 for &(v, w) in log.leftover.iter() {
870 del_graph_with_rev(
871 txn,
872 channel,
873 EdgeFlags::DELETED | EdgeFlags::BLOCK,
874 v,
875 w,
876 change_id,
877 )?;
878 }
879 Ok(())
880}
881
882fn collect_missing_contexts<T: GraphMutTxnT + TreeTxnT>(
885 txn: &T,
886 channel: &T::Graph,
887 ws: &mut crate::apply::Workspace,
888 change: &Change,
889 change_id: ChangeId,
890 inodes: &mut HashSet<Position<ChangeId>>,
891) -> Result<(), crate::apply::LocalApplyError<T>> {
892 debug!("collect_missing_contexts");
893 inodes.extend(
894 ws.missing_context
895 .unknown_parents
896 .drain(..)
897 .map(|x| internal_pos(txn, &x.2, change_id).unwrap()),
898 );
899 for atom in change.changes.iter().flat_map(|r| r.iter()) {
900 match atom {
901 Atom::NewVertex(_) => {}
903 Atom::EdgeMap(n) => {
904 crate::apply::has_missing_edge_context(
905 txn, channel, change_id, change, n, inodes, true,
906 )?;
907 }
908 }
909 }
910 Ok(())
911}