1 //! Hunk a change from a pristine and a working copy.
2 use crate::changestore::ChangeStore;
3 use crate::diff;
4 pub use crate::diff::Algorithm;
5 use crate::path::{components, Components};
6 use crate::pristine::*;
7 use crate::small_string::SmallString;
8 use crate::working_copy::WorkingCopyRead;
9 use crate::{alive::retrieve, text_encoding::Encoding};
10 use crate::{change::*, changestore::FileMetadata};
11 use crate::{HashMap, HashSet};
12 use parking_lot::Mutex;
13 use std::collections::VecDeque;
14 use std::sync::Arc;
15
16 #[derive(Debug, Error)]
17 pub enum RecordError<
18 C: std::error::Error + 'static,
19 W: std::error::Error,
20 T: std::error::Error + 'static,
21 > {
22 #[error("Changestore error: {0}")]
23 Changestore(C),
24 #[error("Working copy error: {0}")]
25 WorkingCopy(W),
26 #[error("System time error: {0}")]
27 SystemTimeError(#[from] std::time::SystemTimeError),
28 #[error(transparent)]
29 Txn(T),
30 #[error(transparent)]
31 Diff(#[from] diff::DiffError<C, T>),
32 #[error("Path not in repository: {0}")]
33 PathNotInRepo(String),
34 #[error(transparent)]
35 Io(#[from] std::io::Error),
36 }
37
38 impl<
39 C: std::error::Error + 'static,
40 W: std::error::Error + 'static,
41 T: std::error::Error + 'static,
42 > std::convert::From<TxnErr<T>> for RecordError<C, W, T>
43 {
from(e: TxnErr<T>) -> Self44 fn from(e: TxnErr<T>) -> Self {
45 RecordError::Txn(e.0)
46 }
47 }
48
49 impl<
50 C: std::error::Error + 'static,
51 W: std::error::Error + 'static,
52 T: std::error::Error + 'static,
53 > std::convert::From<crate::output::FileError<C, T>> for RecordError<C, W, T>
54 {
from(e: crate::output::FileError<C, T>) -> Self55 fn from(e: crate::output::FileError<C, T>) -> Self {
56 match e {
57 crate::output::FileError::Changestore(e) => RecordError::Changestore(e),
58 crate::output::FileError::Io(e) => RecordError::Io(e),
59 crate::output::FileError::Txn(t) => RecordError::Txn(t),
60 }
61 }
62 }
63
64 /// A change in the process of being recorded. This is typically
65 /// created using `Builder::new`.
66 pub struct Builder {
67 pub(crate) rec: Vec<Arc<Mutex<Recorded>>>,
68 recorded_inodes: Arc<Mutex<HashMap<Inode, Position<Option<ChangeId>>>>>,
69 deleted_vertices: Arc<Mutex<HashSet<Position<ChangeId>>>>,
70 pub force_rediff: bool,
71 pub ignore_missing: bool,
72 pub contents: Arc<Mutex<Vec<u8>>>,
73 }
74
75 #[derive(Debug)]
76 struct Parent {
77 basename: String,
78 metadata: InodeMetadata,
79 encoding: Option<Encoding>,
80 parent: Position<Option<ChangeId>>,
81 }
82
83 /// The result of recording a change:
84 pub struct Recorded {
85 /// The "byte contents" of the change.
86 pub contents: Arc<Mutex<Vec<u8>>>,
87 /// The current records, to be lated converted into change operations.
88 pub actions: Vec<Hunk<Option<ChangeId>, LocalByte>>,
89 /// The updates that need to be made to the ~tree~ and ~revtree~
90 /// tables when this change is applied to the local repository.
91 pub updatables: HashMap<usize, InodeUpdate>,
92 /// The size of the largest file that was recorded in this change.
93 pub largest_file: u64,
94 /// Whether we have recorded binary files.
95 pub has_binary_files: bool,
96 /// Timestamp of the oldest changed file. If nothing changed,
97 /// returns now().
98 pub oldest_change: std::time::SystemTime,
99 /// Redundant edges found during the comparison.
100 pub redundant: Vec<(Vertex<ChangeId>, SerializedEdge)>,
101 /// Force a re-diff
102 force_rediff: bool,
103 deleted_vertices: Arc<Mutex<HashSet<Position<ChangeId>>>>,
104 recorded_inodes: Arc<Mutex<HashMap<Inode, Position<Option<ChangeId>>>>>,
105 }
106
107 impl Default for Builder {
default() -> Self108 fn default() -> Self {
109 Self {
110 rec: Vec::new(),
111 recorded_inodes: Arc::new(Mutex::new(HashMap::default())),
112 force_rediff: false,
113 ignore_missing: false,
114 deleted_vertices: Arc::new(Mutex::new(HashSet::default())),
115 contents: Arc::new(Mutex::new(Vec::new())),
116 }
117 }
118 }
119
120 impl Builder {
121 /// Initialise a `Builder`.
new() -> Self122 pub fn new() -> Self {
123 Self::default()
124 }
125
recorded(&mut self) -> Arc<Mutex<Recorded>>126 pub fn recorded(&mut self) -> Arc<Mutex<Recorded>> {
127 let m = Arc::new(Mutex::new(self.recorded_()));
128 self.rec.push(m.clone());
129 m
130 }
131
recorded_(&self) -> Recorded132 fn recorded_(&self) -> Recorded {
133 Recorded {
134 contents: self.contents.clone(),
135 actions: Vec::new(),
136 updatables: HashMap::default(),
137 largest_file: 0,
138 has_binary_files: false,
139 oldest_change: std::time::SystemTime::UNIX_EPOCH,
140 redundant: Vec::new(),
141 force_rediff: self.force_rediff,
142 deleted_vertices: self.deleted_vertices.clone(),
143 recorded_inodes: self.recorded_inodes.clone(),
144 }
145 }
146
147 /// Finish the recording.
finish(mut self) -> Recorded148 pub fn finish(mut self) -> Recorded {
149 if self.rec.is_empty() {
150 self.recorded();
151 }
152 let mut it = self.rec.into_iter();
153 let mut result = if let Ok(rec) = Arc::try_unwrap(it.next().unwrap()) {
154 rec.into_inner()
155 } else {
156 unreachable!()
157 };
158 for rec in it {
159 let rec = if let Ok(rec) = Arc::try_unwrap(rec) {
160 rec.into_inner()
161 } else {
162 unreachable!()
163 };
164 let off = result.actions.len();
165 result.actions.extend(rec.actions.into_iter());
166 for (a, b) in rec.updatables {
167 result.updatables.insert(a + off, b);
168 }
169 result.largest_file = result.largest_file.max(rec.largest_file);
170 result.has_binary_files |= rec.has_binary_files;
171 if result.oldest_change == std::time::UNIX_EPOCH
172 || (rec.oldest_change > std::time::UNIX_EPOCH
173 && rec.oldest_change < result.oldest_change)
174 {
175 result.oldest_change = rec.oldest_change
176 }
177 result.redundant.extend(rec.redundant.into_iter())
178 }
179 debug!(
180 "result = {:?}, updatables = {:?}",
181 result.actions, result.updatables
182 );
183 result
184 }
185 }
186
187 /// An account of the files that have been added, moved or deleted, as
188 /// returned by record, and used by apply (when applying a change
189 /// created locally) to update the trees and inodes databases.
190 #[derive(Debug, Hash, PartialEq, Eq)]
191 pub enum InodeUpdate {
192 Add {
193 /// Inode vertex in the graph.
194 pos: ChangePosition,
195 /// `Inode` added by this file addition.
196 inode: Inode,
197 },
198 Deleted {
199 /// `Inode` of the deleted file.
200 inode: Inode,
201 },
202 }
203
204 #[derive(Debug, Clone)]
205 struct RecordItem {
206 v_papa: Position<Option<ChangeId>>,
207 papa: Inode,
208 inode: Inode,
209 basename: String,
210 full_path: String,
211 metadata: InodeMetadata,
212 }
213
214 impl RecordItem {
root() -> Self215 fn root() -> Self {
216 RecordItem {
217 inode: Inode::ROOT,
218 papa: Inode::ROOT,
219 v_papa: Position::OPTION_ROOT,
220 basename: String::new(),
221 full_path: String::new(),
222 metadata: InodeMetadata::new(0, true),
223 }
224 }
225 }
226
227 /// Ignore inodes that are in another channel
get_inodes_<T: ChannelTxnT + TreeTxnT<TreeError = <T as GraphTxnT>::GraphError>>( txn: &ArcTxn<T>, channel: &ChannelRef<T>, inode: &Inode, ) -> Result<Option<Position<ChangeId>>, TxnErr<T::GraphError>>228 fn get_inodes_<T: ChannelTxnT + TreeTxnT<TreeError = <T as GraphTxnT>::GraphError>>(
229 txn: &ArcTxn<T>,
230 channel: &ChannelRef<T>,
231 inode: &Inode,
232 ) -> Result<Option<Position<ChangeId>>, TxnErr<T::GraphError>> {
233 let txn = txn.read();
234 let channel = channel.r.read();
235 Ok(get_inodes(&*txn, &*channel, inode)?.map(|x| *x))
236 }
237
get_inodes<'a, T: ChannelTxnT + TreeTxnT<TreeError = <T as GraphTxnT>::GraphError>>( txn: &'a T, channel: &T::Channel, inode: &Inode, ) -> Result<Option<&'a Position<ChangeId>>, TxnErr<T::GraphError>>238 fn get_inodes<'a, T: ChannelTxnT + TreeTxnT<TreeError = <T as GraphTxnT>::GraphError>>(
239 txn: &'a T,
240 channel: &T::Channel,
241 inode: &Inode,
242 ) -> Result<Option<&'a Position<ChangeId>>, TxnErr<T::GraphError>> {
243 if let Some(vertex) = txn.get_inodes(inode, None)? {
244 if let Some(e) = iter_adjacent(
245 txn,
246 txn.graph(channel),
247 vertex.inode_vertex(),
248 EdgeFlags::PARENT,
249 EdgeFlags::all(),
250 )?
251 .next()
252 {
253 if e?.flag().is_alive_parent() {
254 return Ok(Some(vertex));
255 }
256 }
257 Ok(None)
258 } else {
259 Ok(None)
260 }
261 }
262
263 struct Tasks {
264 stop: bool,
265 t: VecDeque<(
266 RecordItem,
267 Position<ChangeId>,
268 Arc<Mutex<Recorded>>,
269 Option<Position<Option<ChangeId>>>,
270 )>,
271 }
272
273 impl Builder {
record< T, W: WorkingCopyRead + Clone + Send + Sync + 'static, C: ChangeStore + Clone + Send + 'static, >( &mut self, txn: ArcTxn<T>, diff_algorithm: diff::Algorithm, diff_separator: ®ex::bytes::Regex, channel: ChannelRef<T>, working_copy: &W, changes: &C, prefix: &str, _n_workers: usize, ) -> Result<(), RecordError<C::Error, W::Error, T::GraphError>> where T: ChannelMutTxnT + TreeTxnT<TreeError = <T as GraphTxnT>::GraphError> + Send + Sync + 'static, T::Channel: Send + Sync, <W as WorkingCopyRead>::Error: 'static,274 pub fn record<
275 T,
276 W: WorkingCopyRead + Clone + Send + Sync + 'static,
277 C: ChangeStore + Clone + Send + 'static,
278 >(
279 &mut self,
280 txn: ArcTxn<T>,
281 diff_algorithm: diff::Algorithm,
282 diff_separator: ®ex::bytes::Regex,
283 channel: ChannelRef<T>,
284 working_copy: &W,
285 changes: &C,
286 prefix: &str,
287 _n_workers: usize,
288 ) -> Result<(), RecordError<C::Error, W::Error, T::GraphError>>
289 where
290 T: ChannelMutTxnT
291 + TreeTxnT<TreeError = <T as GraphTxnT>::GraphError>
292 + Send
293 + Sync
294 + 'static,
295 T::Channel: Send + Sync,
296 <W as WorkingCopyRead>::Error: 'static,
297 {
298 let work = Arc::new(Mutex::new(Tasks {
299 t: VecDeque::new(),
300 stop: false,
301 }));
302 let mut workers: Vec<std::thread::JoinHandle<_>> = Vec::new();
303 for t in 0..0 {
304 // n_workers - 1 {
305 let working_copy = working_copy.clone();
306 let changes = changes.clone();
307 let channel = channel.clone();
308 let work = work.clone();
309 let txn = txn.clone();
310 let sep: regex::bytes::Regex = diff_separator.clone();
311 workers.push(std::thread::spawn(move || {
312 loop {
313 let (w, stop) = {
314 let mut work = work.lock();
315 (work.t.pop_front(), work.stop)
316 };
317 if let Some((item, vertex, rec, new_papa)) = w {
318 // This parent has changed.
319 info!("record existing file {:?} on thread {:?}", item, t);
320 rec.lock().record_existing_file(
321 &txn,
322 diff_algorithm,
323 &sep,
324 &channel,
325 working_copy.clone(),
326 &changes,
327 &item,
328 new_papa,
329 vertex,
330 )?;
331 } else if stop {
332 info!("stop {:?}", t);
333 break;
334 } else {
335 info!("yield {:?}", t);
336 std::thread::park_timeout(std::time::Duration::from_secs(1));
337 }
338 }
339 Ok::<_, RecordError<C::Error, W::Error, T::GraphError>>(())
340 }))
341 }
342
343 let now = std::time::Instant::now();
344 let mut stack = vec![(RecordItem::root(), components(prefix))];
345 while let Some((mut item, mut components)) = stack.pop() {
346 debug!("stack.pop() = Some({:?})", item);
347
348 // Check for moves and file conflicts.
349 let vertex: Option<Position<Option<ChangeId>>> =
350 self.recorded_inodes.lock().get(&item.inode).cloned();
351 let vertex = if let Some(vertex) = vertex {
352 vertex
353 } else if item.inode == Inode::ROOT {
354 self.recorded_inodes
355 .lock()
356 .insert(Inode::ROOT, Position::OPTION_ROOT);
357 debug!("TAKING LOCK {}", line!());
358 let txn = txn.read();
359 debug!("TAKEN");
360 let channel = channel.r.read();
361 debug!("TAKEN 2");
362 self.delete_obsolete_children(
363 &*txn,
364 txn.graph(&channel),
365 working_copy,
366 changes,
367 &item.full_path,
368 Position::ROOT,
369 )?;
370 Position::OPTION_ROOT
371 } else if let Some(vertex) = get_inodes_(&txn, &channel, &item.inode)? {
372 {
373 let mut txn = txn.write();
374 let mut channel = channel.r.write();
375 let mut graph = txn.graph(&mut *channel);
376 self.delete_obsolete_children(
377 &mut *txn,
378 &mut graph,
379 working_copy,
380 changes,
381 &item.full_path,
382 vertex,
383 )?;
384 }
385
386 let rec = self.recorded();
387 let new_papa = {
388 let mut recorded = self.recorded_inodes.lock();
389 recorded.insert(item.inode, vertex.to_option());
390 recorded.get(&item.papa).cloned()
391 };
392 let mut work = work.lock();
393 work.t.push_back((item.clone(), vertex, rec, new_papa));
394 std::mem::drop(work);
395 for t in workers.iter() {
396 t.thread().unpark()
397 }
398
399 vertex.to_option()
400 } else {
401 let rec = self.recorded();
402 debug!("TAKING LOCK {}", line!());
403 let mut rec = rec.lock();
404 match rec.add_file(working_copy, item.clone()) {
405 Ok(Some(vertex)) => {
406 // Path addition (maybe just a single directory).
407 self.recorded_inodes.lock().insert(item.inode, vertex);
408 vertex
409 }
410 _ => continue,
411 }
412 };
413
414 // Move on to the next step.
415 debug!("TAKING LOCK {}", line!());
416 let txn = txn.read();
417 let channel = channel.r.read();
418 self.push_children::<_, _, C>(
419 &*txn,
420 &*channel,
421 working_copy,
422 &mut item,
423 &mut components,
424 vertex,
425 &mut stack,
426 prefix,
427 changes,
428 )?;
429 }
430
431 info!("stop work");
432 work.lock().stop = true;
433 for t in workers.iter() {
434 t.thread().unpark()
435 }
436 loop {
437 let w = {
438 let mut work = work.lock();
439 debug!("waiting, stop = {:?}", work.stop);
440 work.t.pop_front()
441 };
442 if let Some((item, vertex, rec, new_papa)) = w {
443 // This parent has changed.
444 info!("record existing file {:?}", item);
445 rec.lock().record_existing_file(
446 &txn,
447 diff_algorithm,
448 diff_separator,
449 &channel,
450 working_copy.clone(),
451 changes,
452 &item,
453 new_papa,
454 vertex,
455 )?;
456 } else {
457 break;
458 }
459 }
460 for (n, t) in workers.into_iter().enumerate() {
461 debug!("WAITING {:?}", n);
462 match t.join() {
463 Ok(x) => x?,
464 Err(e) => {
465 warn!("Thread error {:?}", e);
466 }
467 }
468 }
469 crate::TIMERS.lock().unwrap().record += now.elapsed();
470 info!("record done");
471 Ok(())
472 }
473
delete_obsolete_children< T: GraphTxnT + TreeTxnT<TreeError = <T as GraphTxnT>::GraphError>, W: WorkingCopyRead, C: ChangeStore, >( &mut self, txn: &T, channel: &T::Graph, working_copy: &W, changes: &C, full_path: &str, v: Position<ChangeId>, ) -> Result<(), RecordError<C::Error, W::Error, T::GraphError>> where <W as WorkingCopyRead>::Error: 'static,474 fn delete_obsolete_children<
475 T: GraphTxnT + TreeTxnT<TreeError = <T as GraphTxnT>::GraphError>,
476 W: WorkingCopyRead,
477 C: ChangeStore,
478 >(
479 &mut self,
480 txn: &T,
481 channel: &T::Graph,
482 working_copy: &W,
483 changes: &C,
484 full_path: &str,
485 v: Position<ChangeId>,
486 ) -> Result<(), RecordError<C::Error, W::Error, T::GraphError>>
487 where
488 <W as WorkingCopyRead>::Error: 'static,
489 {
490 if self.ignore_missing {
491 return Ok(());
492 }
493 let f0 = EdgeFlags::FOLDER | EdgeFlags::BLOCK;
494 let f1 = f0 | EdgeFlags::PSEUDO;
495 debug!("delete_obsolete_children, v = {:?}", v);
496 for child in iter_adjacent(txn, channel, v.inode_vertex(), f0, f1)? {
497 let child = child?;
498 let child = txn.find_block(channel, child.dest()).unwrap();
499 for grandchild in iter_adjacent(txn, channel, *child, f0, f1)? {
500 let grandchild = grandchild?;
501 debug!("grandchild {:?}", grandchild);
502 let needs_deletion =
503 if let Some(inode) = txn.get_revinodes(&grandchild.dest(), None)? {
504 debug!("inode = {:?} {:?}", inode, txn.get_revtree(inode, None));
505 if let Some(path) = crate::fs::inode_filename(txn, *inode)? {
506 working_copy.file_metadata(&path).is_err()
507 } else {
508 true
509 }
510 } else {
511 true
512 };
513 if needs_deletion {
514 let mut name = Vec::new();
515 changes
516 .get_contents(
517 |p| txn.get_external(&p).unwrap().map(From::from),
518 *child,
519 &mut name,
520 )
521 .map_err(RecordError::Changestore)?;
522 let mut full_path = full_path.to_string();
523 let meta = FileMetadata::read(&name);
524 if !full_path.is_empty() {
525 full_path.push('/');
526 }
527 full_path.push_str(meta.basename);
528 // delete recursively.
529 let rec = self.recorded();
530 let mut rec = rec.lock();
531 rec.record_deleted_file(
532 txn,
533 &channel,
534 working_copy,
535 &full_path,
536 grandchild.dest(),
537 changes,
538 )?
539 }
540 }
541 }
542 Ok(())
543 }
544
push_children< 'a, T: ChannelTxnT + TreeTxnT<TreeError = <T as GraphTxnT>::GraphError>, W: WorkingCopyRead, C: ChangeStore, >( &mut self, txn: &T, channel: &T::Channel, working_copy: &W, item: &mut RecordItem, components: &mut Components<'a>, vertex: Position<Option<ChangeId>>, stack: &mut Vec<(RecordItem, Components<'a>)>, prefix: &str, changes: &C, ) -> Result<(), RecordError<C::Error, W::Error, T::GraphError>> where <W as crate::working_copy::WorkingCopyRead>::Error: 'static,545 fn push_children<
546 'a,
547 T: ChannelTxnT + TreeTxnT<TreeError = <T as GraphTxnT>::GraphError>,
548 W: WorkingCopyRead,
549 C: ChangeStore,
550 >(
551 &mut self,
552 txn: &T,
553 channel: &T::Channel,
554 working_copy: &W,
555 item: &mut RecordItem,
556 components: &mut Components<'a>,
557 vertex: Position<Option<ChangeId>>,
558 stack: &mut Vec<(RecordItem, Components<'a>)>,
559 prefix: &str,
560 changes: &C,
561 ) -> Result<(), RecordError<C::Error, W::Error, T::GraphError>>
562 where
563 <W as crate::working_copy::WorkingCopyRead>::Error: 'static,
564 {
565 debug!("push_children, item = {:?}", item);
566 let comp = components.next();
567 let full_path = item.full_path.clone();
568 let fileid = OwnedPathId {
569 parent_inode: item.inode,
570 basename: SmallString::new(),
571 };
572 let mut has_matching_children = false;
573 for x in txn.iter_tree(&fileid, None)? {
574 let (fileid_, child_inode) = x?;
575 debug!("push_children {:?} {:?}", fileid_, child_inode);
576 if fileid_.parent_inode < fileid.parent_inode || fileid_.basename.is_empty() {
577 continue;
578 } else if fileid_.parent_inode > fileid.parent_inode {
579 break;
580 }
581 if let Some(comp) = comp {
582 if comp != fileid_.basename.as_str() {
583 continue;
584 }
585 }
586 has_matching_children = true;
587 let basename = fileid_.basename.as_str().to_string();
588 let full_path = if full_path.is_empty() {
589 basename.clone()
590 } else {
591 full_path.clone() + "/" + &basename
592 };
593 debug!("fileid_ {:?} child_inode {:?}", fileid_, child_inode);
594 if let Ok(meta) = working_copy.file_metadata(&full_path) {
595 stack.push((
596 RecordItem {
597 papa: item.inode,
598 inode: *child_inode,
599 v_papa: vertex,
600 basename,
601 full_path,
602 metadata: meta,
603 },
604 components.clone(),
605 ));
606 } else if let Some(vertex) = get_inodes(txn, &channel, child_inode)? {
607 let rec = self.recorded();
608 let mut rec = rec.lock();
609 rec.record_deleted_file(
610 txn,
611 txn.graph(channel),
612 working_copy,
613 &full_path,
614 *vertex,
615 changes,
616 )?
617 }
618 }
619 if comp.is_some() && !has_matching_children {
620 debug!("comp = {:?}", comp);
621 return Err(RecordError::PathNotInRepo(prefix.to_string()));
622 }
623 Ok(())
624 }
625 }
626
modified_since_last_commit<T: ChannelTxnT, W: WorkingCopyRead>( txn: &T, channel: &T::Channel, working_copy: &W, prefix: &str, ) -> Result<bool, std::time::SystemTimeError>627 fn modified_since_last_commit<T: ChannelTxnT, W: WorkingCopyRead>(
628 txn: &T,
629 channel: &T::Channel,
630 working_copy: &W,
631 prefix: &str,
632 ) -> Result<bool, std::time::SystemTimeError> {
633 if let Ok(last_modified) = working_copy.modified_time(prefix) {
634 debug!(
635 "last_modified = {:?}, channel.last = {:?}",
636 last_modified
637 .duration_since(std::time::UNIX_EPOCH)?
638 .as_secs(),
639 txn.last_modified(channel)
640 );
641 Ok(last_modified
642 .duration_since(std::time::UNIX_EPOCH)?
643 .as_secs()
644 >= txn.last_modified(channel))
645 } else {
646 Ok(true)
647 }
648 }
649
650 impl Recorded {
add_file<W: WorkingCopyRead>( &mut self, working_copy: &W, item: RecordItem, ) -> Result<Option<Position<Option<ChangeId>>>, W::Error>651 fn add_file<W: WorkingCopyRead>(
652 &mut self,
653 working_copy: &W,
654 item: RecordItem,
655 ) -> Result<Option<Position<Option<ChangeId>>>, W::Error> {
656 debug!("record_file_addition {:?}", item);
657 let meta = working_copy.file_metadata(&item.full_path)?;
658 let mut contents = self.contents.lock();
659 contents.push(0);
660 let inode_pos = ChangePosition(contents.len().into());
661 contents.push(0);
662 let (contents_, encoding) = if meta.is_file() {
663 let start = ChangePosition(contents.len().into());
664 let encoding = working_copy.decode_file(&item.full_path, &mut contents)?;
665 self.has_binary_files |= encoding.is_none();
666 let end = ChangePosition(contents.len().into());
667 self.largest_file = self.largest_file.max(end.0.as_u64() - start.0.as_u64());
668 contents.push(0);
669 if end > start {
670 (
671 Some(Atom::NewVertex(NewVertex {
672 up_context: vec![Position {
673 change: None,
674 pos: inode_pos,
675 }],
676 down_context: vec![],
677 start,
678 end,
679 flag: EdgeFlags::BLOCK,
680 inode: Position {
681 change: None,
682 pos: inode_pos,
683 },
684 })),
685 encoding,
686 )
687 } else {
688 (None, encoding)
689 }
690 } else {
691 (None, None)
692 };
693
694 let name_start = ChangePosition(contents.len().into());
695 let file_meta = FileMetadata {
696 metadata: meta,
697 basename: item.basename.as_str(),
698 encoding: encoding.clone(),
699 };
700 file_meta.write(&mut contents);
701 let name_end = ChangePosition(contents.len().into());
702 contents.push(0);
703 self.actions.push(Hunk::FileAdd {
704 add_name: Atom::NewVertex(NewVertex {
705 up_context: vec![item.v_papa],
706 down_context: vec![],
707 start: name_start,
708 end: name_end,
709 flag: EdgeFlags::FOLDER | EdgeFlags::BLOCK,
710 inode: item.v_papa,
711 }),
712 add_inode: Atom::NewVertex(NewVertex {
713 up_context: vec![Position {
714 change: None,
715 pos: name_end,
716 }],
717 down_context: vec![],
718 start: inode_pos,
719 end: inode_pos,
720 flag: EdgeFlags::FOLDER | EdgeFlags::BLOCK,
721 inode: item.v_papa,
722 }),
723 contents: contents_,
724 path: item.full_path.clone(),
725 encoding,
726 });
727 debug!("{:?}", self.actions.last().unwrap());
728 self.updatables.insert(
729 self.actions.len(),
730 InodeUpdate::Add {
731 inode: item.inode,
732 pos: inode_pos,
733 },
734 );
735 if meta.is_dir() {
736 Ok(Some(Position {
737 change: None,
738 pos: inode_pos,
739 }))
740 } else {
741 Ok(None)
742 }
743 }
744
record_existing_file< T: ChannelTxnT + TreeTxnT<TreeError = <T as GraphTxnT>::GraphError>, W: WorkingCopyRead + Clone, C: ChangeStore, >( &mut self, txn: &ArcTxn<T>, diff_algorithm: diff::Algorithm, diff_sep: ®ex::bytes::Regex, channel: &ChannelRef<T>, working_copy: W, changes: &C, item: &RecordItem, new_papa: Option<Position<Option<ChangeId>>>, vertex: Position<ChangeId>, ) -> Result<(), RecordError<C::Error, W::Error, T::GraphError>> where <W as crate::working_copy::WorkingCopyRead>::Error: 'static,745 fn record_existing_file<
746 T: ChannelTxnT + TreeTxnT<TreeError = <T as GraphTxnT>::GraphError>,
747 W: WorkingCopyRead + Clone,
748 C: ChangeStore,
749 >(
750 &mut self,
751 txn: &ArcTxn<T>,
752 diff_algorithm: diff::Algorithm,
753 diff_sep: ®ex::bytes::Regex,
754 channel: &ChannelRef<T>,
755 working_copy: W,
756 changes: &C,
757 item: &RecordItem,
758 new_papa: Option<Position<Option<ChangeId>>>,
759 vertex: Position<ChangeId>,
760 ) -> Result<(), RecordError<C::Error, W::Error, T::GraphError>>
761 where
762 <W as crate::working_copy::WorkingCopyRead>::Error: 'static,
763 {
764 debug!(
765 "record_existing_file {:?}: {:?} {:?}",
766 item.full_path, item.inode, vertex
767 );
768 // Former parent(s) of vertex
769 let mut former_parents = Vec::new();
770 let f0 = EdgeFlags::FOLDER | EdgeFlags::PARENT;
771 let f1 = EdgeFlags::all();
772 let mut is_deleted = true;
773 let txn_ = txn.read();
774 let channel_ = channel.read();
775 for name_ in iter_adjacent(
776 &*txn_,
777 txn_.graph(&*channel_),
778 vertex.inode_vertex(),
779 f0,
780 f1,
781 )? {
782 debug!("name_ = {:?}", name_);
783 let name_ = name_?;
784 if !name_.flag().contains(EdgeFlags::PARENT) {
785 debug!("continue");
786 continue;
787 }
788 if name_.flag().contains(EdgeFlags::DELETED) {
789 debug!("is_deleted {:?}: {:?}", item.full_path, name_);
790 is_deleted = true;
791 break;
792 }
793 let name_dest = txn_
794 .find_block_end(txn_.graph(&*channel_), name_.dest())
795 .unwrap();
796 let mut meta = Vec::new();
797 let FileMetadata {
798 basename,
799 metadata,
800 encoding,
801 } = changes
802 .get_file_meta(
803 |p| txn_.get_external(&p).unwrap().map(From::from),
804 *name_dest,
805 &mut meta,
806 )
807 .map_err(RecordError::Changestore)?;
808 debug!(
809 "former basename of {:?}: {:?} {:?}",
810 vertex, basename, metadata
811 );
812 if let Some(v_papa) =
813 iter_adjacent(&*txn_, txn_.graph(&*channel_), *name_dest, f0, f1)?.next()
814 {
815 let v_papa = v_papa?;
816 if !v_papa.flag().contains(EdgeFlags::DELETED) {
817 former_parents.push(Parent {
818 basename: basename.to_string(),
819 metadata,
820 encoding,
821 parent: v_papa.dest().to_option(),
822 })
823 }
824 }
825 }
826 debug!(
827 "record_existing_file: {:?} {:?} {:?}",
828 item, former_parents, is_deleted,
829 );
830 assert!(!former_parents.is_empty());
831 if let Ok(new_meta) = working_copy.file_metadata(&item.full_path) {
832 debug!("new_meta = {:?}", new_meta);
833 if former_parents.len() > 1
834 || former_parents[0].basename != item.basename
835 || former_parents[0].metadata != item.metadata
836 || former_parents[0].parent != item.v_papa
837 || is_deleted
838 {
839 debug!("new_papa = {:?}", new_papa);
840 self.record_moved_file::<_, _, W>(
841 changes,
842 &*txn_,
843 &*channel_,
844 &item,
845 vertex,
846 new_papa.unwrap(),
847 former_parents[0].encoding.clone(),
848 )?
849 }
850 if new_meta.is_file()
851 && (self.force_rediff
852 || modified_since_last_commit(
853 &*txn_,
854 &*channel_,
855 &working_copy,
856 &item.full_path,
857 )?)
858 {
859 let mut ret = retrieve(&*txn_, txn_.graph(&*channel_), vertex)?;
860 let mut b = Vec::new();
861 let encoding = working_copy
862 .decode_file(&item.full_path, &mut b)
863 .map_err(RecordError::WorkingCopy)?;
864 debug!("diffing…");
865 let len = self.actions.len();
866 self.diff(
867 changes,
868 &*txn_,
869 &*channel_,
870 diff_algorithm,
871 item.full_path.clone(),
872 item.inode,
873 vertex.to_option(),
874 &mut ret,
875 &b,
876 &encoding,
877 diff_sep,
878 )?;
879 if self.actions.len() > len {
880 if let Ok(last_modified) = working_copy.modified_time(&item.full_path) {
881 if self.oldest_change == std::time::SystemTime::UNIX_EPOCH {
882 self.oldest_change = last_modified;
883 } else {
884 self.oldest_change = self.oldest_change.min(last_modified);
885 }
886 }
887 }
888 debug!(
889 "new actions: {:?}, total {:?}",
890 &self.actions.len() - len,
891 self.actions.len()
892 );
893 }
894 } else {
895 debug!("calling record_deleted_file on {:?}", item.full_path);
896 self.record_deleted_file(
897 &*txn_,
898 txn_.graph(&*channel_),
899 &working_copy,
900 &item.full_path,
901 vertex,
902 changes,
903 )?
904 }
905 Ok(())
906 }
907
record_moved_file<T: ChannelTxnT, C: ChangeStore, W: WorkingCopyRead>( &mut self, changes: &C, txn: &T, channel: &T::Channel, item: &RecordItem, vertex: Position<ChangeId>, new_papa: Position<Option<ChangeId>>, encoding: Option<Encoding>, ) -> Result<(), RecordError<C::Error, W::Error, T::GraphError>> where <W as crate::working_copy::WorkingCopyRead>::Error: 'static,908 fn record_moved_file<T: ChannelTxnT, C: ChangeStore, W: WorkingCopyRead>(
909 &mut self,
910 changes: &C,
911 txn: &T,
912 channel: &T::Channel,
913 item: &RecordItem,
914 vertex: Position<ChangeId>,
915 new_papa: Position<Option<ChangeId>>,
916 encoding: Option<Encoding>,
917 ) -> Result<(), RecordError<C::Error, W::Error, T::GraphError>>
918 where
919 <W as crate::working_copy::WorkingCopyRead>::Error: 'static,
920 {
921 debug!("record_moved_file {:?}", item);
922 let mut contents = self.contents.lock();
923 let basename = item.basename.as_str();
924 let meta_start = ChangePosition(contents.len().into());
925 FileMetadata {
926 metadata: item.metadata,
927 basename,
928 encoding: encoding.clone(),
929 }
930 .write(&mut contents);
931 let meta_end = ChangePosition(contents.len().into());
932 let mut moved = collect_moved_edges::<_, _, W>(
933 txn,
934 changes,
935 txn.graph(channel),
936 new_papa,
937 vertex,
938 item.metadata,
939 basename,
940 )?;
941 debug!("moved = {:#?}", moved);
942 if !moved.resurrect.is_empty() {
943 moved.resurrect.extend(moved.alive.into_iter());
944 if !moved.need_new_name {
945 moved.resurrect.extend(moved.edges.drain(..));
946 }
947 self.actions.push(Hunk::FileUndel {
948 undel: Atom::EdgeMap(EdgeMap {
949 edges: moved.resurrect,
950 inode: item.v_papa,
951 }),
952 contents: None,
953 path: item.full_path.clone(),
954 encoding,
955 });
956 }
957 if !moved.edges.is_empty() {
958 if moved.need_new_name {
959 self.actions.push(Hunk::FileMove {
960 del: Atom::EdgeMap(EdgeMap {
961 edges: moved.edges,
962 inode: item.v_papa,
963 }),
964 add: Atom::NewVertex(NewVertex {
965 up_context: vec![item.v_papa],
966 down_context: vec![vertex.to_option()],
967 start: meta_start,
968 end: meta_end,
969 flag: EdgeFlags::FOLDER | EdgeFlags::BLOCK,
970 inode: item.v_papa,
971 }),
972 path: crate::fs::find_path(changes, txn, channel, true, vertex)?
973 .unwrap()
974 .0,
975 });
976 } else {
977 self.actions.push(Hunk::SolveNameConflict {
978 name: Atom::EdgeMap(EdgeMap {
979 edges: moved.edges,
980 inode: item.v_papa,
981 }),
982 path: item.full_path.clone(),
983 });
984 contents.truncate(meta_start.0.as_usize())
985 }
986 } else {
987 contents.truncate(meta_start.0.as_usize())
988 }
989 Ok(())
990 }
991
take_updatables(&mut self) -> HashMap<usize, InodeUpdate>992 pub fn take_updatables(&mut self) -> HashMap<usize, InodeUpdate> {
993 std::mem::replace(&mut self.updatables, HashMap::default())
994 }
995
996
into_change<T: ChannelTxnT + DepsTxnT<DepsError = <T as GraphTxnT>::GraphError>>( self, txn: &T, channel: &ChannelRef<T>, header: crate::change::ChangeHeader, ) -> Result< crate::change::LocalChange< crate::change::Hunk<Option<Hash>, crate::change::Local>, crate::change::Author, >, TxnErr<T::GraphError>, >997 pub fn into_change<T: ChannelTxnT + DepsTxnT<DepsError = <T as GraphTxnT>::GraphError>>(
998 self,
999 txn: &T,
1000 channel: &ChannelRef<T>,
1001 header: crate::change::ChangeHeader,
1002 ) -> Result<
1003 crate::change::LocalChange<
1004 crate::change::Hunk<Option<Hash>, crate::change::Local>,
1005 crate::change::Author,
1006 >,
1007 TxnErr<T::GraphError>,
1008 > {
1009 let actions = self
1010 .actions
1011 .into_iter()
1012 .map(|rec| rec.globalize(txn).unwrap())
1013 .collect();
1014 let contents = if let Ok(c) = Arc::try_unwrap(self.contents) {
1015 c.into_inner()
1016 } else {
1017 unreachable!()
1018 };
1019 Ok(crate::change::LocalChange::make_change(
1020 txn,
1021 &channel,
1022 actions,
1023 contents,
1024 header,
1025 Vec::new(),
1026 )?)
1027 }
1028 }
1029
1030 #[derive(Debug)]
1031 struct MovedEdges {
1032 edges: Vec<NewEdge<Option<ChangeId>>>,
1033 alive: Vec<NewEdge<Option<ChangeId>>>,
1034 resurrect: Vec<NewEdge<Option<ChangeId>>>,
1035 need_new_name: bool,
1036 }
1037
collect_moved_edges<T: GraphTxnT, C: ChangeStore, W: WorkingCopyRead>( txn: &T, changes: &C, channel: &T::Graph, parent_pos: Position<Option<ChangeId>>, current_pos: Position<ChangeId>, new_meta: InodeMetadata, name: &str, ) -> Result<MovedEdges, RecordError<C::Error, W::Error, T::GraphError>> where <W as crate::working_copy::WorkingCopyRead>::Error: 'static,1038 fn collect_moved_edges<T: GraphTxnT, C: ChangeStore, W: WorkingCopyRead>(
1039 txn: &T,
1040 changes: &C,
1041 channel: &T::Graph,
1042 parent_pos: Position<Option<ChangeId>>,
1043 current_pos: Position<ChangeId>,
1044 new_meta: InodeMetadata,
1045 name: &str,
1046 ) -> Result<MovedEdges, RecordError<C::Error, W::Error, T::GraphError>>
1047 where
1048 <W as crate::working_copy::WorkingCopyRead>::Error: 'static,
1049 {
1050 debug!("collect_moved_edges {:?}", current_pos);
1051 let mut moved = MovedEdges {
1052 edges: Vec::new(),
1053 alive: Vec::new(),
1054 resurrect: Vec::new(),
1055 need_new_name: true,
1056 };
1057 let mut del_del = HashMap::default();
1058 let mut alive = HashMap::default();
1059 let mut previous_name = Vec::new();
1060 let mut last_alive_meta = None;
1061 let mut is_first_parent = true;
1062 for parent in iter_adjacent(
1063 txn,
1064 channel,
1065 current_pos.inode_vertex(),
1066 EdgeFlags::FOLDER | EdgeFlags::PARENT,
1067 EdgeFlags::all(),
1068 )? {
1069 let parent = parent?;
1070 if !parent
1071 .flag()
1072 .contains(EdgeFlags::FOLDER | EdgeFlags::PARENT)
1073 {
1074 continue;
1075 }
1076 debug!("parent = {:?}", parent);
1077 let mut parent_was_resurrected = false;
1078 if !parent.flag().contains(EdgeFlags::PSEUDO) {
1079 if parent.flag().contains(EdgeFlags::DELETED) {
1080 moved.resurrect.push(NewEdge {
1081 previous: parent.flag() - EdgeFlags::PARENT,
1082 flag: EdgeFlags::FOLDER | EdgeFlags::BLOCK,
1083 from: parent.dest().to_option(),
1084 to: current_pos.inode_vertex().to_option(),
1085 introduced_by: Some(parent.introduced_by()),
1086 });
1087 parent_was_resurrected = true;
1088 let v = alive
1089 .entry((parent.dest(), current_pos.inode_vertex()))
1090 .or_insert_with(Vec::new);
1091 v.push(None)
1092 } else {
1093 let v = alive
1094 .entry((parent.dest(), current_pos.inode_vertex()))
1095 .or_insert_with(Vec::new);
1096 v.push(Some(parent.introduced_by()))
1097 }
1098 }
1099 previous_name.clear();
1100 let parent_dest = txn.find_block_end(channel, parent.dest()).unwrap();
1101 let FileMetadata {
1102 metadata: parent_meta,
1103 basename: parent_name,
1104 ..
1105 } = changes
1106 .get_file_meta(
1107 |p| txn.get_external(&p).unwrap().map(From::from),
1108 *parent_dest,
1109 &mut previous_name,
1110 )
1111 .map_err(RecordError::Changestore)?;
1112 debug!(
1113 "parent_dest {:?} {:?} {:?}",
1114 parent_dest, parent_meta, parent_name
1115 );
1116 debug!("new_meta = {:?}, parent_meta = {:?}", new_meta, parent_meta);
1117 let name_changed = parent_name != name;
1118 let mut meta_changed = new_meta != parent_meta;
1119 if cfg!(windows) && !meta_changed {
1120 if let Some(m) = last_alive_meta {
1121 meta_changed = new_meta != m
1122 }
1123 }
1124 for grandparent in iter_adjacent(
1125 txn,
1126 channel,
1127 *parent_dest,
1128 EdgeFlags::FOLDER | EdgeFlags::PARENT,
1129 EdgeFlags::all(),
1130 )? {
1131 let grandparent = grandparent?;
1132 if !grandparent
1133 .flag()
1134 .contains(EdgeFlags::FOLDER | EdgeFlags::PARENT)
1135 || grandparent.flag().contains(EdgeFlags::PSEUDO)
1136 {
1137 continue;
1138 }
1139 debug!("grandparent: {:?}", grandparent);
1140 let grandparent_dest = txn.find_block_end(channel, grandparent.dest()).unwrap();
1141 assert_eq!(grandparent_dest.start, grandparent_dest.end);
1142 debug!(
1143 "grandparent_dest {:?} {:?}",
1144 grandparent_dest,
1145 std::str::from_utf8(&previous_name[2..])
1146 );
1147 let grandparent_changed = parent_pos != grandparent.dest().to_option();
1148 debug!(
1149 "change = {:?} {:?} {:?}",
1150 grandparent_changed, name_changed, meta_changed
1151 );
1152
1153 if grandparent.flag().contains(EdgeFlags::DELETED) {
1154 if !grandparent_changed && !name_changed && !meta_changed {
1155 // We resurrect the name
1156 moved.resurrect.push(NewEdge {
1157 previous: grandparent.flag() - EdgeFlags::PARENT,
1158 flag: EdgeFlags::FOLDER | EdgeFlags::BLOCK,
1159 from: grandparent.dest().to_option(),
1160 to: parent_dest.to_option(),
1161 introduced_by: Some(grandparent.introduced_by()),
1162 });
1163 if !parent_was_resurrected && !parent.flag().contains(EdgeFlags::PSEUDO) {
1164 moved.alive.push(NewEdge {
1165 previous: parent.flag() - EdgeFlags::PARENT,
1166 flag: EdgeFlags::FOLDER | EdgeFlags::BLOCK,
1167 from: parent.dest().to_option(),
1168 to: current_pos.inode_vertex().to_option(),
1169 introduced_by: Some(parent.introduced_by()),
1170 })
1171 }
1172 moved.need_new_name = false
1173 } else {
1174 // Clean up the extra deleted edges.
1175 debug!("cleanup");
1176 let v = del_del
1177 .entry((grandparent.dest(), parent_dest))
1178 .or_insert_with(Vec::new);
1179 v.push(Some(grandparent.introduced_by()))
1180 }
1181 } else if grandparent_changed
1182 || name_changed
1183 || (meta_changed && cfg!(unix))
1184 || !is_first_parent
1185 {
1186 moved.edges.push(NewEdge {
1187 previous: parent.flag() - EdgeFlags::PARENT,
1188 flag: EdgeFlags::FOLDER | EdgeFlags::BLOCK | EdgeFlags::DELETED,
1189 from: grandparent.dest().to_option(),
1190 to: parent_dest.to_option(),
1191 introduced_by: Some(grandparent.introduced_by()),
1192 });
1193 // The following is really important in missing context detection:
1194 if !parent_was_resurrected && !parent.flag().contains(EdgeFlags::PSEUDO) {
1195 moved.alive.push(NewEdge {
1196 previous: parent.flag() - EdgeFlags::PARENT,
1197 flag: EdgeFlags::FOLDER | EdgeFlags::BLOCK,
1198 from: parent.dest().to_option(),
1199 to: current_pos.inode_vertex().to_option(),
1200 introduced_by: Some(parent.introduced_by()),
1201 })
1202 }
1203 } else {
1204 last_alive_meta = Some(new_meta);
1205 let v = alive
1206 .entry((grandparent.dest(), *parent_dest))
1207 .or_insert_with(Vec::new);
1208 v.push(Some(grandparent.introduced_by()));
1209 moved.need_new_name = false
1210 }
1211 if !grandparent.flag().contains(EdgeFlags::DELETED) {
1212 is_first_parent = false;
1213 }
1214 }
1215 }
1216
1217 for ((from, to), intro) in del_del {
1218 if intro.len() > 1 {
1219 for introduced_by in intro {
1220 if introduced_by.is_some() {
1221 moved.edges.push(NewEdge {
1222 previous: EdgeFlags::FOLDER | EdgeFlags::BLOCK | EdgeFlags::DELETED,
1223 flag: EdgeFlags::FOLDER | EdgeFlags::BLOCK | EdgeFlags::DELETED,
1224 from: from.to_option(),
1225 to: to.to_option(),
1226 introduced_by,
1227 })
1228 }
1229 }
1230 }
1231 }
1232
1233 for ((from, to), intro) in alive {
1234 if intro.len() > 1 || !moved.resurrect.is_empty() {
1235 for introduced_by in intro {
1236 if introduced_by.is_some() {
1237 moved.alive.push(NewEdge {
1238 previous: EdgeFlags::FOLDER | EdgeFlags::BLOCK,
1239 flag: EdgeFlags::FOLDER | EdgeFlags::BLOCK,
1240 from: from.to_option(),
1241 to: to.to_option(),
1242 introduced_by,
1243 })
1244 }
1245 }
1246 }
1247 }
1248
1249 Ok(moved)
1250 }
1251
1252 impl Recorded {
record_deleted_file< T: GraphTxnT + TreeTxnT<TreeError = <T as GraphTxnT>::GraphError>, W: WorkingCopyRead, C: ChangeStore, >( &mut self, txn: &T, channel: &T::Graph, working_copy: &W, full_path: &str, current_vertex: Position<ChangeId>, changes: &C, ) -> Result<(), RecordError<C::Error, W::Error, T::GraphError>> where <W as WorkingCopyRead>::Error: 'static,1253 fn record_deleted_file<
1254 T: GraphTxnT + TreeTxnT<TreeError = <T as GraphTxnT>::GraphError>,
1255 W: WorkingCopyRead,
1256 C: ChangeStore,
1257 >(
1258 &mut self,
1259 txn: &T,
1260 channel: &T::Graph,
1261 working_copy: &W,
1262 full_path: &str,
1263 current_vertex: Position<ChangeId>,
1264 changes: &C,
1265 ) -> Result<(), RecordError<C::Error, W::Error, T::GraphError>>
1266 where
1267 <W as WorkingCopyRead>::Error: 'static,
1268 {
1269 debug!("record_deleted_file {:?} {:?}", current_vertex, full_path);
1270 let mut stack = vec![(current_vertex.inode_vertex(), None)];
1271 let mut visited = HashSet::default();
1272 while let Some((vertex, inode)) = stack.pop() {
1273 debug!("vertex {:?}, inode {:?}", vertex, inode);
1274 if let Some(path) = tree_path(txn, &vertex.start_pos())? {
1275 if working_copy.file_metadata(&path).is_ok() {
1276 debug!("not deleting {:?}", path);
1277 continue;
1278 }
1279 }
1280
1281 // Kill this vertex
1282 if let Some(inode) = inode {
1283 self.delete_file_edge(txn, channel, vertex, inode)?
1284 } else if vertex.start == vertex.end {
1285 debug!("delete_recursively {:?}", vertex);
1286 // Killing an inode.
1287 {
1288 let mut deleted_vertices = self.deleted_vertices.lock();
1289 if !deleted_vertices.insert(vertex.start_pos()) {
1290 continue;
1291 }
1292 }
1293 if let Some(inode) = txn.get_revinodes(&vertex.start_pos(), None)? {
1294 debug!(
1295 "delete_recursively, vertex = {:?}, inode = {:?}",
1296 vertex, inode
1297 );
1298 self.recorded_inodes
1299 .lock()
1300 .insert(*inode, vertex.start_pos().to_option());
1301 self.updatables
1302 .insert(self.actions.len(), InodeUpdate::Deleted { inode: *inode });
1303 }
1304 self.delete_inode_vertex::<_, _, W>(
1305 changes,
1306 txn,
1307 channel,
1308 vertex,
1309 vertex.start_pos(),
1310 full_path,
1311 )?
1312 }
1313
1314 // Move on to the descendants.
1315 for edge in iter_adjacent(
1316 txn,
1317 channel,
1318 vertex,
1319 EdgeFlags::empty(),
1320 EdgeFlags::all() - EdgeFlags::DELETED - EdgeFlags::PARENT,
1321 )? {
1322 let edge = edge?;
1323 debug!("delete_recursively, edge: {:?}", edge);
1324 let dest = txn
1325 .find_block(channel, edge.dest())
1326 .expect("delete_recursively, descendants");
1327 let inode = if inode.is_some() {
1328 assert!(!edge.flag().contains(EdgeFlags::FOLDER));
1329 inode
1330 } else if edge.flag().contains(EdgeFlags::FOLDER) {
1331 None
1332 } else {
1333 assert_eq!(vertex.start, vertex.end);
1334 Some(vertex.start_pos())
1335 };
1336 if visited.insert(edge.dest()) {
1337 stack.push((*dest, inode))
1338 }
1339 }
1340 }
1341 Ok(())
1342 }
1343
delete_inode_vertex<T: GraphTxnT, C: ChangeStore, W: WorkingCopyRead>( &mut self, changes: &C, txn: &T, channel: &T::Graph, vertex: Vertex<ChangeId>, inode: Position<ChangeId>, path: &str, ) -> Result<(), RecordError<C::Error, W::Error, T::GraphError>> where <W as WorkingCopyRead>::Error: 'static,1344 fn delete_inode_vertex<T: GraphTxnT, C: ChangeStore, W: WorkingCopyRead>(
1345 &mut self,
1346 changes: &C,
1347 txn: &T,
1348 channel: &T::Graph,
1349 vertex: Vertex<ChangeId>,
1350 inode: Position<ChangeId>,
1351 path: &str,
1352 ) -> Result<(), RecordError<C::Error, W::Error, T::GraphError>>
1353 where
1354 <W as WorkingCopyRead>::Error: 'static,
1355 {
1356 debug!("delete_inode_vertex {:?}", path);
1357 let mut edges = Vec::new();
1358 let mut enc = None;
1359 let mut previous_name = Vec::new();
1360 for parent in iter_adjacent(
1361 txn,
1362 channel,
1363 vertex,
1364 EdgeFlags::FOLDER | EdgeFlags::PARENT,
1365 EdgeFlags::all(),
1366 )? {
1367 let parent = parent?;
1368 if !parent.flag().contains(EdgeFlags::PARENT) {
1369 continue;
1370 }
1371 assert!(parent.flag().contains(EdgeFlags::FOLDER));
1372 let parent_dest = txn.find_block_end(channel, parent.dest()).unwrap();
1373 if enc.is_none() {
1374 let FileMetadata { encoding, .. } = changes
1375 .get_file_meta(
1376 |p| txn.get_external(&p).unwrap().map(From::from),
1377 *parent_dest,
1378 &mut previous_name,
1379 )
1380 .map_err(RecordError::Changestore)?;
1381 enc = Some(encoding);
1382 }
1383
1384 for grandparent in iter_adjacent(
1385 txn,
1386 channel,
1387 *parent_dest,
1388 EdgeFlags::FOLDER | EdgeFlags::PARENT,
1389 EdgeFlags::all(),
1390 )? {
1391 let grandparent = grandparent?;
1392 if !grandparent.flag().contains(EdgeFlags::PARENT)
1393 || grandparent.flag().contains(EdgeFlags::PSEUDO)
1394 {
1395 continue;
1396 }
1397 assert!(grandparent.flag().contains(EdgeFlags::PARENT));
1398 assert!(grandparent.flag().contains(EdgeFlags::FOLDER));
1399 edges.push(NewEdge {
1400 previous: grandparent.flag() - EdgeFlags::PARENT,
1401 flag: EdgeFlags::FOLDER | EdgeFlags::BLOCK | EdgeFlags::DELETED,
1402 from: grandparent.dest().to_option(),
1403 to: parent_dest.to_option(),
1404 introduced_by: Some(grandparent.introduced_by()),
1405 });
1406 }
1407 if !parent.flag().contains(EdgeFlags::PSEUDO) {
1408 edges.push(NewEdge {
1409 previous: parent.flag() - EdgeFlags::PARENT,
1410 flag: EdgeFlags::FOLDER | EdgeFlags::BLOCK | EdgeFlags::DELETED,
1411 from: parent.dest().to_option(),
1412 to: vertex.to_option(),
1413 introduced_by: Some(parent.introduced_by()),
1414 });
1415 }
1416 }
1417 debug!("deleting {:?}", edges);
1418 if !edges.is_empty() {
1419 self.actions.push(Hunk::FileDel {
1420 del: Atom::EdgeMap(EdgeMap {
1421 edges,
1422 inode: inode.to_option(),
1423 }),
1424 contents: None,
1425 path: path.to_string(),
1426 encoding: enc.unwrap(),
1427 })
1428 }
1429 Ok(())
1430 }
1431
delete_file_edge<T: GraphTxnT>( &mut self, txn: &T, channel: &T::Graph, to: Vertex<ChangeId>, inode: Position<ChangeId>, ) -> Result<(), TxnErr<T::GraphError>>1432 fn delete_file_edge<T: GraphTxnT>(
1433 &mut self,
1434 txn: &T,
1435 channel: &T::Graph,
1436 to: Vertex<ChangeId>,
1437 inode: Position<ChangeId>,
1438 ) -> Result<(), TxnErr<T::GraphError>> {
1439 if let Some(Hunk::FileDel {
1440 ref mut contents, ..
1441 }) = self.actions.last_mut()
1442 {
1443 if contents.is_none() {
1444 *contents = Some(Atom::EdgeMap(EdgeMap {
1445 edges: Vec::new(),
1446 inode: inode.to_option(),
1447 }))
1448 }
1449 if let Some(Atom::EdgeMap(mut e)) = contents.take() {
1450 for parent in iter_adjacent(
1451 txn,
1452 channel,
1453 to,
1454 EdgeFlags::PARENT,
1455 EdgeFlags::all() - EdgeFlags::DELETED,
1456 )? {
1457 let parent = parent?;
1458 if parent.flag().contains(EdgeFlags::PSEUDO) {
1459 continue;
1460 }
1461 assert!(parent.flag().contains(EdgeFlags::PARENT));
1462 assert!(!parent.flag().contains(EdgeFlags::FOLDER));
1463 e.edges.push(NewEdge {
1464 previous: parent.flag() - EdgeFlags::PARENT,
1465 flag: (parent.flag() - EdgeFlags::PARENT) | EdgeFlags::DELETED,
1466 from: parent.dest().to_option(),
1467 to: to.to_option(),
1468 introduced_by: Some(parent.introduced_by()),
1469 })
1470 }
1471 if !e.edges.is_empty() {
1472 *contents = Some(Atom::EdgeMap(e))
1473 }
1474 }
1475 } else {
1476 unreachable!()
1477 }
1478 Ok(())
1479 }
1480 }
1481