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: &regex::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: &regex::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: &regex::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: &regex::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