##// END OF EJS Templates
dirstate-tree: Add clear_ambiguous_times in the new DirstateMap...
Simon Sapin -
r47875:ba17a2ee default
parent child Browse files
Show More
@@ -1,505 +1,498 b''
1 1 // dirstate_map.rs
2 2 //
3 3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 4 //
5 5 // This software may be used and distributed according to the terms of the
6 6 // GNU General Public License version 2 or any later version.
7 7
8 use crate::dirstate::parsers::clear_ambiguous_mtime;
8 9 use crate::dirstate::parsers::Timestamp;
9 10 use crate::errors::HgError;
10 11 use crate::revlog::node::NULL_NODE;
11 12 use crate::{
12 13 dirstate::{parsers::PARENT_SIZE, EntryState, SIZE_FROM_OTHER_PARENT},
13 14 pack_dirstate, parse_dirstate,
14 15 utils::{
15 16 files::normalize_case,
16 17 hg_path::{HgPath, HgPathBuf},
17 18 },
18 19 CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateMapError,
19 20 DirstateParents, FastHashMap, StateMap,
20 21 };
21 22 use micro_timer::timed;
22 23 use std::collections::HashSet;
23 24 use std::convert::TryInto;
24 25 use std::iter::FromIterator;
25 26 use std::ops::Deref;
26 27
27 28 pub type FileFoldMap = FastHashMap<HgPathBuf, HgPathBuf>;
28 29
29 30 const MTIME_UNSET: i32 = -1;
30 31
31 32 #[derive(Default)]
32 33 pub struct DirstateMap {
33 34 state_map: StateMap,
34 35 pub copy_map: CopyMap,
35 36 file_fold_map: Option<FileFoldMap>,
36 37 pub dirs: Option<DirsMultiset>,
37 38 pub all_dirs: Option<DirsMultiset>,
38 39 non_normal_set: Option<HashSet<HgPathBuf>>,
39 40 other_parent_set: Option<HashSet<HgPathBuf>>,
40 41 parents: Option<DirstateParents>,
41 42 dirty_parents: bool,
42 43 }
43 44
44 45 /// Should only really be used in python interface code, for clarity
45 46 impl Deref for DirstateMap {
46 47 type Target = StateMap;
47 48
48 49 fn deref(&self) -> &Self::Target {
49 50 &self.state_map
50 51 }
51 52 }
52 53
53 54 impl FromIterator<(HgPathBuf, DirstateEntry)> for DirstateMap {
54 55 fn from_iter<I: IntoIterator<Item = (HgPathBuf, DirstateEntry)>>(
55 56 iter: I,
56 57 ) -> Self {
57 58 Self {
58 59 state_map: iter.into_iter().collect(),
59 60 ..Self::default()
60 61 }
61 62 }
62 63 }
63 64
64 65 impl DirstateMap {
65 66 pub fn new() -> Self {
66 67 Self::default()
67 68 }
68 69
69 70 pub fn clear(&mut self) {
70 71 self.state_map = StateMap::default();
71 72 self.copy_map.clear();
72 73 self.file_fold_map = None;
73 74 self.non_normal_set = None;
74 75 self.other_parent_set = None;
75 76 self.set_parents(&DirstateParents {
76 77 p1: NULL_NODE,
77 78 p2: NULL_NODE,
78 79 })
79 80 }
80 81
81 82 /// Add a tracked file to the dirstate
82 83 pub fn add_file(
83 84 &mut self,
84 85 filename: &HgPath,
85 86 old_state: EntryState,
86 87 entry: DirstateEntry,
87 88 ) -> Result<(), DirstateMapError> {
88 89 if old_state == EntryState::Unknown || old_state == EntryState::Removed
89 90 {
90 91 if let Some(ref mut dirs) = self.dirs {
91 92 dirs.add_path(filename)?;
92 93 }
93 94 }
94 95 if old_state == EntryState::Unknown {
95 96 if let Some(ref mut all_dirs) = self.all_dirs {
96 97 all_dirs.add_path(filename)?;
97 98 }
98 99 }
99 100 self.state_map.insert(filename.to_owned(), entry.to_owned());
100 101
101 102 if entry.state != EntryState::Normal || entry.mtime == MTIME_UNSET {
102 103 self.get_non_normal_other_parent_entries()
103 104 .0
104 105 .insert(filename.to_owned());
105 106 }
106 107
107 108 if entry.size == SIZE_FROM_OTHER_PARENT {
108 109 self.get_non_normal_other_parent_entries()
109 110 .1
110 111 .insert(filename.to_owned());
111 112 }
112 113 Ok(())
113 114 }
114 115
115 116 /// Mark a file as removed in the dirstate.
116 117 ///
117 118 /// The `size` parameter is used to store sentinel values that indicate
118 119 /// the file's previous state. In the future, we should refactor this
119 120 /// to be more explicit about what that state is.
120 121 pub fn remove_file(
121 122 &mut self,
122 123 filename: &HgPath,
123 124 old_state: EntryState,
124 125 size: i32,
125 126 ) -> Result<(), DirstateMapError> {
126 127 if old_state != EntryState::Unknown && old_state != EntryState::Removed
127 128 {
128 129 if let Some(ref mut dirs) = self.dirs {
129 130 dirs.delete_path(filename)?;
130 131 }
131 132 }
132 133 if old_state == EntryState::Unknown {
133 134 if let Some(ref mut all_dirs) = self.all_dirs {
134 135 all_dirs.add_path(filename)?;
135 136 }
136 137 }
137 138
138 139 if let Some(ref mut file_fold_map) = self.file_fold_map {
139 140 file_fold_map.remove(&normalize_case(filename));
140 141 }
141 142 self.state_map.insert(
142 143 filename.to_owned(),
143 144 DirstateEntry {
144 145 state: EntryState::Removed,
145 146 mode: 0,
146 147 size,
147 148 mtime: 0,
148 149 },
149 150 );
150 151 self.get_non_normal_other_parent_entries()
151 152 .0
152 153 .insert(filename.to_owned());
153 154 Ok(())
154 155 }
155 156
156 157 /// Remove a file from the dirstate.
157 158 /// Returns `true` if the file was previously recorded.
158 159 pub fn drop_file(
159 160 &mut self,
160 161 filename: &HgPath,
161 162 old_state: EntryState,
162 163 ) -> Result<bool, DirstateMapError> {
163 164 let exists = self.state_map.remove(filename).is_some();
164 165
165 166 if exists {
166 167 if old_state != EntryState::Removed {
167 168 if let Some(ref mut dirs) = self.dirs {
168 169 dirs.delete_path(filename)?;
169 170 }
170 171 }
171 172 if let Some(ref mut all_dirs) = self.all_dirs {
172 173 all_dirs.delete_path(filename)?;
173 174 }
174 175 }
175 176 if let Some(ref mut file_fold_map) = self.file_fold_map {
176 177 file_fold_map.remove(&normalize_case(filename));
177 178 }
178 179 self.get_non_normal_other_parent_entries()
179 180 .0
180 181 .remove(filename);
181 182
182 183 Ok(exists)
183 184 }
184 185
185 186 pub fn clear_ambiguous_times(
186 187 &mut self,
187 188 filenames: Vec<HgPathBuf>,
188 189 now: i32,
189 190 ) {
190 191 for filename in filenames {
191 let mut changed = false;
192 192 if let Some(entry) = self.state_map.get_mut(&filename) {
193 if entry.state == EntryState::Normal && entry.mtime == now {
194 changed = true;
195 *entry = DirstateEntry {
196 mtime: MTIME_UNSET,
197 ..*entry
198 };
199 }
200 }
201 if changed {
193 if clear_ambiguous_mtime(entry, now) {
202 194 self.get_non_normal_other_parent_entries()
203 195 .0
204 196 .insert(filename.to_owned());
205 197 }
206 198 }
207 199 }
200 }
208 201
209 202 pub fn non_normal_entries_remove(
210 203 &mut self,
211 204 key: impl AsRef<HgPath>,
212 205 ) -> bool {
213 206 self.get_non_normal_other_parent_entries()
214 207 .0
215 208 .remove(key.as_ref())
216 209 }
217 210 pub fn non_normal_entries_union(
218 211 &mut self,
219 212 other: HashSet<HgPathBuf>,
220 213 ) -> Vec<HgPathBuf> {
221 214 self.get_non_normal_other_parent_entries()
222 215 .0
223 216 .union(&other)
224 217 .map(ToOwned::to_owned)
225 218 .collect()
226 219 }
227 220
228 221 pub fn get_non_normal_other_parent_entries(
229 222 &mut self,
230 223 ) -> (&mut HashSet<HgPathBuf>, &mut HashSet<HgPathBuf>) {
231 224 self.set_non_normal_other_parent_entries(false);
232 225 (
233 226 self.non_normal_set.as_mut().unwrap(),
234 227 self.other_parent_set.as_mut().unwrap(),
235 228 )
236 229 }
237 230
238 231 /// Useful to get immutable references to those sets in contexts where
239 232 /// you only have an immutable reference to the `DirstateMap`, like when
240 233 /// sharing references with Python.
241 234 ///
242 235 /// TODO, get rid of this along with the other "setter/getter" stuff when
243 236 /// a nice typestate plan is defined.
244 237 ///
245 238 /// # Panics
246 239 ///
247 240 /// Will panic if either set is `None`.
248 241 pub fn get_non_normal_other_parent_entries_panic(
249 242 &self,
250 243 ) -> (&HashSet<HgPathBuf>, &HashSet<HgPathBuf>) {
251 244 (
252 245 self.non_normal_set.as_ref().unwrap(),
253 246 self.other_parent_set.as_ref().unwrap(),
254 247 )
255 248 }
256 249
257 250 pub fn set_non_normal_other_parent_entries(&mut self, force: bool) {
258 251 if !force
259 252 && self.non_normal_set.is_some()
260 253 && self.other_parent_set.is_some()
261 254 {
262 255 return;
263 256 }
264 257 let mut non_normal = HashSet::new();
265 258 let mut other_parent = HashSet::new();
266 259
267 260 for (
268 261 filename,
269 262 DirstateEntry {
270 263 state, size, mtime, ..
271 264 },
272 265 ) in self.state_map.iter()
273 266 {
274 267 if *state != EntryState::Normal || *mtime == MTIME_UNSET {
275 268 non_normal.insert(filename.to_owned());
276 269 }
277 270 if *state == EntryState::Normal && *size == SIZE_FROM_OTHER_PARENT
278 271 {
279 272 other_parent.insert(filename.to_owned());
280 273 }
281 274 }
282 275 self.non_normal_set = Some(non_normal);
283 276 self.other_parent_set = Some(other_parent);
284 277 }
285 278
286 279 /// Both of these setters and their uses appear to be the simplest way to
287 280 /// emulate a Python lazy property, but it is ugly and unidiomatic.
288 281 /// TODO One day, rewriting this struct using the typestate might be a
289 282 /// good idea.
290 283 pub fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
291 284 if self.all_dirs.is_none() {
292 285 self.all_dirs = Some(DirsMultiset::from_dirstate(
293 286 self.state_map.iter(),
294 287 None,
295 288 )?);
296 289 }
297 290 Ok(())
298 291 }
299 292
300 293 pub fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
301 294 if self.dirs.is_none() {
302 295 self.dirs = Some(DirsMultiset::from_dirstate(
303 296 &self.state_map,
304 297 Some(EntryState::Removed),
305 298 )?);
306 299 }
307 300 Ok(())
308 301 }
309 302
310 303 pub fn has_tracked_dir(
311 304 &mut self,
312 305 directory: &HgPath,
313 306 ) -> Result<bool, DirstateMapError> {
314 307 self.set_dirs()?;
315 308 Ok(self.dirs.as_ref().unwrap().contains(directory))
316 309 }
317 310
318 311 pub fn has_dir(
319 312 &mut self,
320 313 directory: &HgPath,
321 314 ) -> Result<bool, DirstateMapError> {
322 315 self.set_all_dirs()?;
323 316 Ok(self.all_dirs.as_ref().unwrap().contains(directory))
324 317 }
325 318
326 319 pub fn parents(
327 320 &mut self,
328 321 file_contents: &[u8],
329 322 ) -> Result<&DirstateParents, DirstateError> {
330 323 if let Some(ref parents) = self.parents {
331 324 return Ok(parents);
332 325 }
333 326 let parents;
334 327 if file_contents.len() == PARENT_SIZE * 2 {
335 328 parents = DirstateParents {
336 329 p1: file_contents[..PARENT_SIZE].try_into().unwrap(),
337 330 p2: file_contents[PARENT_SIZE..PARENT_SIZE * 2]
338 331 .try_into()
339 332 .unwrap(),
340 333 };
341 334 } else if file_contents.is_empty() {
342 335 parents = DirstateParents {
343 336 p1: NULL_NODE,
344 337 p2: NULL_NODE,
345 338 };
346 339 } else {
347 340 return Err(
348 341 HgError::corrupted("Dirstate appears to be damaged").into()
349 342 );
350 343 }
351 344
352 345 self.parents = Some(parents);
353 346 Ok(self.parents.as_ref().unwrap())
354 347 }
355 348
356 349 pub fn set_parents(&mut self, parents: &DirstateParents) {
357 350 self.parents = Some(parents.clone());
358 351 self.dirty_parents = true;
359 352 }
360 353
361 354 #[timed]
362 355 pub fn read<'a>(
363 356 &mut self,
364 357 file_contents: &'a [u8],
365 358 ) -> Result<Option<&'a DirstateParents>, DirstateError> {
366 359 if file_contents.is_empty() {
367 360 return Ok(None);
368 361 }
369 362
370 363 let (parents, entries, copies) = parse_dirstate(file_contents)?;
371 364 self.state_map.extend(
372 365 entries
373 366 .into_iter()
374 367 .map(|(path, entry)| (path.to_owned(), entry)),
375 368 );
376 369 self.copy_map.extend(
377 370 copies
378 371 .into_iter()
379 372 .map(|(path, copy)| (path.to_owned(), copy.to_owned())),
380 373 );
381 374
382 375 if !self.dirty_parents {
383 376 self.set_parents(&parents);
384 377 }
385 378
386 379 Ok(Some(parents))
387 380 }
388 381
389 382 pub fn pack(
390 383 &mut self,
391 384 parents: DirstateParents,
392 385 now: Timestamp,
393 386 ) -> Result<Vec<u8>, DirstateError> {
394 387 let packed =
395 388 pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
396 389
397 390 self.dirty_parents = false;
398 391
399 392 self.set_non_normal_other_parent_entries(true);
400 393 Ok(packed)
401 394 }
402 395 pub fn build_file_fold_map(&mut self) -> &FileFoldMap {
403 396 if let Some(ref file_fold_map) = self.file_fold_map {
404 397 return file_fold_map;
405 398 }
406 399 let mut new_file_fold_map = FileFoldMap::default();
407 400
408 401 for (filename, DirstateEntry { state, .. }) in self.state_map.iter() {
409 402 if *state != EntryState::Removed {
410 403 new_file_fold_map
411 404 .insert(normalize_case(&filename), filename.to_owned());
412 405 }
413 406 }
414 407 self.file_fold_map = Some(new_file_fold_map);
415 408 self.file_fold_map.as_ref().unwrap()
416 409 }
417 410 }
418 411
419 412 #[cfg(test)]
420 413 mod tests {
421 414 use super::*;
422 415
423 416 #[test]
424 417 fn test_dirs_multiset() {
425 418 let mut map = DirstateMap::new();
426 419 assert!(map.dirs.is_none());
427 420 assert!(map.all_dirs.is_none());
428 421
429 422 assert_eq!(map.has_dir(HgPath::new(b"nope")).unwrap(), false);
430 423 assert!(map.all_dirs.is_some());
431 424 assert!(map.dirs.is_none());
432 425
433 426 assert_eq!(map.has_tracked_dir(HgPath::new(b"nope")).unwrap(), false);
434 427 assert!(map.dirs.is_some());
435 428 }
436 429
437 430 #[test]
438 431 fn test_add_file() {
439 432 let mut map = DirstateMap::new();
440 433
441 434 assert_eq!(0, map.len());
442 435
443 436 map.add_file(
444 437 HgPath::new(b"meh"),
445 438 EntryState::Normal,
446 439 DirstateEntry {
447 440 state: EntryState::Normal,
448 441 mode: 1337,
449 442 mtime: 1337,
450 443 size: 1337,
451 444 },
452 445 )
453 446 .unwrap();
454 447
455 448 assert_eq!(1, map.len());
456 449 assert_eq!(0, map.get_non_normal_other_parent_entries().0.len());
457 450 assert_eq!(0, map.get_non_normal_other_parent_entries().1.len());
458 451 }
459 452
460 453 #[test]
461 454 fn test_non_normal_other_parent_entries() {
462 455 let mut map: DirstateMap = [
463 456 (b"f1", (EntryState::Removed, 1337, 1337, 1337)),
464 457 (b"f2", (EntryState::Normal, 1337, 1337, -1)),
465 458 (b"f3", (EntryState::Normal, 1337, 1337, 1337)),
466 459 (b"f4", (EntryState::Normal, 1337, -2, 1337)),
467 460 (b"f5", (EntryState::Added, 1337, 1337, 1337)),
468 461 (b"f6", (EntryState::Added, 1337, 1337, -1)),
469 462 (b"f7", (EntryState::Merged, 1337, 1337, -1)),
470 463 (b"f8", (EntryState::Merged, 1337, 1337, 1337)),
471 464 (b"f9", (EntryState::Merged, 1337, -2, 1337)),
472 465 (b"fa", (EntryState::Added, 1337, -2, 1337)),
473 466 (b"fb", (EntryState::Removed, 1337, -2, 1337)),
474 467 ]
475 468 .iter()
476 469 .map(|(fname, (state, mode, size, mtime))| {
477 470 (
478 471 HgPathBuf::from_bytes(fname.as_ref()),
479 472 DirstateEntry {
480 473 state: *state,
481 474 mode: *mode,
482 475 size: *size,
483 476 mtime: *mtime,
484 477 },
485 478 )
486 479 })
487 480 .collect();
488 481
489 482 let mut non_normal = [
490 483 b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
491 484 ]
492 485 .iter()
493 486 .map(|x| HgPathBuf::from_bytes(x.as_ref()))
494 487 .collect();
495 488
496 489 let mut other_parent = HashSet::new();
497 490 other_parent.insert(HgPathBuf::from_bytes(b"f4"));
498 491 let entries = map.get_non_normal_other_parent_entries();
499 492
500 493 assert_eq!(
501 494 (&mut non_normal, &mut other_parent),
502 495 (entries.0, entries.1)
503 496 );
504 497 }
505 498 }
@@ -1,524 +1,526 b''
1 1 use bytes_cast::BytesCast;
2 2 use std::path::PathBuf;
3 3 use std::{collections::BTreeMap, convert::TryInto};
4 4
5 5 use super::path_with_basename::WithBasename;
6 6 use crate::dirstate::parsers::clear_ambiguous_mtime;
7 7 use crate::dirstate::parsers::pack_entry;
8 8 use crate::dirstate::parsers::packed_entry_size;
9 9 use crate::dirstate::parsers::parse_dirstate_entries;
10 10 use crate::dirstate::parsers::parse_dirstate_parents;
11 11 use crate::dirstate::parsers::Timestamp;
12 12 use crate::matchers::Matcher;
13 13 use crate::revlog::node::NULL_NODE;
14 14 use crate::utils::hg_path::{HgPath, HgPathBuf};
15 15 use crate::CopyMapIter;
16 16 use crate::DirstateEntry;
17 17 use crate::DirstateError;
18 18 use crate::DirstateMapError;
19 19 use crate::DirstateParents;
20 20 use crate::DirstateStatus;
21 21 use crate::EntryState;
22 22 use crate::FastHashMap;
23 23 use crate::HgPathCow;
24 24 use crate::PatternFileWarning;
25 25 use crate::StateMapIter;
26 26 use crate::StatusError;
27 27 use crate::StatusOptions;
28 28
29 29 pub struct DirstateMap {
30 30 parents: Option<DirstateParents>,
31 31 dirty_parents: bool,
32 32 root: ChildNodes,
33 33
34 34 /// Number of nodes anywhere in the tree that have `.entry.is_some()`.
35 35 nodes_with_entry_count: usize,
36 36
37 37 /// Number of nodes anywhere in the tree that have
38 38 /// `.copy_source.is_some()`.
39 39 nodes_with_copy_source_count: usize,
40 40 }
41 41
42 42 /// Using a plain `HgPathBuf` of the full path from the repository root as a
43 43 /// map key would also work: all paths in a given map have the same parent
44 44 /// path, so comparing full paths gives the same result as comparing base
45 45 /// names. However `BTreeMap` would waste time always re-comparing the same
46 46 /// string prefix.
47 47 type ChildNodes = BTreeMap<WithBasename<HgPathBuf>, Node>;
48 48
49 49 #[derive(Default)]
50 50 struct Node {
51 51 entry: Option<DirstateEntry>,
52 52 copy_source: Option<HgPathBuf>,
53 53 children: ChildNodes,
54 54 }
55 55
56 56 /// `(full_path, entry, copy_source)`
57 57 type NodeDataMut<'a> = (
58 58 &'a WithBasename<HgPathBuf>,
59 59 &'a mut Option<DirstateEntry>,
60 60 &'a mut Option<HgPathBuf>,
61 61 );
62 62
63 63 impl DirstateMap {
64 64 pub fn new() -> Self {
65 65 Self {
66 66 parents: None,
67 67 dirty_parents: false,
68 68 root: ChildNodes::new(),
69 69 nodes_with_entry_count: 0,
70 70 nodes_with_copy_source_count: 0,
71 71 }
72 72 }
73 73
74 74 fn get_node(&self, path: &HgPath) -> Option<&Node> {
75 75 let mut children = &self.root;
76 76 let mut components = path.components();
77 77 let mut component =
78 78 components.next().expect("expected at least one components");
79 79 loop {
80 80 let child = children.get(component)?;
81 81 if let Some(next_component) = components.next() {
82 82 component = next_component;
83 83 children = &child.children;
84 84 } else {
85 85 return Some(child);
86 86 }
87 87 }
88 88 }
89 89
90 90 /// This takes `root` instead of `&mut self` so that callers can mutate
91 91 /// other fields while the returned borrow is still valid
92 92 fn get_node_mut<'tree>(
93 93 root: &'tree mut ChildNodes,
94 94 path: &HgPath,
95 95 ) -> Option<&'tree mut Node> {
96 96 let mut children = root;
97 97 let mut components = path.components();
98 98 let mut component =
99 99 components.next().expect("expected at least one components");
100 100 loop {
101 101 let child = children.get_mut(component)?;
102 102 if let Some(next_component) = components.next() {
103 103 component = next_component;
104 104 children = &mut child.children;
105 105 } else {
106 106 return Some(child);
107 107 }
108 108 }
109 109 }
110 110
111 111 fn get_or_insert_node<'tree>(
112 112 root: &'tree mut ChildNodes,
113 113 path: &HgPath,
114 114 ) -> &'tree mut Node {
115 115 let mut child_nodes = root;
116 116 let mut inclusive_ancestor_paths =
117 117 WithBasename::inclusive_ancestors_of(path);
118 118 let mut ancestor_path = inclusive_ancestor_paths
119 119 .next()
120 120 .expect("expected at least one inclusive ancestor");
121 121 loop {
122 122 // TODO: can we avoid double lookup in all cases without allocating
123 123 // an owned key in cases where the map already contains that key?
124 124 let child_node =
125 125 if child_nodes.contains_key(ancestor_path.base_name()) {
126 126 child_nodes.get_mut(ancestor_path.base_name()).unwrap()
127 127 } else {
128 128 // This is always a vacant entry, using `.entry()` lets us
129 129 // return a `&mut Node` of the newly-inserted node without
130 130 // yet another lookup. `BTreeMap::insert` doesn’t do this.
131 131 child_nodes.entry(ancestor_path.to_owned()).or_default()
132 132 };
133 133 if let Some(next) = inclusive_ancestor_paths.next() {
134 134 ancestor_path = next;
135 135 child_nodes = &mut child_node.children;
136 136 } else {
137 137 return child_node;
138 138 }
139 139 }
140 140 }
141 141
142 142 /// The meaning of `new_copy_source` is:
143 143 ///
144 144 /// * `Some(Some(x))`: set `Node::copy_source` to `Some(x)`
145 145 /// * `Some(None)`: set `Node::copy_source` to `None`
146 146 /// * `None`: leave `Node::copy_source` unchanged
147 147 fn add_file_node(
148 148 &mut self,
149 149 path: &HgPath,
150 150 new_entry: DirstateEntry,
151 151 new_copy_source: Option<Option<HgPathBuf>>,
152 152 ) {
153 153 let node = Self::get_or_insert_node(&mut self.root, path);
154 154 if node.entry.is_none() {
155 155 self.nodes_with_entry_count += 1
156 156 }
157 157 if let Some(source) = &new_copy_source {
158 158 if node.copy_source.is_none() && source.is_some() {
159 159 self.nodes_with_copy_source_count += 1
160 160 }
161 161 if node.copy_source.is_some() && source.is_none() {
162 162 self.nodes_with_copy_source_count -= 1
163 163 }
164 164 }
165 165 node.entry = Some(new_entry);
166 166 if let Some(source) = new_copy_source {
167 167 node.copy_source = source
168 168 }
169 169 }
170 170
171 171 fn iter_nodes<'a>(
172 172 &'a self,
173 173 ) -> impl Iterator<Item = (&'a WithBasename<HgPathBuf>, &'a Node)> + 'a
174 174 {
175 175 // Depth first tree traversal.
176 176 //
177 177 // If we could afford internal iteration and recursion,
178 178 // this would look like:
179 179 //
180 180 // ```
181 181 // fn traverse_children(
182 182 // children: &ChildNodes,
183 183 // each: &mut impl FnMut(&Node),
184 184 // ) {
185 185 // for child in children.values() {
186 186 // traverse_children(&child.children, each);
187 187 // each(child);
188 188 // }
189 189 // }
190 190 // ```
191 191 //
192 192 // However we want an external iterator and therefore can’t use the
193 193 // call stack. Use an explicit stack instead:
194 194 let mut stack = Vec::new();
195 195 let mut iter = self.root.iter();
196 196 std::iter::from_fn(move || {
197 197 while let Some((key, child_node)) = iter.next() {
198 198 // Pseudo-recursion
199 199 let new_iter = child_node.children.iter();
200 200 let old_iter = std::mem::replace(&mut iter, new_iter);
201 201 stack.push((key, child_node, old_iter));
202 202 }
203 203 // Found the end of a `children.iter()` iterator.
204 204 if let Some((key, child_node, next_iter)) = stack.pop() {
205 205 // "Return" from pseudo-recursion by restoring state from the
206 206 // explicit stack
207 207 iter = next_iter;
208 208
209 209 Some((key, child_node))
210 210 } else {
211 211 // Reached the bottom of the stack, we’re done
212 212 None
213 213 }
214 214 })
215 215 }
216 216
217 217 /// Mutable iterator for the `(entry, copy source)` of each node.
218 218 ///
219 219 /// It would not be safe to yield mutable references to nodes themeselves
220 220 /// with `-> impl Iterator<Item = &mut Node>` since child nodes are
221 221 /// reachable from their ancestor nodes, potentially creating multiple
222 222 /// `&mut` references to a given node.
223 223 fn iter_node_data_mut<'a>(
224 224 &'a mut self,
225 225 ) -> impl Iterator<Item = NodeDataMut<'a>> + 'a {
226 226 // Explict stack for pseudo-recursion, see `iter_nodes` above.
227 227 let mut stack = Vec::new();
228 228 let mut iter = self.root.iter_mut();
229 229 std::iter::from_fn(move || {
230 230 while let Some((key, child_node)) = iter.next() {
231 231 // Pseudo-recursion
232 232 let data =
233 233 (key, &mut child_node.entry, &mut child_node.copy_source);
234 234 let new_iter = child_node.children.iter_mut();
235 235 let old_iter = std::mem::replace(&mut iter, new_iter);
236 236 stack.push((data, old_iter));
237 237 }
238 238 // Found the end of a `children.values_mut()` iterator.
239 239 if let Some((data, next_iter)) = stack.pop() {
240 240 // "Return" from pseudo-recursion by restoring state from the
241 241 // explicit stack
242 242 iter = next_iter;
243 243
244 244 Some(data)
245 245 } else {
246 246 // Reached the bottom of the stack, we’re done
247 247 None
248 248 }
249 249 })
250 250 }
251 251 }
252 252
253 253 impl super::dispatch::DirstateMapMethods for DirstateMap {
254 254 fn clear(&mut self) {
255 255 self.set_parents(&DirstateParents {
256 256 p1: NULL_NODE,
257 257 p2: NULL_NODE,
258 258 });
259 259 self.root.clear();
260 260 self.nodes_with_entry_count = 0;
261 261 self.nodes_with_copy_source_count = 0;
262 262 }
263 263
264 264 fn add_file(
265 265 &mut self,
266 266 _filename: &HgPath,
267 267 _old_state: EntryState,
268 268 _entry: DirstateEntry,
269 269 ) -> Result<(), DirstateMapError> {
270 270 todo!()
271 271 }
272 272
273 273 fn remove_file(
274 274 &mut self,
275 275 _filename: &HgPath,
276 276 _old_state: EntryState,
277 277 _size: i32,
278 278 ) -> Result<(), DirstateMapError> {
279 279 todo!()
280 280 }
281 281
282 282 fn drop_file(
283 283 &mut self,
284 284 _filename: &HgPath,
285 285 _old_state: EntryState,
286 286 ) -> Result<bool, DirstateMapError> {
287 287 todo!()
288 288 }
289 289
290 fn clear_ambiguous_times(
291 &mut self,
292 _filenames: Vec<HgPathBuf>,
293 _now: i32,
294 ) {
295 todo!()
290 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32) {
291 for filename in filenames {
292 if let Some(node) = Self::get_node_mut(&mut self.root, &filename) {
293 if let Some(entry) = node.entry.as_mut() {
294 clear_ambiguous_mtime(entry, now);
295 }
296 }
297 }
296 298 }
297 299
298 300 fn non_normal_entries_contains(&mut self, _key: &HgPath) -> bool {
299 301 todo!()
300 302 }
301 303
302 304 fn non_normal_entries_remove(&mut self, _key: &HgPath) -> bool {
303 305 todo!()
304 306 }
305 307
306 308 fn non_normal_or_other_parent_paths(
307 309 &mut self,
308 310 ) -> Box<dyn Iterator<Item = &HgPathBuf> + '_> {
309 311 todo!()
310 312 }
311 313
312 314 fn set_non_normal_other_parent_entries(&mut self, _force: bool) {
313 315 todo!()
314 316 }
315 317
316 318 fn iter_non_normal_paths(
317 319 &mut self,
318 320 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
319 321 todo!()
320 322 }
321 323
322 324 fn iter_non_normal_paths_panic(
323 325 &self,
324 326 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
325 327 todo!()
326 328 }
327 329
328 330 fn iter_other_parent_paths(
329 331 &mut self,
330 332 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
331 333 todo!()
332 334 }
333 335
334 336 fn has_tracked_dir(
335 337 &mut self,
336 338 _directory: &HgPath,
337 339 ) -> Result<bool, DirstateMapError> {
338 340 todo!()
339 341 }
340 342
341 343 fn has_dir(
342 344 &mut self,
343 345 _directory: &HgPath,
344 346 ) -> Result<bool, DirstateMapError> {
345 347 todo!()
346 348 }
347 349
348 350 fn parents(
349 351 &mut self,
350 352 file_contents: &[u8],
351 353 ) -> Result<&DirstateParents, DirstateError> {
352 354 if self.parents.is_none() {
353 355 let parents = if !file_contents.is_empty() {
354 356 parse_dirstate_parents(file_contents)?.clone()
355 357 } else {
356 358 DirstateParents {
357 359 p1: NULL_NODE,
358 360 p2: NULL_NODE,
359 361 }
360 362 };
361 363 self.parents = Some(parents);
362 364 }
363 365 Ok(self.parents.as_ref().unwrap())
364 366 }
365 367
366 368 fn set_parents(&mut self, parents: &DirstateParents) {
367 369 self.parents = Some(parents.clone());
368 370 self.dirty_parents = true;
369 371 }
370 372
371 373 fn read<'a>(
372 374 &mut self,
373 375 file_contents: &'a [u8],
374 376 ) -> Result<Option<&'a DirstateParents>, DirstateError> {
375 377 if file_contents.is_empty() {
376 378 return Ok(None);
377 379 }
378 380
379 381 let parents = parse_dirstate_entries(
380 382 file_contents,
381 383 |path, entry, copy_source| {
382 384 self.add_file_node(
383 385 path,
384 386 *entry,
385 387 Some(copy_source.map(HgPath::to_owned)),
386 388 )
387 389 },
388 390 )?;
389 391
390 392 if !self.dirty_parents {
391 393 self.set_parents(parents);
392 394 }
393 395
394 396 Ok(Some(parents))
395 397 }
396 398
397 399 fn pack(
398 400 &mut self,
399 401 parents: DirstateParents,
400 402 now: Timestamp,
401 403 ) -> Result<Vec<u8>, DirstateError> {
402 404 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
403 405 // reallocations
404 406 let mut size = parents.as_bytes().len();
405 407 for (path, node) in self.iter_nodes() {
406 408 if node.entry.is_some() {
407 409 size += packed_entry_size(
408 410 path.full_path(),
409 411 node.copy_source.as_ref(),
410 412 )
411 413 }
412 414 }
413 415
414 416 let mut packed = Vec::with_capacity(size);
415 417 packed.extend(parents.as_bytes());
416 418
417 419 let now: i32 = now.0.try_into().expect("time overflow");
418 420 for (path, opt_entry, copy_source) in self.iter_node_data_mut() {
419 421 if let Some(entry) = opt_entry {
420 422 clear_ambiguous_mtime(entry, now);
421 423 pack_entry(
422 424 path.full_path(),
423 425 entry,
424 426 copy_source.as_ref(),
425 427 &mut packed,
426 428 );
427 429 }
428 430 }
429 431 self.dirty_parents = false;
430 432 Ok(packed)
431 433 }
432 434
433 435 fn build_file_fold_map(&mut self) -> &FastHashMap<HgPathBuf, HgPathBuf> {
434 436 todo!()
435 437 }
436 438
437 439 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
438 440 todo!()
439 441 }
440 442
441 443 fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
442 444 todo!()
443 445 }
444 446
445 447 fn status<'a>(
446 448 &'a self,
447 449 _matcher: &'a (dyn Matcher + Sync),
448 450 _root_dir: PathBuf,
449 451 _ignore_files: Vec<PathBuf>,
450 452 _options: StatusOptions,
451 453 ) -> Result<
452 454 (
453 455 (Vec<HgPathCow<'a>>, DirstateStatus<'a>),
454 456 Vec<PatternFileWarning>,
455 457 ),
456 458 StatusError,
457 459 > {
458 460 todo!()
459 461 }
460 462
461 463 fn copy_map_len(&self) -> usize {
462 464 self.nodes_with_copy_source_count
463 465 }
464 466
465 467 fn copy_map_iter(&self) -> CopyMapIter<'_> {
466 468 Box::new(self.iter_nodes().filter_map(|(path, node)| {
467 469 node.copy_source
468 470 .as_ref()
469 471 .map(|copy_source| (path.full_path(), copy_source))
470 472 }))
471 473 }
472 474
473 475 fn copy_map_contains_key(&self, key: &HgPath) -> bool {
474 476 if let Some(node) = self.get_node(key) {
475 477 node.copy_source.is_some()
476 478 } else {
477 479 false
478 480 }
479 481 }
480 482
481 483 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPathBuf> {
482 484 self.get_node(key)?.copy_source.as_ref()
483 485 }
484 486
485 487 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf> {
486 488 let count = &mut self.nodes_with_copy_source_count;
487 489 Self::get_node_mut(&mut self.root, key).and_then(|node| {
488 490 if node.copy_source.is_some() {
489 491 *count -= 1
490 492 }
491 493 node.copy_source.take()
492 494 })
493 495 }
494 496
495 497 fn copy_map_insert(
496 498 &mut self,
497 499 key: HgPathBuf,
498 500 value: HgPathBuf,
499 501 ) -> Option<HgPathBuf> {
500 502 let node = Self::get_or_insert_node(&mut self.root, &key);
501 503 if node.copy_source.is_none() {
502 504 self.nodes_with_copy_source_count += 1
503 505 }
504 506 node.copy_source.replace(value)
505 507 }
506 508
507 509 fn len(&self) -> usize {
508 510 self.nodes_with_entry_count
509 511 }
510 512
511 513 fn contains_key(&self, key: &HgPath) -> bool {
512 514 self.get(key).is_some()
513 515 }
514 516
515 517 fn get(&self, key: &HgPath) -> Option<&DirstateEntry> {
516 518 self.get_node(key)?.entry.as_ref()
517 519 }
518 520
519 521 fn iter(&self) -> StateMapIter<'_> {
520 522 Box::new(self.iter_nodes().filter_map(|(path, node)| {
521 523 node.entry.as_ref().map(|entry| (path.full_path(), entry))
522 524 }))
523 525 }
524 526 }
General Comments 0
You need to be logged in to leave comments. Login now