##// END OF EJS Templates
dirstate-tree: Remove DirstateMap::iter_node_data_mut...
Simon Sapin -
r48121:73f23e76 default
parent child Browse files
Show More
@@ -5,7 +5,6 b''
5 // This software may be used and distributed according to the terms of the
5 // This software may be used and distributed according to the terms of the
6 // GNU General Public License version 2 or any later version.
6 // GNU General Public License version 2 or any later version.
7
7
8 use crate::dirstate::parsers::clear_ambiguous_mtime;
9 use crate::dirstate::parsers::Timestamp;
8 use crate::dirstate::parsers::Timestamp;
10 use crate::{
9 use crate::{
11 dirstate::EntryState,
10 dirstate::EntryState,
@@ -166,7 +165,7 b' impl DirstateMap {'
166 ) {
165 ) {
167 for filename in filenames {
166 for filename in filenames {
168 if let Some(entry) = self.state_map.get_mut(&filename) {
167 if let Some(entry) = self.state_map.get_mut(&filename) {
169 if clear_ambiguous_mtime(entry, now) {
168 if entry.clear_ambiguous_mtime(now) {
170 self.get_non_normal_other_parent_entries()
169 self.get_non_normal_other_parent_entries()
171 .0
170 .0
172 .insert(filename.to_owned());
171 .insert(filename.to_owned());
@@ -126,25 +126,31 b' pub fn pack_entry('
126 /// Seconds since the Unix epoch
126 /// Seconds since the Unix epoch
127 pub struct Timestamp(pub u64);
127 pub struct Timestamp(pub u64);
128
128
129 pub fn clear_ambiguous_mtime(
129 impl DirstateEntry {
130 entry: &mut DirstateEntry,
130 pub fn mtime_is_ambiguous(&self, now: i32) -> bool {
131 mtime_now: i32,
131 self.state == EntryState::Normal && self.mtime == now
132 ) -> bool {
133 let ambiguous =
134 entry.state == EntryState::Normal && entry.mtime == mtime_now;
135 if ambiguous {
136 // The file was last modified "simultaneously" with the current
137 // write to dirstate (i.e. within the same second for file-
138 // systems with a granularity of 1 sec). This commonly happens
139 // for at least a couple of files on 'update'.
140 // The user could change the file without changing its size
141 // within the same second. Invalidate the file's mtime in
142 // dirstate, forcing future 'status' calls to compare the
143 // contents of the file if the size is the same. This prevents
144 // mistakenly treating such files as clean.
145 entry.mtime = -1;
146 }
132 }
147 ambiguous
133
134 pub fn clear_ambiguous_mtime(&mut self, now: i32) -> bool {
135 let ambiguous = self.mtime_is_ambiguous(now);
136 if ambiguous {
137 // The file was last modified "simultaneously" with the current
138 // write to dirstate (i.e. within the same second for file-
139 // systems with a granularity of 1 sec). This commonly happens
140 // for at least a couple of files on 'update'.
141 // The user could change the file without changing its size
142 // within the same second. Invalidate the file's mtime in
143 // dirstate, forcing future 'status' calls to compare the
144 // contents of the file if the size is the same. This prevents
145 // mistakenly treating such files as clean.
146 self.clear_mtime()
147 }
148 ambiguous
149 }
150
151 pub fn clear_mtime(&mut self) {
152 self.mtime = -1;
153 }
148 }
154 }
149
155
150 pub fn pack_dirstate(
156 pub fn pack_dirstate(
@@ -170,7 +176,7 b' pub fn pack_dirstate('
170 packed.extend(parents.p2.as_bytes());
176 packed.extend(parents.p2.as_bytes());
171
177
172 for (filename, entry) in state_map.iter_mut() {
178 for (filename, entry) in state_map.iter_mut() {
173 clear_ambiguous_mtime(entry, now);
179 entry.clear_ambiguous_mtime(now);
174 pack_entry(
180 pack_entry(
175 filename,
181 filename,
176 entry,
182 entry,
@@ -6,7 +6,6 b' use std::path::PathBuf;'
6
6
7 use super::on_disk;
7 use super::on_disk;
8 use super::path_with_basename::WithBasename;
8 use super::path_with_basename::WithBasename;
9 use crate::dirstate::parsers::clear_ambiguous_mtime;
10 use crate::dirstate::parsers::pack_entry;
9 use crate::dirstate::parsers::pack_entry;
11 use crate::dirstate::parsers::packed_entry_size;
10 use crate::dirstate::parsers::packed_entry_size;
12 use crate::dirstate::parsers::parse_dirstate_entries;
11 use crate::dirstate::parsers::parse_dirstate_entries;
@@ -79,13 +78,6 b" impl<'on_disk> Node<'on_disk> {"
79 }
78 }
80 }
79 }
81
80
82 /// `(full_path, entry, copy_source)`
83 type NodeDataMut<'tree, 'on_disk> = (
84 &'tree HgPath,
85 &'tree mut Option<DirstateEntry>,
86 &'tree mut Option<Cow<'on_disk, HgPath>>,
87 );
88
89 impl<'on_disk> DirstateMap<'on_disk> {
81 impl<'on_disk> DirstateMap<'on_disk> {
90 pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self {
82 pub(super) fn empty(on_disk: &'on_disk [u8]) -> Self {
91 Self {
83 Self {
@@ -252,7 +244,7 b" impl<'on_disk> DirstateMap<'on_disk> {"
252
244
253 fn iter_nodes<'a>(
245 fn iter_nodes<'a>(
254 &'a self,
246 &'a self,
255 ) -> impl Iterator<Item = (&'a HgPath, &'a Node)> + 'a {
247 ) -> impl Iterator<Item = (&'a Cow<'on_disk, HgPath>, &'a Node)> + 'a {
256 // Depth first tree traversal.
248 // Depth first tree traversal.
257 //
249 //
258 // If we could afford internal iteration and recursion,
250 // If we could afford internal iteration and recursion,
@@ -279,7 +271,7 b" impl<'on_disk> DirstateMap<'on_disk> {"
279 // Pseudo-recursion
271 // Pseudo-recursion
280 let new_iter = child_node.children.iter();
272 let new_iter = child_node.children.iter();
281 let old_iter = std::mem::replace(&mut iter, new_iter);
273 let old_iter = std::mem::replace(&mut iter, new_iter);
282 let key = &**key.full_path();
274 let key = key.full_path();
283 stack.push((key, child_node, old_iter));
275 stack.push((key, child_node, old_iter));
284 }
276 }
285 // Found the end of a `children.iter()` iterator.
277 // Found the end of a `children.iter()` iterator.
@@ -296,42 +288,16 b" impl<'on_disk> DirstateMap<'on_disk> {"
296 })
288 })
297 }
289 }
298
290
299 /// Mutable iterator for the `(entry, copy source)` of each node.
291 fn clear_known_ambiguous_mtimes(&mut self, paths: &[impl AsRef<HgPath>]) {
300 ///
292 for path in paths {
301 /// It would not be safe to yield mutable references to nodes themeselves
293 if let Some(node) =
302 /// with `-> impl Iterator<Item = &mut Node>` since child nodes are
294 Self::get_node_mut(&mut self.root, path.as_ref())
303 /// reachable from their ancestor nodes, potentially creating multiple
295 {
304 /// `&mut` references to a given node.
296 if let Some(entry) = node.entry.as_mut() {
305 fn iter_node_data_mut<'tree>(
297 entry.clear_mtime();
306 &'tree mut self,
298 }
307 ) -> impl Iterator<Item = NodeDataMut<'tree, 'on_disk>> + 'tree {
308 // Explict stack for pseudo-recursion, see `iter_nodes` above.
309 let mut stack = Vec::new();
310 let mut iter = self.root.iter_mut();
311 std::iter::from_fn(move || {
312 while let Some((key, child_node)) = iter.next() {
313 // Pseudo-recursion
314 let data = (
315 &**key.full_path(),
316 &mut child_node.entry,
317 &mut child_node.copy_source,
318 );
319 let new_iter = child_node.children.iter_mut();
320 let old_iter = std::mem::replace(&mut iter, new_iter);
321 stack.push((data, old_iter));
322 }
299 }
323 // Found the end of a `children.values_mut()` iterator.
300 }
324 if let Some((data, next_iter)) = stack.pop() {
325 // "Return" from pseudo-recursion by restoring state from the
326 // explicit stack
327 iter = next_iter;
328
329 Some(data)
330 } else {
331 // Reached the bottom of the stack, we’re done
332 None
333 }
334 })
335 }
301 }
336 }
302 }
337
303
@@ -427,7 +393,7 b" impl<'on_disk> super::dispatch::Dirstate"
427 for filename in filenames {
393 for filename in filenames {
428 if let Some(node) = Self::get_node_mut(&mut self.root, &filename) {
394 if let Some(node) = Self::get_node_mut(&mut self.root, &filename) {
429 if let Some(entry) = node.entry.as_mut() {
395 if let Some(entry) = node.entry.as_mut() {
430 clear_ambiguous_mtime(entry, now);
396 entry.clear_ambiguous_mtime(now);
431 }
397 }
432 }
398 }
433 }
399 }
@@ -453,7 +419,7 b" impl<'on_disk> super::dispatch::Dirstate"
453 .filter(|entry| {
419 .filter(|entry| {
454 entry.is_non_normal() || entry.is_from_other_parent()
420 entry.is_non_normal() || entry.is_from_other_parent()
455 })
421 })
456 .map(|_| path)
422 .map(|_| &**path)
457 }))
423 }))
458 }
424 }
459
425
@@ -475,7 +441,7 b" impl<'on_disk> super::dispatch::Dirstate"
475 node.entry
441 node.entry
476 .as_ref()
442 .as_ref()
477 .filter(|entry| entry.is_non_normal())
443 .filter(|entry| entry.is_non_normal())
478 .map(|_| path)
444 .map(|_| &**path)
479 }))
445 }))
480 }
446 }
481
447
@@ -486,7 +452,7 b" impl<'on_disk> super::dispatch::Dirstate"
486 node.entry
452 node.entry
487 .as_ref()
453 .as_ref()
488 .filter(|entry| entry.is_from_other_parent())
454 .filter(|entry| entry.is_from_other_parent())
489 .map(|_| path)
455 .map(|_| &**path)
490 }))
456 }))
491 }
457 }
492
458
@@ -522,29 +488,33 b" impl<'on_disk> super::dispatch::Dirstate"
522 parents: DirstateParents,
488 parents: DirstateParents,
523 now: Timestamp,
489 now: Timestamp,
524 ) -> Result<Vec<u8>, DirstateError> {
490 ) -> Result<Vec<u8>, DirstateError> {
491 let now: i32 = now.0.try_into().expect("time overflow");
492 let mut ambiguous_mtimes = Vec::new();
525 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
493 // Optizimation (to be measured?): pre-compute size to avoid `Vec`
526 // reallocations
494 // reallocations
527 let mut size = parents.as_bytes().len();
495 let mut size = parents.as_bytes().len();
528 for (path, node) in self.iter_nodes() {
496 for (path, node) in self.iter_nodes() {
529 if node.entry.is_some() {
497 if let Some(entry) = &node.entry {
530 size += packed_entry_size(
498 size += packed_entry_size(
531 path,
499 path,
532 node.copy_source.as_ref().map(|p| &**p),
500 node.copy_source.as_ref().map(|p| &**p),
533 )
501 );
502 if entry.mtime_is_ambiguous(now) {
503 ambiguous_mtimes.push(path.clone())
504 }
534 }
505 }
535 }
506 }
507 self.clear_known_ambiguous_mtimes(&ambiguous_mtimes);
536
508
537 let mut packed = Vec::with_capacity(size);
509 let mut packed = Vec::with_capacity(size);
538 packed.extend(parents.as_bytes());
510 packed.extend(parents.as_bytes());
539
511
540 let now: i32 = now.0.try_into().expect("time overflow");
512 for (path, node) in self.iter_nodes() {
541 for (path, opt_entry, copy_source) in self.iter_node_data_mut() {
513 if let Some(entry) = &node.entry {
542 if let Some(entry) = opt_entry {
543 clear_ambiguous_mtime(entry, now);
544 pack_entry(
514 pack_entry(
545 path,
515 path,
546 entry,
516 entry,
547 copy_source.as_ref().map(|p| &**p),
517 node.copy_source.as_ref().map(|p| &**p),
548 &mut packed,
518 &mut packed,
549 );
519 );
550 }
520 }
@@ -558,7 +528,21 b" impl<'on_disk> super::dispatch::Dirstate"
558 parents: DirstateParents,
528 parents: DirstateParents,
559 now: Timestamp,
529 now: Timestamp,
560 ) -> Result<Vec<u8>, DirstateError> {
530 ) -> Result<Vec<u8>, DirstateError> {
561 on_disk::write(self, parents, now)
531 // TODO: how do we want to handle this in 2038?
532 let now: i32 = now.0.try_into().expect("time overflow");
533 let mut paths = Vec::new();
534 for (path, node) in self.iter_nodes() {
535 if let Some(entry) = &node.entry {
536 if entry.mtime_is_ambiguous(now) {
537 paths.push(path.clone())
538 }
539 }
540 }
541 // Borrow of `self` ends here since we collect cloned paths
542
543 self.clear_known_ambiguous_mtimes(&paths);
544
545 on_disk::write(self, parents)
562 }
546 }
563
547
564 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
548 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
@@ -592,7 +576,7 b" impl<'on_disk> super::dispatch::Dirstate"
592 Box::new(self.iter_nodes().filter_map(|(path, node)| {
576 Box::new(self.iter_nodes().filter_map(|(path, node)| {
593 node.copy_source
577 node.copy_source
594 .as_ref()
578 .as_ref()
595 .map(|copy_source| (path, &**copy_source))
579 .map(|copy_source| (&**path, &**copy_source))
596 }))
580 }))
597 }
581 }
598
582
@@ -649,7 +633,7 b" impl<'on_disk> super::dispatch::Dirstate"
649
633
650 fn iter(&self) -> StateMapIter<'_> {
634 fn iter(&self) -> StateMapIter<'_> {
651 Box::new(self.iter_nodes().filter_map(|(path, node)| {
635 Box::new(self.iter_nodes().filter_map(|(path, node)| {
652 node.entry.as_ref().map(|entry| (path, entry))
636 node.entry.as_ref().map(|entry| (&**path, entry))
653 }))
637 }))
654 }
638 }
655 }
639 }
@@ -9,8 +9,6 b''
9 //! Nodes in turn contain slices to variable-size paths, and to their own child
9 //! Nodes in turn contain slices to variable-size paths, and to their own child
10 //! nodes (if any) for nested files and directories.
10 //! nodes (if any) for nested files and directories.
11
11
12 use crate::dirstate::parsers::clear_ambiguous_mtime;
13 use crate::dirstate::parsers::Timestamp;
14 use crate::dirstate_tree::dirstate_map::{self, DirstateMap};
12 use crate::dirstate_tree::dirstate_map::{self, DirstateMap};
15 use crate::dirstate_tree::path_with_basename::WithBasename;
13 use crate::dirstate_tree::path_with_basename::WithBasename;
16 use crate::errors::HgError;
14 use crate::errors::HgError;
@@ -230,11 +228,7 b' where'
230 pub(super) fn write(
228 pub(super) fn write(
231 dirstate_map: &mut DirstateMap,
229 dirstate_map: &mut DirstateMap,
232 parents: DirstateParents,
230 parents: DirstateParents,
233 now: Timestamp,
234 ) -> Result<Vec<u8>, DirstateError> {
231 ) -> Result<Vec<u8>, DirstateError> {
235 // TODO: how do we want to handle this in 2038?
236 let now: i32 = now.0.try_into().expect("time overflow");
237
238 let header_len = std::mem::size_of::<Header>();
232 let header_len = std::mem::size_of::<Header>();
239
233
240 // This ignores the space for paths, and for nodes without an entry.
234 // This ignores the space for paths, and for nodes without an entry.
@@ -248,7 +242,7 b' pub(super) fn write('
248 // actual offset for the root nodes.
242 // actual offset for the root nodes.
249 out.resize(header_len, 0_u8);
243 out.resize(header_len, 0_u8);
250
244
251 let root = write_nodes(&mut dirstate_map.root, now, &mut out)?;
245 let root = write_nodes(&mut dirstate_map.root, &mut out)?;
252
246
253 let header = Header {
247 let header = Header {
254 marker: *V2_FORMAT_MARKER,
248 marker: *V2_FORMAT_MARKER,
@@ -263,10 +257,8 b' pub(super) fn write('
263 Ok(out)
257 Ok(out)
264 }
258 }
265
259
266 /// Serialize the dirstate to the `v2` format after clearing ambigous `mtime`s.
267 fn write_nodes(
260 fn write_nodes(
268 nodes: &mut dirstate_map::ChildNodes,
261 nodes: &mut dirstate_map::ChildNodes,
269 now: i32,
270 out: &mut Vec<u8>,
262 out: &mut Vec<u8>,
271 ) -> Result<ChildNodes, DirstateError> {
263 ) -> Result<ChildNodes, DirstateError> {
272 // `dirstate_map::ChildNodes` is a `HashMap` with undefined iteration
264 // `dirstate_map::ChildNodes` is a `HashMap` with undefined iteration
@@ -277,7 +269,7 b' fn write_nodes('
277 let mut on_disk_nodes = Vec::with_capacity(nodes.len());
269 let mut on_disk_nodes = Vec::with_capacity(nodes.len());
278 for (full_path, node) in nodes {
270 for (full_path, node) in nodes {
279 on_disk_nodes.push(Node {
271 on_disk_nodes.push(Node {
280 children: write_nodes(&mut node.children, now, out)?,
272 children: write_nodes(&mut node.children, out)?,
281 tracked_descendants_count: node.tracked_descendants_count.into(),
273 tracked_descendants_count: node.tracked_descendants_count.into(),
282 full_path: write_slice::<u8>(
274 full_path: write_slice::<u8>(
283 full_path.full_path().as_bytes(),
275 full_path.full_path().as_bytes(),
@@ -296,7 +288,6 b' fn write_nodes('
296 }
288 }
297 },
289 },
298 entry: if let Some(entry) = &mut node.entry {
290 entry: if let Some(entry) = &mut node.entry {
299 clear_ambiguous_mtime(entry, now);
300 OptEntry {
291 OptEntry {
301 state: entry.state.into(),
292 state: entry.state.into(),
302 mode: entry.mode.into(),
293 mode: entry.mode.into(),
General Comments 0
You need to be logged in to leave comments. Login now