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 crate::apply::repair_zombies(txn, channel, inode)?;
282 }
283 }
284
285 let mut inodes = crate::apply::clean_obsolete_pseudo_edges(
286 txn,
287 T::graph_mut(channel),
288 &mut ws.apply,
289 change_id,
290 )?;
291 debug!("inodes = {:?}", inodes);
292 collect_missing_contexts(
293 txn,
294 txn.graph(channel),
295 &mut ws.apply,
296 change,
297 change_id,
298 &mut inodes,
299 )?;
300 for i in inodes {
301 debug!("inodes: repair zombie {:?}", i);
302 crate::apply::repair_zombies(txn, T::graph_mut(channel), i)?;
303 }
304 crate::apply::repair_cyclic_paths(txn, T::graph_mut(channel), &mut ws.apply)?;
305 debug!("unapply done");
306 Ok(())
307}
308
309#[derive(Error)]
310pub enum TouchError<WorkingCopyError: std::error::Error + 'static, T: GraphTxnT + TreeTxnT> {
311 #[error(transparent)]
312 Txn(#[from] TxnErr<T::GraphError>),
313 #[error(transparent)]
314 Tree(#[from] TreeErr<T::TreeError>),
315 #[error(transparent)]
316 WorkingCopy(WorkingCopyError),
317}
318
319impl<W: std::error::Error, T: GraphTxnT + TreeTxnT> std::fmt::Debug for TouchError<W, T> {
320 fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
321 match self {
322 TouchError::Txn(e) => std::fmt::Debug::fmt(e, fmt),
323 TouchError::Tree(e) => std::fmt::Debug::fmt(e, fmt),
324 TouchError::WorkingCopy(e) => std::fmt::Debug::fmt(e, fmt),
325 }
326 }
327}
328
329pub fn touch_inodes<
330 T: ChannelMutTxnT + TreeMutTxnT<TreeError = <T as GraphTxnT>::GraphError>,
331 W: WorkingCopy,
332>(
333 txn: &mut T,
334 working_copy: &W,
335 touched_inodes: &TouchedInodes,
336) -> Result<(), TouchError<W::Error, T>> {
337 let now = std::time::SystemTime::now();
338 for (change_id, inode) in touched_inodes.iter() {
339 if let Ok(inode) = internal_pos(txn, inode, *change_id)
340 && let Some(inode) = txn.get_revinodes(&inode, None)?
341 && let Some(name) = crate::fs::inode_filename(txn, *inode)?
342 {
343 debug!(
344 "touching file {:?} {:?}",
345 name,
346 now.duration_since(std::time::SystemTime::UNIX_EPOCH)
347 .unwrap()
348 .as_millis()
349 );
350 working_copy.touch(&name, now).unwrap_or(())
351 }
352 }
353 Ok(())
354}
355
356#[derive(Default)]
357struct Workspace {
358 up: HashMap<Vertex<ChangeId>, Position<Option<Hash>>>,
359 down: HashMap<Vertex<ChangeId>, Position<Option<Hash>>>,
360 parents: HashSet<Vertex<ChangeId>>,
361 del: Vec<SerializedEdge>,
362 apply: crate::apply::Workspace,
363 stack: Vec<Vertex<ChangeId>>,
364 del_edges: Vec<(Vertex<ChangeId>, SerializedEdge)>,
365 must_reintroduce: HashSet<(Vertex<ChangeId>, Vertex<ChangeId>)>,
366 zombies_stack: Vec<(Vertex<ChangeId>, bool, bool)>,
367}
368
369fn unapply_newvertex<T: GraphMutTxnT + TreeTxnT, C: ChangeStore>(
370 txn: &mut T,
371 channel: &mut T::Graph,
372 change_id: ChangeId,
373 ws: &mut Workspace,
374 new_vertex: &NewVertex<Option<Hash>>,
375) -> Result<(), UnrecordError<C::Error, T>> {
376 let mut pos = Position {
377 change: change_id,
378 pos: new_vertex.start,
379 };
380 debug!("unapply_newvertex = {:?}", new_vertex);
381 while let Ok(&vertex) = txn.find_block(channel, pos) {
382 debug!("vertex = {:?}", vertex);
383 for e in iter_adj_all(txn, channel, vertex)? {
384 let e = e?;
385 debug!("e = {:?}", e);
386 if !e.flag().is_deleted() {
387 if e.flag().is_parent() {
388 if !e.flag().is_folder() {
389 let up_v = txn.find_block_end(channel, e.dest())?;
390 ws.up.insert(*up_v, new_vertex.inode);
391 }
392 } else {
393 let down_v = txn.find_block(channel, e.dest())?;
394 ws.down.insert(*down_v, new_vertex.inode);
395 if e.flag().is_folder() {
396 ws.apply.missing_context.files.insert(*down_v);
397 }
398 }
399 }
400 ws.del.push(*e)
401 }
402 debug!("del = {:#?}", ws.del);
403 ws.up.remove(&vertex);
404 ws.down.remove(&vertex);
405 ws.perform_del::<C, T>(txn, channel, vertex)?;
406 if vertex.end < new_vertex.end {
407 pos.pos = vertex.end
408 }
409 }
410 Ok(())
411}
412
413impl Workspace {
414 fn perform_del<C: ChangeStore, T: GraphMutTxnT + TreeTxnT>(
415 &mut self,
416 txn: &mut T,
417 channel: &mut T::Graph,
418 vertex: Vertex<ChangeId>,
419 ) -> Result<(), UnrecordError<C::Error, T>> {
420 for e in self.del.drain(..) {
421 let (a, b) = if e.flag().is_parent() {
422 (*txn.find_block_end(channel, e.dest())?, vertex)
423 } else {
424 (vertex, *txn.find_block(channel, e.dest())?)
425 };
426 debug!("line {}, del {:?} {:?} {:?}", line!(), a, b, e);
427 del_graph_with_rev(
428 txn,
429 channel,
430 e.flag() - EdgeFlags::PARENT,
431 a,
432 b,
433 e.introduced_by(),
434 )?;
435 }
436 Ok(())
437 }
438}
439
440fn unapply_edges<T: GraphMutTxnT + TreeTxnT, P: ChangeStore>(
441 changes: &P,
442 txn: &mut T,
443 channel: &mut T::Graph,
444 change_id: ChangeId,
445 newedges: &EdgeMap<Option<Hash>>,
446 ws: &mut Workspace,
447) -> Result<(), UnrecordError<P::Error, T>> {
448 debug!("newedges = {:#?}", newedges);
449 let ext: Hash = txn.get_external(&change_id).optional()?.unwrap().into();
450 ws.must_reintroduce.clear();
451 for n in newedges.edges.iter() {
452 let mut source = crate::apply::edge::find_source_vertex(
453 txn,
454 channel,
455 &n.from,
456 change_id,
457 newedges.inode,
458 n.flag,
459 &mut ws.apply,
460 )?;
461 let mut target = crate::apply::edge::find_target_vertex(
462 txn,
463 channel,
464 &n.to,
465 change_id,
466 newedges.inode,
467 n.flag,
468 &mut ws.apply,
469 )?;
470 loop {
471 let intro_ext = n.introduced_by.unwrap_or(ext);
472 let intro = internal(txn, &n.introduced_by, change_id)?.unwrap();
473 if must_reintroduce::<_, _>(
474 txn, channel, changes, source, target, intro_ext, intro, change_id,
475 )? {
476 ws.must_reintroduce.insert((source, target));
477 }
478 if target.end >= n.to.end {
479 break;
480 }
481 source = target;
482 target = *txn
483 .find_block(channel, target.end_pos())
484 .map_err(UnrecordError::from)?;
485 assert_ne!(source, target);
486 }
487 }
488 let reintro = std::mem::take(&mut ws.must_reintroduce);
489 for edge in newedges.edges.iter() {
490 let intro = internal(txn, &edge.introduced_by, change_id)?.unwrap();
491 apply::put_newedge(
492 txn,
493 channel,
494 &mut ws.apply,
495 intro,
496 newedges.inode,
497 &edge.reverse(Some(ext)),
498 |a, b| reintro.contains(&(a, b)),
499 |h| {
500 if h == &ext {
501 return true;
502 }
503 if edge.previous.contains(EdgeFlags::DELETED) {
504 changes
508 .knows(edge.introduced_by.as_ref().unwrap_or(&ext), h)
509 .unwrap()
510 } else {
511 true
517 }
518 },
519 )?;
520 }
521 ws.must_reintroduce = reintro;
522 ws.must_reintroduce.clear();
523 Ok(())
524}
525
526#[allow(clippy::too_many_arguments)]
527fn must_reintroduce<T: GraphTxnT + TreeTxnT, C: ChangeStore>(
528 txn: &T,
529 channel: &T::Graph,
530 changes: &C,
531 a: Vertex<ChangeId>,
532 b: Vertex<ChangeId>,
533 intro: Hash,
534 intro_id: ChangeId,
535 current_id: ChangeId,
536) -> Result<bool, UnrecordError<C::Error, T>> {
537 debug!("a = {:?}, b = {:?}", a, b);
538 let b_ext = Position {
544 change: txn.get_external(&b.change).optional()?.map(From::from),
545 pos: b.start,
546 };
547 let mut stack = Vec::new();
548 for e in iter_adj_all(txn, channel, a)? {
549 let e = e?;
550 if e.flag().contains(EdgeFlags::PARENT)
551 || e.dest() != b.start_pos()
552 || e.introduced_by().is_root()
553 || e.introduced_by() == current_id
554 {
555 continue;
556 }
557 if a.change == intro_id || b.change == intro_id {
563 return Ok(false);
564 }
565 stack.push(e.introduced_by())
566 }
567 edge_is_in_channel::<_, _>(txn, changes, b_ext, intro, &mut stack)
568}
569
570fn edge_is_in_channel<T: GraphTxnT + TreeTxnT, C: ChangeStore>(
571 txn: &T,
572 changes: &C,
573 pos: Position<Option<Hash>>,
574 introduced_by: Hash,
575 stack: &mut Vec<ChangeId>,
576) -> Result<bool, UnrecordError<C::Error, T>> {
577 let mut visited = HashSet::new();
578 while let Some(s) = stack.pop() {
579 if !visited.insert(s) {
580 continue;
581 }
582 debug!("stack: {:?}", s);
583 for next in changes
584 .change_deletes_position(|c| txn.get_external(&c).ok().map(From::from), s, pos)
585 .map_err(UnrecordError::Changestore)?
586 {
587 if next == introduced_by {
588 return Ok(false);
589 } else if let Some(i) = txn.get_internal(&next.into())? {
590 stack.push(*i)
591 }
592 }
593 }
594 Ok(true)
595}
596
597fn remove_zombies_edges<T: GraphMutTxnT + TreeTxnT, C: ChangeStore>(
600 txn: &mut T,
601 channel: &mut T::Graph,
602 ws: &mut Workspace,
603 change_id: ChangeId,
604 newedges: &EdgeMap<Option<Hash>>,
605) -> Result<(), UnrecordError<C::Error, T>> {
606 debug!("remove_zombies_edges, change_id = {:?}", change_id);
607 for edge in newedges.edges.iter() {
608 let mut to = internal_pos(txn, &edge.to.start_pos(), change_id)?;
609 loop {
610 let to_block = *txn.find_block(channel, to)?;
611 collect_zombies(txn, channel, change_id, to_block, ws)?;
612 debug!("remove_zombies_edges = {:#?}", ws.del_edges);
613 for (v, mut e) in ws.del_edges.drain(..) {
614 if e.flag().contains(EdgeFlags::PARENT) {
615 let u = *txn.find_block_end(channel, e.dest())?;
616 e -= EdgeFlags::PARENT;
617 debug!("line {}, del {:?} {:?} {:?}", line!(), u, v, e);
618 del_graph_with_rev(txn, channel, e.flag(), u, v, e.introduced_by())?;
619 } else {
620 let w = *txn.find_block(channel, e.dest())?;
621 debug!("line {}, del {:?} {:?} {:?}", line!(), v, w, e);
622 del_graph_with_rev(txn, channel, e.flag(), v, w, e.introduced_by())?;
623 }
624 }
625 if to_block.end < edge.to.end {
626 to = to_block.end_pos()
627 } else {
628 break;
629 }
630 }
631 let from = internal_pos(txn, &edge.from, change_id)?;
632 let from_block = *txn.find_block_end(channel, from)?;
633 collect_zombies(txn, channel, change_id, from_block, ws)?;
634 }
635 Ok(())
636}
637
638fn collect_zombies<T: GraphTxnT>(
641 txn: &T,
642 channel: &T::Graph,
643 change_id: ChangeId,
644 to_block: Vertex<ChangeId>,
645 ws: &mut Workspace,
646) -> Result<(), BlockError<T::GraphError>> {
647 ws.stack.push(to_block);
648 while let Some(v) = ws.stack.pop() {
649 debug!("collect_zombies, v = {:?}", v);
650 if !ws.parents.insert(v) {
651 debug!("already seen");
652 continue;
653 }
654 for e in iter_adj_all(txn, channel, v)? {
655 let e = e?;
656 debug!("e = {:?}", e);
657
658 if e.introduced_by() != change_id {
659 continue;
660 }
661 if e.flag().contains(EdgeFlags::PARENT) {
662 ws.stack.push(*txn.find_block_end(channel, e.dest())?)
663 } else {
664 ws.stack.push(*txn.find_block(channel, e.dest())?)
665 }
666 if e.introduced_by() == change_id {
667 ws.del_edges.push((v, *e))
668 }
669 }
670 }
671 ws.stack.clear();
672 ws.parents.clear();
673 debug!("zombies collected");
674 Ok(())
675}
676
677fn collect_zombies_pseudo<T: GraphTxnT>(
680 txn: &T,
681 channel: &T::Graph,
682 to: Position<ChangeId>,
683 ws: &mut Workspace,
684) -> Result<(), BlockError<T::GraphError>> {
685 if let Ok(to) = txn.find_block(channel, to) {
687 ws.zombies_stack.push((*to, false, false))
688 }
689
690 while let Some((v, alive, on_path)) = ws.zombies_stack.pop() {
691 debug!("collect_zombies_pseudo {:?} {:?} {:?}", v, alive, on_path);
692 if on_path {
693 if !alive {
695 for e in iter_adj_all(txn, channel, v)? {
696 let e = e?;
697 if e.flag().contains(EdgeFlags::PSEUDO) {
698 ws.del_edges.push((v, *e))
699 }
700 }
701 if ws.zombies_stack.is_empty() {
702 debug_assert_eq!(v.start_pos(), to);
703 ws.parents.clear();
705 collect_zombies_up(txn, channel, to, ws)?
706 }
707 }
708 continue;
709 }
710
711 assert!(!alive);
713
714 if !ws.parents.insert(v) {
716 continue;
717 }
718
719 ws.zombies_stack.push((v, false, true));
720
721 for e in iter_alive_children(txn, channel, v)? {
725 let e = e?;
726 if e.flag().intersects(EdgeFlags::PARENT | EdgeFlags::DELETED) {
727 continue;
728 }
729 let x = txn.find_block(channel, e.dest())?;
730 if is_alive(txn, channel, x)? {
731 for (_, alive, on_path) in ws.zombies_stack.iter_mut() {
733 if *on_path {
734 *alive = true
735 }
736 }
737 } else {
738 ws.zombies_stack.push((*x, false, false))
739 }
740 }
741 }
742 ws.zombies_stack.clear();
743 ws.parents.clear();
744 Ok(())
745}
746
747fn collect_zombies_up<T: GraphTxnT>(
748 txn: &T,
749 channel: &T::Graph,
750 to: Position<ChangeId>,
751 ws: &mut Workspace,
752) -> Result<(), BlockError<T::GraphError>> {
753 if let Ok(&to) = txn.find_block(channel, to) {
754 ws.stack.push(to);
755 }
756
757 while let Some(v) = ws.stack.pop() {
758 debug!("remove_zombies, v = {:?}", v);
759 if !ws.parents.insert(v) {
760 continue;
761 }
762 let del_len = ws.del_edges.len();
763 let stack_len = ws.stack.len();
764 for e in iter_adj_all(txn, channel, v)? {
765 let e = e?;
766 debug!("e = {:?}", e);
767 if e.flag().contains(EdgeFlags::PARENT) {
768 assert!(e.flag().contains(EdgeFlags::FOLDER));
769
770 if !e.flag().intersects(EdgeFlags::PSEUDO | EdgeFlags::DELETED) {
771 ws.del_edges.truncate(del_len);
773 ws.stack.truncate(stack_len);
774 break;
775 }
776 if let Ok(x) = txn.find_block_end(channel, e.dest()) {
777 ws.stack.push(*x)
778 }
779 if e.flag().contains(EdgeFlags::PSEUDO) {
780 ws.del_edges.push((v, *e))
781 }
782 }
783 }
784 }
785 ws.stack.clear();
786 ws.parents.clear();
787 Ok(())
788}
789
790fn collect_missing_contexts<T: GraphMutTxnT + TreeTxnT>(
793 txn: &T,
794 channel: &T::Graph,
795 ws: &mut crate::apply::Workspace,
796 change: &Change,
797 change_id: ChangeId,
798 inodes: &mut HashSet<Position<ChangeId>>,
799) -> Result<(), crate::apply::LocalApplyError<T>> {
800 debug!("collect_missing_contexts");
801 inodes.extend(
802 ws.missing_context
803 .unknown_parents
804 .drain(..)
805 .map(|x| internal_pos(txn, &x.2, change_id).unwrap()),
806 );
807 for atom in change.changes.iter().flat_map(|r| r.iter()) {
808 match atom {
809 Atom::NewVertex(_) => {}
811 Atom::EdgeMap(n) => {
812 crate::apply::has_missing_edge_context(
813 txn, channel, change_id, change, n, inodes, true,
814 )?;
815 }
816 }
817 }
818 Ok(())
819}