##// END OF EJS Templates
dirstate-tree: Add has_dir and has_tracked_dir...
Simon Sapin -
r47876:52906934 default
parent child Browse files
Show More
@@ -1,97 +1,107 b''
1 1 // dirstate module
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 8 use crate::errors::HgError;
9 9 use crate::revlog::Node;
10 10 use crate::{utils::hg_path::HgPathBuf, FastHashMap};
11 11 use bytes_cast::{unaligned, BytesCast};
12 12 use std::convert::TryFrom;
13 13
14 14 pub mod dirs_multiset;
15 15 pub mod dirstate_map;
16 16 pub mod parsers;
17 17 pub mod status;
18 18
19 19 #[derive(Debug, PartialEq, Clone, BytesCast)]
20 20 #[repr(C)]
21 21 pub struct DirstateParents {
22 22 pub p1: Node,
23 23 pub p2: Node,
24 24 }
25 25
26 26 /// The C implementation uses all signed types. This will be an issue
27 27 /// either when 4GB+ source files are commonplace or in 2038, whichever
28 28 /// comes first.
29 29 #[derive(Debug, PartialEq, Copy, Clone)]
30 30 pub struct DirstateEntry {
31 31 pub state: EntryState,
32 32 pub mode: i32,
33 33 pub mtime: i32,
34 34 pub size: i32,
35 35 }
36 36
37 37 #[derive(BytesCast)]
38 38 #[repr(C)]
39 39 struct RawEntry {
40 40 state: u8,
41 41 mode: unaligned::I32Be,
42 42 size: unaligned::I32Be,
43 43 mtime: unaligned::I32Be,
44 44 length: unaligned::I32Be,
45 45 }
46 46
47 47 /// A `DirstateEntry` with a size of `-2` means that it was merged from the
48 48 /// other parent. This allows revert to pick the right status back during a
49 49 /// merge.
50 50 pub const SIZE_FROM_OTHER_PARENT: i32 = -2;
51 51
52 52 pub type StateMap = FastHashMap<HgPathBuf, DirstateEntry>;
53 53 pub type StateMapIter<'a> =
54 54 Box<dyn Iterator<Item = (&'a HgPathBuf, &'a DirstateEntry)> + Send + 'a>;
55 55
56 56 pub type CopyMap = FastHashMap<HgPathBuf, HgPathBuf>;
57 57 pub type CopyMapIter<'a> =
58 58 Box<dyn Iterator<Item = (&'a HgPathBuf, &'a HgPathBuf)> + Send + 'a>;
59 59
60 60 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
61 61 pub enum EntryState {
62 62 Normal,
63 63 Added,
64 64 Removed,
65 65 Merged,
66 66 Unknown,
67 67 }
68 68
69 impl EntryState {
70 pub fn is_tracked(self) -> bool {
71 use EntryState::*;
72 match self {
73 Normal | Added | Merged => true,
74 Removed | Unknown => false,
75 }
76 }
77 }
78
69 79 impl TryFrom<u8> for EntryState {
70 80 type Error = HgError;
71 81
72 82 fn try_from(value: u8) -> Result<Self, Self::Error> {
73 83 match value {
74 84 b'n' => Ok(EntryState::Normal),
75 85 b'a' => Ok(EntryState::Added),
76 86 b'r' => Ok(EntryState::Removed),
77 87 b'm' => Ok(EntryState::Merged),
78 88 b'?' => Ok(EntryState::Unknown),
79 89 _ => Err(HgError::CorruptedRepository(format!(
80 90 "Incorrect dirstate entry state {}",
81 91 value
82 92 ))),
83 93 }
84 94 }
85 95 }
86 96
87 97 impl Into<u8> for EntryState {
88 98 fn into(self) -> u8 {
89 99 match self {
90 100 EntryState::Normal => b'n',
91 101 EntryState::Added => b'a',
92 102 EntryState::Removed => b'r',
93 103 EntryState::Merged => b'm',
94 104 EntryState::Unknown => b'?',
95 105 }
96 106 }
97 107 }
@@ -1,526 +1,609 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 /// Represents a file or a directory
49 50 #[derive(Default)]
50 51 struct Node {
52 /// `None` for directories
51 53 entry: Option<DirstateEntry>,
54
52 55 copy_source: Option<HgPathBuf>,
56
53 57 children: ChildNodes,
58
59 /// How many (non-inclusive) descendants of this node are tracked files
60 tracked_descendants_count: usize,
61 }
62
63 impl Node {
64 /// Whether this node has a `DirstateEntry` with `.state.is_tracked()`
65 fn is_tracked_file(&self) -> bool {
66 if let Some(entry) = &self.entry {
67 entry.state.is_tracked()
68 } else {
69 false
70 }
71 }
54 72 }
55 73
56 74 /// `(full_path, entry, copy_source)`
57 75 type NodeDataMut<'a> = (
58 76 &'a WithBasename<HgPathBuf>,
59 77 &'a mut Option<DirstateEntry>,
60 78 &'a mut Option<HgPathBuf>,
61 79 );
62 80
63 81 impl DirstateMap {
64 82 pub fn new() -> Self {
65 83 Self {
66 84 parents: None,
67 85 dirty_parents: false,
68 86 root: ChildNodes::new(),
69 87 nodes_with_entry_count: 0,
70 88 nodes_with_copy_source_count: 0,
71 89 }
72 90 }
73 91
74 92 fn get_node(&self, path: &HgPath) -> Option<&Node> {
75 93 let mut children = &self.root;
76 94 let mut components = path.components();
77 95 let mut component =
78 96 components.next().expect("expected at least one components");
79 97 loop {
80 98 let child = children.get(component)?;
81 99 if let Some(next_component) = components.next() {
82 100 component = next_component;
83 101 children = &child.children;
84 102 } else {
85 103 return Some(child);
86 104 }
87 105 }
88 106 }
89 107
108 /// Returns a mutable reference to the node at `path` if it exists
109 ///
90 110 /// This takes `root` instead of `&mut self` so that callers can mutate
91 111 /// other fields while the returned borrow is still valid
92 112 fn get_node_mut<'tree>(
93 113 root: &'tree mut ChildNodes,
94 114 path: &HgPath,
95 115 ) -> Option<&'tree mut Node> {
116 Self::each_and_get(root, path, |_| {})
117 }
118
119 /// Call `each` for each ancestor node of the one at `path` (not including
120 /// that node itself), starting from nearest the root.
121 ///
122 /// Panics (possibly after some calls to `each`) if there is no node at
123 /// `path`.
124 fn for_each_ancestor_node<'tree>(
125 &mut self,
126 path: &HgPath,
127 each: impl FnMut(&mut Node),
128 ) {
129 let parent = path.parent();
130 if !parent.is_empty() {
131 Self::each_and_get(&mut self.root, parent, each)
132 .expect("missing dirstate node");
133 }
134 }
135
136 /// Common implementation detail of `get_node_mut` and
137 /// `for_each_ancestor_node`
138 fn each_and_get<'tree>(
139 root: &'tree mut ChildNodes,
140 path: &HgPath,
141 mut each: impl FnMut(&mut Node),
142 ) -> Option<&'tree mut Node> {
96 143 let mut children = root;
97 144 let mut components = path.components();
98 145 let mut component =
99 146 components.next().expect("expected at least one components");
100 147 loop {
101 148 let child = children.get_mut(component)?;
149 each(child);
102 150 if let Some(next_component) = components.next() {
103 151 component = next_component;
104 152 children = &mut child.children;
105 153 } else {
106 154 return Some(child);
107 155 }
108 156 }
109 157 }
110 158
111 159 fn get_or_insert_node<'tree>(
112 160 root: &'tree mut ChildNodes,
113 161 path: &HgPath,
114 162 ) -> &'tree mut Node {
115 163 let mut child_nodes = root;
116 164 let mut inclusive_ancestor_paths =
117 165 WithBasename::inclusive_ancestors_of(path);
118 166 let mut ancestor_path = inclusive_ancestor_paths
119 167 .next()
120 168 .expect("expected at least one inclusive ancestor");
121 169 loop {
122 170 // TODO: can we avoid double lookup in all cases without allocating
123 171 // an owned key in cases where the map already contains that key?
124 172 let child_node =
125 173 if child_nodes.contains_key(ancestor_path.base_name()) {
126 174 child_nodes.get_mut(ancestor_path.base_name()).unwrap()
127 175 } else {
128 176 // This is always a vacant entry, using `.entry()` lets us
129 177 // return a `&mut Node` of the newly-inserted node without
130 178 // yet another lookup. `BTreeMap::insert` doesn’t do this.
131 179 child_nodes.entry(ancestor_path.to_owned()).or_default()
132 180 };
133 181 if let Some(next) = inclusive_ancestor_paths.next() {
134 182 ancestor_path = next;
135 183 child_nodes = &mut child_node.children;
136 184 } else {
137 185 return child_node;
138 186 }
139 187 }
140 188 }
141 189
142 190 /// The meaning of `new_copy_source` is:
143 191 ///
144 192 /// * `Some(Some(x))`: set `Node::copy_source` to `Some(x)`
145 193 /// * `Some(None)`: set `Node::copy_source` to `None`
146 194 /// * `None`: leave `Node::copy_source` unchanged
147 195 fn add_file_node(
148 196 &mut self,
149 197 path: &HgPath,
150 198 new_entry: DirstateEntry,
151 199 new_copy_source: Option<Option<HgPathBuf>>,
152 200 ) {
153 201 let node = Self::get_or_insert_node(&mut self.root, path);
154 202 if node.entry.is_none() {
155 203 self.nodes_with_entry_count += 1
156 204 }
157 205 if let Some(source) = &new_copy_source {
158 206 if node.copy_source.is_none() && source.is_some() {
159 207 self.nodes_with_copy_source_count += 1
160 208 }
161 209 if node.copy_source.is_some() && source.is_none() {
162 210 self.nodes_with_copy_source_count -= 1
163 211 }
164 212 }
213 let tracked_count_increment =
214 match (node.is_tracked_file(), new_entry.state.is_tracked()) {
215 (false, true) => 1,
216 (true, false) => -1,
217 _ => 0,
218 };
219
165 220 node.entry = Some(new_entry);
166 221 if let Some(source) = new_copy_source {
167 222 node.copy_source = source
168 223 }
224 // Borrow of `self.root` through `node` ends here
225
226 match tracked_count_increment {
227 1 => self.for_each_ancestor_node(path, |node| {
228 node.tracked_descendants_count += 1
229 }),
230 // We can’t use `+= -1` because the counter is unsigned
231 -1 => self.for_each_ancestor_node(path, |node| {
232 node.tracked_descendants_count -= 1
233 }),
234 _ => {}
235 }
169 236 }
170 237
171 238 fn iter_nodes<'a>(
172 239 &'a self,
173 240 ) -> impl Iterator<Item = (&'a WithBasename<HgPathBuf>, &'a Node)> + 'a
174 241 {
175 242 // Depth first tree traversal.
176 243 //
177 244 // If we could afford internal iteration and recursion,
178 245 // this would look like:
179 246 //
180 247 // ```
181 248 // fn traverse_children(
182 249 // children: &ChildNodes,
183 250 // each: &mut impl FnMut(&Node),
184 251 // ) {
185 252 // for child in children.values() {
186 253 // traverse_children(&child.children, each);
187 254 // each(child);
188 255 // }
189 256 // }
190 257 // ```
191 258 //
192 259 // However we want an external iterator and therefore can’t use the
193 260 // call stack. Use an explicit stack instead:
194 261 let mut stack = Vec::new();
195 262 let mut iter = self.root.iter();
196 263 std::iter::from_fn(move || {
197 264 while let Some((key, child_node)) = iter.next() {
198 265 // Pseudo-recursion
199 266 let new_iter = child_node.children.iter();
200 267 let old_iter = std::mem::replace(&mut iter, new_iter);
201 268 stack.push((key, child_node, old_iter));
202 269 }
203 270 // Found the end of a `children.iter()` iterator.
204 271 if let Some((key, child_node, next_iter)) = stack.pop() {
205 272 // "Return" from pseudo-recursion by restoring state from the
206 273 // explicit stack
207 274 iter = next_iter;
208 275
209 276 Some((key, child_node))
210 277 } else {
211 278 // Reached the bottom of the stack, we’re done
212 279 None
213 280 }
214 281 })
215 282 }
216 283
217 284 /// Mutable iterator for the `(entry, copy source)` of each node.
218 285 ///
219 286 /// It would not be safe to yield mutable references to nodes themeselves
220 287 /// with `-> impl Iterator<Item = &mut Node>` since child nodes are
221 288 /// reachable from their ancestor nodes, potentially creating multiple
222 289 /// `&mut` references to a given node.
223 290 fn iter_node_data_mut<'a>(
224 291 &'a mut self,
225 292 ) -> impl Iterator<Item = NodeDataMut<'a>> + 'a {
226 293 // Explict stack for pseudo-recursion, see `iter_nodes` above.
227 294 let mut stack = Vec::new();
228 295 let mut iter = self.root.iter_mut();
229 296 std::iter::from_fn(move || {
230 297 while let Some((key, child_node)) = iter.next() {
231 298 // Pseudo-recursion
232 299 let data =
233 300 (key, &mut child_node.entry, &mut child_node.copy_source);
234 301 let new_iter = child_node.children.iter_mut();
235 302 let old_iter = std::mem::replace(&mut iter, new_iter);
236 303 stack.push((data, old_iter));
237 304 }
238 305 // Found the end of a `children.values_mut()` iterator.
239 306 if let Some((data, next_iter)) = stack.pop() {
240 307 // "Return" from pseudo-recursion by restoring state from the
241 308 // explicit stack
242 309 iter = next_iter;
243 310
244 311 Some(data)
245 312 } else {
246 313 // Reached the bottom of the stack, we’re done
247 314 None
248 315 }
249 316 })
250 317 }
251 318 }
252 319
253 320 impl super::dispatch::DirstateMapMethods for DirstateMap {
254 321 fn clear(&mut self) {
255 322 self.set_parents(&DirstateParents {
256 323 p1: NULL_NODE,
257 324 p2: NULL_NODE,
258 325 });
259 326 self.root.clear();
260 327 self.nodes_with_entry_count = 0;
261 328 self.nodes_with_copy_source_count = 0;
262 329 }
263 330
264 331 fn add_file(
265 332 &mut self,
266 333 _filename: &HgPath,
267 334 _old_state: EntryState,
268 335 _entry: DirstateEntry,
269 336 ) -> Result<(), DirstateMapError> {
270 337 todo!()
271 338 }
272 339
273 340 fn remove_file(
274 341 &mut self,
275 342 _filename: &HgPath,
276 343 _old_state: EntryState,
277 344 _size: i32,
278 345 ) -> Result<(), DirstateMapError> {
279 346 todo!()
280 347 }
281 348
282 349 fn drop_file(
283 350 &mut self,
284 351 _filename: &HgPath,
285 352 _old_state: EntryState,
286 353 ) -> Result<bool, DirstateMapError> {
287 354 todo!()
288 355 }
289 356
290 357 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32) {
291 358 for filename in filenames {
292 359 if let Some(node) = Self::get_node_mut(&mut self.root, &filename) {
293 360 if let Some(entry) = node.entry.as_mut() {
294 361 clear_ambiguous_mtime(entry, now);
295 362 }
296 363 }
297 364 }
298 365 }
299 366
300 367 fn non_normal_entries_contains(&mut self, _key: &HgPath) -> bool {
301 368 todo!()
302 369 }
303 370
304 371 fn non_normal_entries_remove(&mut self, _key: &HgPath) -> bool {
305 372 todo!()
306 373 }
307 374
308 375 fn non_normal_or_other_parent_paths(
309 376 &mut self,
310 377 ) -> Box<dyn Iterator<Item = &HgPathBuf> + '_> {
311 378 todo!()
312 379 }
313 380
314 381 fn set_non_normal_other_parent_entries(&mut self, _force: bool) {
315 382 todo!()
316 383 }
317 384
318 385 fn iter_non_normal_paths(
319 386 &mut self,
320 387 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
321 388 todo!()
322 389 }
323 390
324 391 fn iter_non_normal_paths_panic(
325 392 &self,
326 393 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
327 394 todo!()
328 395 }
329 396
330 397 fn iter_other_parent_paths(
331 398 &mut self,
332 399 ) -> Box<dyn Iterator<Item = &HgPathBuf> + Send + '_> {
333 400 todo!()
334 401 }
335 402
336 403 fn has_tracked_dir(
337 404 &mut self,
338 _directory: &HgPath,
405 directory: &HgPath,
339 406 ) -> Result<bool, DirstateMapError> {
340 todo!()
407 if let Some(node) = self.get_node(directory) {
408 // A node without a `DirstateEntry` was created to hold child
409 // nodes, and is therefore a directory.
410 Ok(node.entry.is_none() && node.tracked_descendants_count > 0)
411 } else {
412 Ok(false)
413 }
341 414 }
342 415
343 416 fn has_dir(
344 417 &mut self,
345 _directory: &HgPath,
418 directory: &HgPath,
346 419 ) -> Result<bool, DirstateMapError> {
347 todo!()
420 if let Some(node) = self.get_node(directory) {
421 // A node without a `DirstateEntry` was created to hold child
422 // nodes, and is therefore a directory.
423 Ok(node.entry.is_none())
424 } else {
425 Ok(false)
426 }
348 427 }
349 428
350 429 fn parents(
351 430 &mut self,
352 431 file_contents: &[u8],
353 432 ) -> Result<&DirstateParents, DirstateError> {
354 433 if self.parents.is_none() {
355 434 let parents = if !file_contents.is_empty() {
356 435 parse_dirstate_parents(file_contents)?.clone()
357 436 } else {
358 437 DirstateParents {
359 438 p1: NULL_NODE,
360 439 p2: NULL_NODE,
361 440 }
362 441 };
363 442 self.parents = Some(parents);
364 443 }
365 444 Ok(self.parents.as_ref().unwrap())
366 445 }
367 446
368 447 fn set_parents(&mut self, parents: &DirstateParents) {
369 448 self.parents = Some(parents.clone());
370 449 self.dirty_parents = true;
371 450 }
372 451
373 452 fn read<'a>(
374 453 &mut self,
375 454 file_contents: &'a [u8],
376 455 ) -> Result<Option<&'a DirstateParents>, DirstateError> {
377 456 if file_contents.is_empty() {
378 457 return Ok(None);
379 458 }
380 459
381 460 let parents = parse_dirstate_entries(
382 461 file_contents,
383 462 |path, entry, copy_source| {
384 463 self.add_file_node(
385 464 path,
386 465 *entry,
387 466 Some(copy_source.map(HgPath::to_owned)),
388 467 )
389 468 },
390 469 )?;
391 470
392 471 if !self.dirty_parents {
393 472 self.set_parents(parents);
394 473 }
395 474
396 475 Ok(Some(parents))
397 476 }
398 477
399 478 fn pack(
400 479 &mut self,
401 480 parents: DirstateParents,
402 481 now: Timestamp,
403 482 ) -> Result<Vec<u8>, DirstateError> {
404 483 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
405 484 // reallocations
406 485 let mut size = parents.as_bytes().len();
407 486 for (path, node) in self.iter_nodes() {
408 487 if node.entry.is_some() {
409 488 size += packed_entry_size(
410 489 path.full_path(),
411 490 node.copy_source.as_ref(),
412 491 )
413 492 }
414 493 }
415 494
416 495 let mut packed = Vec::with_capacity(size);
417 496 packed.extend(parents.as_bytes());
418 497
419 498 let now: i32 = now.0.try_into().expect("time overflow");
420 499 for (path, opt_entry, copy_source) in self.iter_node_data_mut() {
421 500 if let Some(entry) = opt_entry {
422 501 clear_ambiguous_mtime(entry, now);
423 502 pack_entry(
424 503 path.full_path(),
425 504 entry,
426 505 copy_source.as_ref(),
427 506 &mut packed,
428 507 );
429 508 }
430 509 }
431 510 self.dirty_parents = false;
432 511 Ok(packed)
433 512 }
434 513
435 514 fn build_file_fold_map(&mut self) -> &FastHashMap<HgPathBuf, HgPathBuf> {
436 515 todo!()
437 516 }
438 517
439 518 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
440 todo!()
519 // Do nothing, this `DirstateMap` does not a separate `all_dirs` that
520 // needs to be recomputed
521 Ok(())
441 522 }
442 523
443 524 fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
444 todo!()
525 // Do nothing, this `DirstateMap` does not a separate `dirs` that needs
526 // to be recomputed
527 Ok(())
445 528 }
446 529
447 530 fn status<'a>(
448 531 &'a self,
449 532 _matcher: &'a (dyn Matcher + Sync),
450 533 _root_dir: PathBuf,
451 534 _ignore_files: Vec<PathBuf>,
452 535 _options: StatusOptions,
453 536 ) -> Result<
454 537 (
455 538 (Vec<HgPathCow<'a>>, DirstateStatus<'a>),
456 539 Vec<PatternFileWarning>,
457 540 ),
458 541 StatusError,
459 542 > {
460 543 todo!()
461 544 }
462 545
463 546 fn copy_map_len(&self) -> usize {
464 547 self.nodes_with_copy_source_count
465 548 }
466 549
467 550 fn copy_map_iter(&self) -> CopyMapIter<'_> {
468 551 Box::new(self.iter_nodes().filter_map(|(path, node)| {
469 552 node.copy_source
470 553 .as_ref()
471 554 .map(|copy_source| (path.full_path(), copy_source))
472 555 }))
473 556 }
474 557
475 558 fn copy_map_contains_key(&self, key: &HgPath) -> bool {
476 559 if let Some(node) = self.get_node(key) {
477 560 node.copy_source.is_some()
478 561 } else {
479 562 false
480 563 }
481 564 }
482 565
483 566 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPathBuf> {
484 567 self.get_node(key)?.copy_source.as_ref()
485 568 }
486 569
487 570 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf> {
488 571 let count = &mut self.nodes_with_copy_source_count;
489 572 Self::get_node_mut(&mut self.root, key).and_then(|node| {
490 573 if node.copy_source.is_some() {
491 574 *count -= 1
492 575 }
493 576 node.copy_source.take()
494 577 })
495 578 }
496 579
497 580 fn copy_map_insert(
498 581 &mut self,
499 582 key: HgPathBuf,
500 583 value: HgPathBuf,
501 584 ) -> Option<HgPathBuf> {
502 585 let node = Self::get_or_insert_node(&mut self.root, &key);
503 586 if node.copy_source.is_none() {
504 587 self.nodes_with_copy_source_count += 1
505 588 }
506 589 node.copy_source.replace(value)
507 590 }
508 591
509 592 fn len(&self) -> usize {
510 593 self.nodes_with_entry_count
511 594 }
512 595
513 596 fn contains_key(&self, key: &HgPath) -> bool {
514 597 self.get(key).is_some()
515 598 }
516 599
517 600 fn get(&self, key: &HgPath) -> Option<&DirstateEntry> {
518 601 self.get_node(key)?.entry.as_ref()
519 602 }
520 603
521 604 fn iter(&self) -> StateMapIter<'_> {
522 605 Box::new(self.iter_nodes().filter_map(|(path, node)| {
523 606 node.entry.as_ref().map(|entry| (path.full_path(), entry))
524 607 }))
525 608 }
526 609 }
General Comments 0
You need to be logged in to leave comments. Login now