1 use crate::apply;
2 use crate::change::*;
3 use crate::changestore::*;
4 use crate::missing_context::*;
5 use crate::pristine::*;
6
7 use std::collections::{HashMap, HashSet};
8
9 mod working_copy;
10
11 #[derive(Debug, Error)]
12 pub enum UnrecordError<
13 ChangestoreError: std::error::Error + 'static,
14 TxnError: std::error::Error + 'static,
15 > {
16 #[error("Changestore error: {0}")]
17 Changestore(ChangestoreError),
18 #[error(transparent)]
19 Txn(TxnError),
20 #[error(transparent)]
21 Block(#[from] crate::pristine::BlockError<TxnError>),
22 #[error(transparent)]
23 InconsistentChange(#[from] crate::pristine::InconsistentChange<TxnError>),
24 #[error("Change not in channel: {}", hash.to_base32())]
25 ChangeNotInChannel { hash: ChangeId },
26 #[error("Change {} is depended upon by {}", change_id.to_base32(), dependent.to_base32())]
27 ChangeIsDependedUpon {
28 change_id: ChangeId,
29 dependent: ChangeId,
30 },
31 #[error(transparent)]
32 Missing(#[from] crate::missing_context::MissingError<TxnError>),
33 #[error(transparent)]
34 LocalApply(#[from] crate::apply::LocalApplyError<TxnError>),
35 #[error(transparent)]
36 Apply(#[from] crate::apply::ApplyError<ChangestoreError, TxnError>),
37 }
38
39 impl<T: std::error::Error + 'static, C: std::error::Error + 'static> std::convert::From<TxnErr<T>>
40 for UnrecordError<C, T>
41 {
from(t: TxnErr<T>) -> Self42 fn from(t: TxnErr<T>) -> Self {
43 UnrecordError::Txn(t.0)
44 }
45 }
46
unrecord<T: MutTxnT, P: ChangeStore>( txn: &mut T, channel: &ChannelRef<T>, changes: &P, hash: &Hash, salt: u64, ) -> Result<bool, UnrecordError<P::Error, T::GraphError>>47 pub fn unrecord<T: MutTxnT, P: ChangeStore>(
48 txn: &mut T,
49 channel: &ChannelRef<T>,
50 changes: &P,
51 hash: &Hash,
52 salt: u64,
53 ) -> Result<bool, UnrecordError<P::Error, T::GraphError>> {
54 let change = changes
55 .get_change(hash)
56 .map_err(UnrecordError::Changestore)?;
57 let change_id = if let Some(&h) = txn.get_internal(&hash.into())? {
58 h
59 } else {
60 return Ok(false);
61 };
62 let unused = unused_in_other_channels(txn, &channel, change_id)?;
63 let mut channel = channel.write();
64
65 del_channel_changes::<T, P>(txn, &mut channel, change_id)?;
66
67 unapply(txn, &mut channel, changes, change_id, &change, salt)?;
68
69 if unused {
70 assert!(txn.get_revdep(&change_id, None)?.is_none());
71 while txn.del_dep(&change_id, None)? {}
72 txn.del_external(&change_id, None)?;
73 txn.del_internal(&hash.into(), None)?;
74 for dep in change.dependencies.iter() {
75 let dep = *txn.get_internal(&dep.into())?.unwrap();
76 txn.del_revdep(&dep, Some(&change_id))?;
77 }
78 Ok(false)
79 } else {
80 Ok(true)
81 }
82 }
83
del_channel_changes< T: ChannelMutTxnT + DepsTxnT<DepsError = <T as GraphTxnT>::GraphError>, P: ChangeStore, >( txn: &mut T, channel: &mut T::Channel, change_id: ChangeId, ) -> Result<(), UnrecordError<P::Error, T::GraphError>>84 fn del_channel_changes<
85 T: ChannelMutTxnT + DepsTxnT<DepsError = <T as GraphTxnT>::GraphError>,
86 P: ChangeStore,
87 >(
88 txn: &mut T,
89 channel: &mut T::Channel,
90 change_id: ChangeId,
91 ) -> Result<(), UnrecordError<P::Error, T::GraphError>> {
92 let timestamp = if let Some(&ts) = txn.get_changeset(txn.changes(channel), &change_id)? {
93 ts
94 } else {
95 return Err(UnrecordError::ChangeNotInChannel { hash: change_id });
96 };
97
98 for x in txn.iter_revdep(&change_id)? {
99 let (p, d) = x?;
100 assert!(*p >= change_id);
101 if *p > change_id {
102 break;
103 }
104 if txn.get_changeset(txn.changes(channel), d)?.is_some() {
105 return Err(UnrecordError::ChangeIsDependedUpon {
106 change_id,
107 dependent: *d,
108 });
109 }
110 }
111
112 txn.del_changes(channel, change_id, timestamp.into())?;
113 Ok(())
114 }
115
unused_in_other_channels<T: TxnT>( txn: &mut T, channel: &ChannelRef<T>, change_id: ChangeId, ) -> Result<bool, TxnErr<T::GraphError>>116 fn unused_in_other_channels<T: TxnT>(
117 txn: &mut T,
118 channel: &ChannelRef<T>,
119 change_id: ChangeId,
120 ) -> Result<bool, TxnErr<T::GraphError>> {
121 let channel = channel.read();
122 for br in txn.iter_channels("")? {
123 let (name, br) = br?;
124 if name.as_str() == txn.name(&channel) {
125 continue;
126 }
127 let br = br.read();
128 if txn.get_changeset(txn.changes(&br), &change_id)?.is_some() {
129 return Ok(false);
130 }
131 }
132 Ok(true)
133 }
134
unapply< T: ChannelMutTxnT + TreeMutTxnT<TreeError = <T as GraphTxnT>::GraphError>, C: ChangeStore, >( txn: &mut T, channel: &mut T::Channel, changes: &C, change_id: ChangeId, change: &Change, salt: u64, ) -> Result<(), UnrecordError<C::Error, T::GraphError>>135 fn unapply<
136 T: ChannelMutTxnT + TreeMutTxnT<TreeError = <T as GraphTxnT>::GraphError>,
137 C: ChangeStore,
138 >(
139 txn: &mut T,
140 channel: &mut T::Channel,
141 changes: &C,
142 change_id: ChangeId,
143 change: &Change,
144 salt: u64,
145 ) -> Result<(), UnrecordError<C::Error, T::GraphError>> {
146 let mut clean_inodes = HashSet::new();
147 let mut ws = Workspace::default();
148 for change_ in change.changes.iter().rev().flat_map(|r| r.rev_iter()) {
149 match *change_ {
150 Atom::EdgeMap(ref newedges) => unapply_edges(
151 changes,
152 txn,
153 T::graph_mut(channel),
154 change_id,
155 newedges,
156 change,
157 &mut ws,
158 )?,
159 Atom::NewVertex(ref newvertex) => {
160 if clean_inodes.insert(newvertex.inode) {
161 crate::alive::remove_forward_edges(
162 txn,
163 T::graph_mut(channel),
164 internal_pos(txn, &newvertex.inode, change_id)?,
165 )?
166 }
167 unapply_newvertex::<T, C>(
168 txn,
169 T::graph_mut(channel),
170 change_id,
171 &mut ws,
172 newvertex,
173 )?;
174 }
175 }
176 }
177 repair_newvertex_contexts::<T, C>(txn, T::graph_mut(channel), &mut ws, change_id)?;
178 for change in change.changes.iter().rev().flat_map(|r| r.rev_iter()) {
179 if let Atom::EdgeMap(ref n) = *change {
180 remove_zombies::<_, C>(txn, T::graph_mut(channel), &mut ws, change_id, n)?;
181 repair_edges_context(
182 changes,
183 txn,
184 T::graph_mut(channel),
185 &mut ws.apply.missing_context,
186 change_id,
187 n,
188 )?
189 }
190 }
191
192 for change_ in change.changes.iter().rev().flat_map(|r| r.rev_iter()) {
193 match *change_ {
194 Atom::EdgeMap(ref newedges) if newedges.edges.is_empty() => {}
195 Atom::EdgeMap(ref newedges) if newedges.edges[0].flag.contains(EdgeFlags::FOLDER) => {
196 if newedges.edges[0].flag.contains(EdgeFlags::DELETED) {
197 working_copy::undo_file_deletion(
198 txn, changes, channel, change_id, newedges, salt,
199 )?
200 } else {
201 working_copy::undo_file_reinsertion::<C, _>(txn, change_id, newedges)?
202 }
203 }
204 Atom::NewVertex(ref new_vertex)
205 if new_vertex.flag.contains(EdgeFlags::FOLDER)
206 && new_vertex.down_context.is_empty() =>
207 {
208 working_copy::undo_file_addition(txn, change_id, new_vertex)?;
209 }
210 _ => {}
211 }
212 }
213
214 crate::apply::clean_obsolete_pseudo_edges(
215 txn,
216 T::graph_mut(channel),
217 &mut ws.apply,
218 change_id,
219 )?;
220 crate::apply::repair_cyclic_paths(txn, T::graph_mut(channel), &mut ws.apply)?;
221 txn.touch_channel(channel, Some(0));
222 Ok(())
223 }
224
225 #[derive(Default)]
226 struct Workspace {
227 up: HashMap<Vertex<ChangeId>, Position<Option<Hash>>>,
228 down: HashMap<Vertex<ChangeId>, Position<Option<Hash>>>,
229 parents: HashSet<Vertex<ChangeId>>,
230 del: Vec<SerializedEdge>,
231 apply: crate::apply::Workspace,
232 stack: Vec<Vertex<ChangeId>>,
233 del_edges: Vec<(Vertex<ChangeId>, SerializedEdge)>,
234 must_reintroduce: HashSet<(Vertex<ChangeId>, Vertex<ChangeId>)>,
235 }
236
unapply_newvertex<T: GraphMutTxnT, C: ChangeStore>( txn: &mut T, channel: &mut T::Graph, change_id: ChangeId, ws: &mut Workspace, new_vertex: &NewVertex<Option<Hash>>, ) -> Result<(), UnrecordError<C::Error, T::GraphError>>237 fn unapply_newvertex<T: GraphMutTxnT, C: ChangeStore>(
238 txn: &mut T,
239 channel: &mut T::Graph,
240 change_id: ChangeId,
241 ws: &mut Workspace,
242 new_vertex: &NewVertex<Option<Hash>>,
243 ) -> Result<(), UnrecordError<C::Error, T::GraphError>> {
244 let mut pos = Position {
245 change: change_id,
246 pos: new_vertex.start,
247 };
248 debug!("unapply_newvertex = {:?}", new_vertex);
249 while let Ok(&vertex) = txn.find_block(channel, pos) {
250 debug!("vertex = {:?}", vertex);
251 for e in iter_adj_all(txn, channel, vertex)? {
252 let e = e?;
253 debug!("e = {:?}", e);
254 if !e.flag().is_deleted() {
255 if e.flag().is_parent() {
256 if !e.flag().is_folder() {
257 let up_v = txn.find_block_end(channel, e.dest())?;
258 ws.up.insert(*up_v, new_vertex.inode);
259 }
260 } else {
261 let down_v = txn.find_block(channel, e.dest())?;
262 ws.down.insert(*down_v, new_vertex.inode);
263 if e.flag().is_folder() {
264 ws.apply.missing_context.files.insert(*down_v);
265 }
266 }
267 }
268 ws.del.push(*e)
269 }
270 debug!("del = {:#?}", ws.del);
271 ws.up.remove(&vertex);
272 ws.down.remove(&vertex);
273 ws.perform_del::<C, T>(txn, channel, vertex)?;
274 if vertex.end < new_vertex.end {
275 pos.pos = vertex.end
276 }
277 }
278 Ok(())
279 }
280
281 impl Workspace {
perform_del<C: ChangeStore, T: GraphMutTxnT>( &mut self, txn: &mut T, channel: &mut T::Graph, vertex: Vertex<ChangeId>, ) -> Result<(), UnrecordError<C::Error, T::GraphError>>282 fn perform_del<C: ChangeStore, T: GraphMutTxnT>(
283 &mut self,
284 txn: &mut T,
285 channel: &mut T::Graph,
286 vertex: Vertex<ChangeId>,
287 ) -> Result<(), UnrecordError<C::Error, T::GraphError>> {
288 for e in self.del.drain(..) {
289 let (a, b) = if e.flag().is_parent() {
290 (*txn.find_block_end(channel, e.dest())?, vertex)
291 } else {
292 (vertex, *txn.find_block(channel, e.dest())?)
293 };
294 del_graph_with_rev(
295 txn,
296 channel,
297 e.flag() - EdgeFlags::PARENT,
298 a,
299 b,
300 e.introduced_by(),
301 )?;
302 }
303 Ok(())
304 }
305 }
306
repair_newvertex_contexts<T: GraphMutTxnT, C: ChangeStore>( txn: &mut T, channel: &mut T::Graph, ws: &mut Workspace, change_id: ChangeId, ) -> Result<(), UnrecordError<C::Error, T::GraphError>>307 fn repair_newvertex_contexts<T: GraphMutTxnT, C: ChangeStore>(
308 txn: &mut T,
309 channel: &mut T::Graph,
310 ws: &mut Workspace,
311 change_id: ChangeId,
312 ) -> Result<(), UnrecordError<C::Error, T::GraphError>> {
313 debug!("up = {:#?}", ws.up);
314 for (up, inode) in ws.up.drain() {
315 if !is_alive(txn, channel, &up)? {
316 continue;
317 }
318 crate::missing_context::repair_missing_down_context(
319 txn,
320 channel,
321 &mut ws.apply.missing_context,
322 inode,
323 up,
324 &[up],
325 )?
326 }
327
328 debug!("down = {:#?}", ws.down);
329 for (down, inode) in ws.down.drain() {
330 if !is_alive(txn, channel, &down)? {
331 continue;
332 }
333 for parent in iter_adjacent(
334 txn,
335 channel,
336 down,
337 EdgeFlags::PARENT,
338 EdgeFlags::all() - EdgeFlags::DELETED,
339 )? {
340 let parent = parent?;
341 let parent = txn.find_block_end(channel, parent.dest())?;
342 if !is_alive(txn, channel, parent)? {
343 ws.parents.insert(*parent);
344 }
345 }
346 debug!("parents {:#?}", ws.parents);
347 for up in ws.parents.drain() {
348 crate::missing_context::repair_missing_up_context(
349 txn,
350 channel,
351 &mut ws.apply.missing_context,
352 change_id,
353 inode,
354 up,
355 &[down],
356 )?
357 }
358 }
359 Ok(())
360 }
361
unapply_edges<T: GraphMutTxnT, P: ChangeStore>( changes: &P, txn: &mut T, channel: &mut T::Graph, change_id: ChangeId, newedges: &EdgeMap<Option<Hash>>, change: &Change, ws: &mut Workspace, ) -> Result<(), UnrecordError<P::Error, T::GraphError>>362 fn unapply_edges<T: GraphMutTxnT, P: ChangeStore>(
363 changes: &P,
364 txn: &mut T,
365 channel: &mut T::Graph,
366 change_id: ChangeId,
367 newedges: &EdgeMap<Option<Hash>>,
368 change: &Change,
369 ws: &mut Workspace,
370 ) -> Result<(), UnrecordError<P::Error, T::GraphError>> {
371 debug!("newedges = {:#?}", newedges);
372 let ext: Hash = txn.get_external(&change_id)?.unwrap().into();
373 ws.must_reintroduce.clear();
374 for n in newedges.edges.iter() {
375 let mut source = crate::apply::edge::find_source_vertex(
376 txn,
377 channel,
378 &n.from,
379 change_id,
380 newedges.inode,
381 n.flag,
382 &mut ws.apply,
383 )?;
384 let mut target = crate::apply::edge::find_target_vertex(
385 txn,
386 channel,
387 &n.to,
388 change_id,
389 newedges.inode,
390 n.flag,
391 &mut ws.apply,
392 )?;
393 loop {
394 let intro_ext = n.introduced_by.unwrap_or(ext);
395 let intro = internal(txn, &n.introduced_by, change_id)?.unwrap();
396 if must_reintroduce(
397 txn, channel, changes, source, target, intro_ext, intro, change_id,
398 )? {
399 ws.must_reintroduce.insert((source, target));
400 }
401 if target.end >= n.to.end {
402 break;
403 }
404 source = target;
405 target = *txn
406 .find_block(channel, target.end_pos())
407 .map_err(UnrecordError::from)?;
408 assert_ne!(source, target);
409 }
410 }
411 let reintro = std::mem::take(&mut ws.must_reintroduce);
412 for edge in newedges.edges.iter() {
413 let intro = internal(txn, &edge.introduced_by, change_id)?.unwrap();
414 apply::put_newedge(
415 txn,
416 channel,
417 &mut ws.apply,
418 intro,
419 newedges.inode,
420 &edge.reverse(Some(ext)),
421 |a, b| reintro.contains(&(a, b)),
422 |h| change.knows(h),
423 )?;
424 }
425 ws.must_reintroduce = reintro;
426 ws.must_reintroduce.clear();
427 Ok(())
428 }
429
must_reintroduce<T: GraphTxnT, C: ChangeStore>( txn: &T, channel: &T::Graph, changes: &C, a: Vertex<ChangeId>, b: Vertex<ChangeId>, intro: Hash, intro_id: ChangeId, current_id: ChangeId, ) -> Result<bool, UnrecordError<C::Error, T::GraphError>>430 fn must_reintroduce<T: GraphTxnT, C: ChangeStore>(
431 txn: &T,
432 channel: &T::Graph,
433 changes: &C,
434 a: Vertex<ChangeId>,
435 b: Vertex<ChangeId>,
436 intro: Hash,
437 intro_id: ChangeId,
438 current_id: ChangeId,
439 ) -> Result<bool, UnrecordError<C::Error, T::GraphError>> {
440 debug!("a = {:?}, b = {:?}", a, b);
441 // does a patch introduced by an edge parallel to
442 // this one remove this edge from the graph?
443 let b_ext = Position {
444 change: txn.get_external(&b.change)?.map(From::from),
445 pos: b.start,
446 };
447 let mut stack = Vec::new();
448 for e in iter_adj_all(txn, channel, a)? {
449 let e = e?;
450 if e.flag().contains(EdgeFlags::PARENT)
451 || e.dest() != b.start_pos()
452 || e.introduced_by().is_root()
453 || e.introduced_by() == current_id
454 {
455 continue;
456 }
457 // Optimisation to avoid opening change files in the vast
458 // majority of cases: if there is an edge `e` parallel to a ->
459 // b introduced by the change that introduced a or b, don't
460 // reinsert a -> b: that edge was removed by `e`.
461 if a.change == intro_id || b.change == intro_id {
462 return Ok(false);
463 }
464 stack.push(e.introduced_by())
465 }
466 edge_is_in_channel(txn, changes, b_ext, intro, &mut stack)
467 }
468
edge_is_in_channel<T: GraphTxnT, C: ChangeStore>( txn: &T, changes: &C, pos: Position<Option<Hash>>, introduced_by: Hash, stack: &mut Vec<ChangeId>, ) -> Result<bool, UnrecordError<C::Error, T::GraphError>>469 fn edge_is_in_channel<T: GraphTxnT, C: ChangeStore>(
470 txn: &T,
471 changes: &C,
472 pos: Position<Option<Hash>>,
473 introduced_by: Hash,
474 stack: &mut Vec<ChangeId>,
475 ) -> Result<bool, UnrecordError<C::Error, T::GraphError>> {
476 let mut visited = HashSet::new();
477 while let Some(s) = stack.pop() {
478 if !visited.insert(s) {
479 continue;
480 }
481 debug!("stack: {:?}", s);
482 for next in changes
483 .change_deletes_position(|c| txn.get_external(&c).unwrap().map(From::from), s, pos)
484 .map_err(UnrecordError::Changestore)?
485 {
486 if next == introduced_by {
487 return Ok(false);
488 } else if let Some(i) = txn.get_internal(&next.into())? {
489 stack.push(*i)
490 }
491 }
492 }
493 Ok(true)
494 }
495
remove_zombies<T: GraphMutTxnT, C: ChangeStore>( txn: &mut T, channel: &mut T::Graph, ws: &mut Workspace, change_id: ChangeId, newedges: &EdgeMap<Option<Hash>>, ) -> Result<(), UnrecordError<C::Error, T::GraphError>>496 fn remove_zombies<T: GraphMutTxnT, C: ChangeStore>(
497 txn: &mut T,
498 channel: &mut T::Graph,
499 ws: &mut Workspace,
500 change_id: ChangeId,
501 newedges: &EdgeMap<Option<Hash>>,
502 ) -> Result<(), UnrecordError<C::Error, T::GraphError>> {
503 debug!("remove_zombies, change_id = {:?}", change_id);
504 for edge in newedges.edges.iter() {
505 let to = internal_pos(txn, &edge.to.start_pos(), change_id)?;
506 collect_zombies(txn, channel, change_id, to, ws)?;
507 debug!("remove_zombies = {:#?}", ws.del_edges);
508 for (v, mut e) in ws.del_edges.drain(..) {
509 if e.flag().contains(EdgeFlags::PARENT) {
510 let u = *txn.find_block_end(channel, e.dest())?;
511 e -= EdgeFlags::PARENT;
512 del_graph_with_rev(txn, channel, e.flag(), u, v, e.introduced_by())?;
513 } else {
514 let w = *txn.find_block(channel, e.dest())?;
515 del_graph_with_rev(txn, channel, e.flag(), v, w, e.introduced_by())?;
516 }
517 }
518 }
519 Ok(())
520 }
521
collect_zombies<T: GraphTxnT>( txn: &T, channel: &T::Graph, change_id: ChangeId, to: Position<ChangeId>, ws: &mut Workspace, ) -> Result<(), BlockError<T::GraphError>>522 fn collect_zombies<T: GraphTxnT>(
523 txn: &T,
524 channel: &T::Graph,
525 change_id: ChangeId,
526 to: Position<ChangeId>,
527 ws: &mut Workspace,
528 ) -> Result<(), BlockError<T::GraphError>> {
529 ws.stack.push(*txn.find_block(channel, to)?);
530 while let Some(v) = ws.stack.pop() {
531 debug!("remove_zombies, v = {:?}", v);
532 if !ws.parents.insert(v) {
533 continue;
534 }
535 for e in iter_adj_all(txn, channel, v)? {
536 let e = e?;
537 debug!("e = {:?}", e);
538 if !(e.introduced_by() == change_id || e.flag() & EdgeFlags::bp() == EdgeFlags::PARENT)
539 {
540 continue;
541 }
542 if e.flag().contains(EdgeFlags::PARENT) {
543 ws.stack.push(*txn.find_block_end(channel, e.dest())?)
544 } else {
545 ws.stack.push(*txn.find_block(channel, e.dest())?)
546 }
547 if e.introduced_by() == change_id {
548 ws.del_edges.push((v, *e))
549 }
550 }
551 }
552 ws.stack.clear();
553 ws.parents.clear();
554 Ok(())
555 }
556
repair_edges_context<T: GraphMutTxnT, P: ChangeStore>( changes: &P, txn: &mut T, channel: &mut T::Graph, ws: &mut crate::missing_context::Workspace, change_id: ChangeId, n: &EdgeMap<Option<Hash>>, ) -> Result<(), UnrecordError<P::Error, T::GraphError>>557 fn repair_edges_context<T: GraphMutTxnT, P: ChangeStore>(
558 changes: &P,
559 txn: &mut T,
560 channel: &mut T::Graph,
561 ws: &mut crate::missing_context::Workspace,
562 change_id: ChangeId,
563 n: &EdgeMap<Option<Hash>>,
564 ) -> Result<(), UnrecordError<P::Error, T::GraphError>> {
565 debug!("repair_edges_context");
566 let change_hash: Hash = txn.get_external(&change_id)?.unwrap().into();
567 for e in n.edges.iter() {
568 assert!(!e.flag.contains(EdgeFlags::PARENT));
569 let intro = internal(txn, &e.introduced_by, change_id)?.unwrap();
570 if e.previous.contains(EdgeFlags::DELETED) {
571 repair_context_deleted(
572 txn,
573 channel,
574 ws,
575 n.inode,
576 intro,
577 |h| changes.knows(&change_hash, &h).unwrap(),
578 &e.reverse(Some(change_hash)),
579 )?
580 } else {
581 let to = internal_pos(txn, &e.to.start_pos(), change_id)?;
582 let to = txn.find_block(channel, to)?;
583 debug!("to = {:?}", to);
584 if !is_alive(txn, channel, to)? {
585 debug!("not alive");
586 continue;
587 }
588 repair_context_nondeleted(
589 txn,
590 channel,
591 ws,
592 n.inode,
593 intro,
594 &e.reverse(Some(change_hash)),
595 )?
596 }
597 }
598 Ok(())
599 }
600