##// END OF EJS Templates
dirstate-v2: Make more APIs fallible, returning Result...
Simon Sapin -
r48126:ed1583a8 default
parent child Browse files
Show More
@@ -5,6 +5,7 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_tree::on_disk::DirstateV2ParseError;
8 use crate::errors::HgError;
9 use crate::errors::HgError;
9 use crate::revlog::Node;
10 use crate::revlog::Node;
10 use crate::utils::hg_path::{HgPath, HgPathBuf};
11 use crate::utils::hg_path::{HgPath, HgPathBuf};
@@ -76,12 +77,19 b' const MTIME_UNSET: i32 = -1;'
76 pub const SIZE_FROM_OTHER_PARENT: i32 = -2;
77 pub const SIZE_FROM_OTHER_PARENT: i32 = -2;
77
78
78 pub type StateMap = FastHashMap<HgPathBuf, DirstateEntry>;
79 pub type StateMap = FastHashMap<HgPathBuf, DirstateEntry>;
79 pub type StateMapIter<'a> =
80 pub type StateMapIter<'a> = Box<
80 Box<dyn Iterator<Item = (&'a HgPath, DirstateEntry)> + Send + 'a>;
81 dyn Iterator<
82 Item = Result<(&'a HgPath, DirstateEntry), DirstateV2ParseError>,
83 > + Send
84 + 'a,
85 >;
81
86
82 pub type CopyMap = FastHashMap<HgPathBuf, HgPathBuf>;
87 pub type CopyMap = FastHashMap<HgPathBuf, HgPathBuf>;
83 pub type CopyMapIter<'a> =
88 pub type CopyMapIter<'a> = Box<
84 Box<dyn Iterator<Item = (&'a HgPath, &'a HgPath)> + Send + 'a>;
89 dyn Iterator<Item = Result<(&'a HgPath, &'a HgPath), DirstateV2ParseError>>
90 + Send
91 + 'a,
92 >;
85
93
86 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
94 #[derive(Copy, Clone, Debug, Eq, PartialEq)]
87 pub enum EntryState {
95 pub enum EntryState {
@@ -8,13 +8,14 b''
8 //! A multiset of directory names.
8 //! A multiset of directory names.
9 //!
9 //!
10 //! Used to counts the references to directories in a manifest or dirstate.
10 //! Used to counts the references to directories in a manifest or dirstate.
11 use crate::dirstate_tree::on_disk::DirstateV2ParseError;
11 use crate::{
12 use crate::{
12 dirstate::EntryState,
13 dirstate::EntryState,
13 utils::{
14 utils::{
14 files,
15 files,
15 hg_path::{HgPath, HgPathBuf, HgPathError},
16 hg_path::{HgPath, HgPathBuf, HgPathError},
16 },
17 },
17 DirstateEntry, DirstateMapError, FastHashMap,
18 DirstateEntry, DirstateError, DirstateMapError, FastHashMap,
18 };
19 };
19 use std::collections::{hash_map, hash_map::Entry, HashMap, HashSet};
20 use std::collections::{hash_map, hash_map::Entry, HashMap, HashSet};
20
21
@@ -33,15 +34,18 b' impl DirsMultiset {'
33 pub fn from_dirstate<I, P>(
34 pub fn from_dirstate<I, P>(
34 dirstate: I,
35 dirstate: I,
35 skip_state: Option<EntryState>,
36 skip_state: Option<EntryState>,
36 ) -> Result<Self, DirstateMapError>
37 ) -> Result<Self, DirstateError>
37 where
38 where
38 I: IntoIterator<Item = (P, DirstateEntry)>,
39 I: IntoIterator<
40 Item = Result<(P, DirstateEntry), DirstateV2ParseError>,
41 >,
39 P: AsRef<HgPath>,
42 P: AsRef<HgPath>,
40 {
43 {
41 let mut multiset = DirsMultiset {
44 let mut multiset = DirsMultiset {
42 inner: FastHashMap::default(),
45 inner: FastHashMap::default(),
43 };
46 };
44 for (filename, entry) in dirstate {
47 for item in dirstate {
48 let (filename, entry) = item?;
45 let filename = filename.as_ref();
49 let filename = filename.as_ref();
46 // This `if` is optimized out of the loop
50 // This `if` is optimized out of the loop
47 if let Some(skip) = skip_state {
51 if let Some(skip) = skip_state {
@@ -337,8 +341,11 b' mod tests {'
337 };
341 };
338 assert_eq!(expected, new);
342 assert_eq!(expected, new);
339
343
340 let new =
344 let new = DirsMultiset::from_dirstate(
341 DirsMultiset::from_dirstate(StateMap::default(), None).unwrap();
345 StateMap::default().into_iter().map(Ok),
346 None,
347 )
348 .unwrap();
342 let expected = DirsMultiset {
349 let expected = DirsMultiset {
343 inner: FastHashMap::default(),
350 inner: FastHashMap::default(),
344 };
351 };
@@ -362,20 +369,17 b' mod tests {'
362 };
369 };
363 assert_eq!(expected, new);
370 assert_eq!(expected, new);
364
371
365 let input_map: HashMap<_, _> = ["b/x", "a/c", "a/d/x"]
372 let input_map = ["b/x", "a/c", "a/d/x"].iter().map(|f| {
366 .iter()
373 Ok((
367 .map(|f| {
374 HgPathBuf::from_bytes(f.as_bytes()),
368 (
375 DirstateEntry {
369 HgPathBuf::from_bytes(f.as_bytes()),
376 state: EntryState::Normal,
370 DirstateEntry {
377 mode: 0,
371 state: EntryState::Normal,
378 mtime: 0,
372 mode: 0,
379 size: 0,
373 mtime: 0,
380 },
374 size: 0,
381 ))
375 },
382 });
376 )
377 })
378 .collect();
379 let expected_inner = [("", 2), ("a", 2), ("b", 1), ("a/d", 1)]
383 let expected_inner = [("", 2), ("a", 2), ("b", 1), ("a/d", 1)]
380 .iter()
384 .iter()
381 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
385 .map(|(k, v)| (HgPathBuf::from_bytes(k.as_bytes()), *v))
@@ -390,7 +394,7 b' mod tests {'
390
394
391 #[test]
395 #[test]
392 fn test_dirsmultiset_new_skip() {
396 fn test_dirsmultiset_new_skip() {
393 let input_map: HashMap<_, _> = [
397 let input_map = [
394 ("a/", EntryState::Normal),
398 ("a/", EntryState::Normal),
395 ("a/b", EntryState::Normal),
399 ("a/b", EntryState::Normal),
396 ("a/c", EntryState::Removed),
400 ("a/c", EntryState::Removed),
@@ -398,7 +402,7 b' mod tests {'
398 ]
402 ]
399 .iter()
403 .iter()
400 .map(|(f, state)| {
404 .map(|(f, state)| {
401 (
405 Ok((
402 HgPathBuf::from_bytes(f.as_bytes()),
406 HgPathBuf::from_bytes(f.as_bytes()),
403 DirstateEntry {
407 DirstateEntry {
404 state: *state,
408 state: *state,
@@ -406,9 +410,8 b' mod tests {'
406 mtime: 0,
410 mtime: 0,
407 size: 0,
411 size: 0,
408 },
412 },
409 )
413 ))
410 })
414 });
411 .collect();
412
415
413 // "a" incremented with "a/c" and "a/d/"
416 // "a" incremented with "a/c" and "a/d/"
414 let expected_inner = [("", 1), ("a", 2)]
417 let expected_inner = [("", 1), ("a", 2)]
@@ -10,8 +10,8 b' use crate::{'
10 dirstate::EntryState,
10 dirstate::EntryState,
11 pack_dirstate, parse_dirstate,
11 pack_dirstate, parse_dirstate,
12 utils::hg_path::{HgPath, HgPathBuf},
12 utils::hg_path::{HgPath, HgPathBuf},
13 CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateMapError,
13 CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateParents,
14 DirstateParents, StateMap,
14 StateMap,
15 };
15 };
16 use micro_timer::timed;
16 use micro_timer::timed;
17 use std::collections::HashSet;
17 use std::collections::HashSet;
@@ -66,7 +66,7 b' impl DirstateMap {'
66 filename: &HgPath,
66 filename: &HgPath,
67 old_state: EntryState,
67 old_state: EntryState,
68 entry: DirstateEntry,
68 entry: DirstateEntry,
69 ) -> Result<(), DirstateMapError> {
69 ) -> Result<(), DirstateError> {
70 if old_state == EntryState::Unknown || old_state == EntryState::Removed
70 if old_state == EntryState::Unknown || old_state == EntryState::Removed
71 {
71 {
72 if let Some(ref mut dirs) = self.dirs {
72 if let Some(ref mut dirs) = self.dirs {
@@ -104,7 +104,7 b' impl DirstateMap {'
104 filename: &HgPath,
104 filename: &HgPath,
105 old_state: EntryState,
105 old_state: EntryState,
106 size: i32,
106 size: i32,
107 ) -> Result<(), DirstateMapError> {
107 ) -> Result<(), DirstateError> {
108 if old_state != EntryState::Unknown && old_state != EntryState::Removed
108 if old_state != EntryState::Unknown && old_state != EntryState::Removed
109 {
109 {
110 if let Some(ref mut dirs) = self.dirs {
110 if let Some(ref mut dirs) = self.dirs {
@@ -138,7 +138,7 b' impl DirstateMap {'
138 &mut self,
138 &mut self,
139 filename: &HgPath,
139 filename: &HgPath,
140 old_state: EntryState,
140 old_state: EntryState,
141 ) -> Result<bool, DirstateMapError> {
141 ) -> Result<bool, DirstateError> {
142 let exists = self.state_map.remove(filename).is_some();
142 let exists = self.state_map.remove(filename).is_some();
143
143
144 if exists {
144 if exists {
@@ -246,20 +246,20 b' impl DirstateMap {'
246 /// emulate a Python lazy property, but it is ugly and unidiomatic.
246 /// emulate a Python lazy property, but it is ugly and unidiomatic.
247 /// TODO One day, rewriting this struct using the typestate might be a
247 /// TODO One day, rewriting this struct using the typestate might be a
248 /// good idea.
248 /// good idea.
249 pub fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
249 pub fn set_all_dirs(&mut self) -> Result<(), DirstateError> {
250 if self.all_dirs.is_none() {
250 if self.all_dirs.is_none() {
251 self.all_dirs = Some(DirsMultiset::from_dirstate(
251 self.all_dirs = Some(DirsMultiset::from_dirstate(
252 self.state_map.iter().map(|(k, v)| (k, *v)),
252 self.state_map.iter().map(|(k, v)| Ok((k, *v))),
253 None,
253 None,
254 )?);
254 )?);
255 }
255 }
256 Ok(())
256 Ok(())
257 }
257 }
258
258
259 pub fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
259 pub fn set_dirs(&mut self) -> Result<(), DirstateError> {
260 if self.dirs.is_none() {
260 if self.dirs.is_none() {
261 self.dirs = Some(DirsMultiset::from_dirstate(
261 self.dirs = Some(DirsMultiset::from_dirstate(
262 self.state_map.iter().map(|(k, v)| (k, *v)),
262 self.state_map.iter().map(|(k, v)| Ok((k, *v))),
263 Some(EntryState::Removed),
263 Some(EntryState::Removed),
264 )?);
264 )?);
265 }
265 }
@@ -269,7 +269,7 b' impl DirstateMap {'
269 pub fn has_tracked_dir(
269 pub fn has_tracked_dir(
270 &mut self,
270 &mut self,
271 directory: &HgPath,
271 directory: &HgPath,
272 ) -> Result<bool, DirstateMapError> {
272 ) -> Result<bool, DirstateError> {
273 self.set_dirs()?;
273 self.set_dirs()?;
274 Ok(self.dirs.as_ref().unwrap().contains(directory))
274 Ok(self.dirs.as_ref().unwrap().contains(directory))
275 }
275 }
@@ -277,7 +277,7 b' impl DirstateMap {'
277 pub fn has_dir(
277 pub fn has_dir(
278 &mut self,
278 &mut self,
279 directory: &HgPath,
279 directory: &HgPath,
280 ) -> Result<bool, DirstateMapError> {
280 ) -> Result<bool, DirstateError> {
281 self.set_all_dirs()?;
281 self.set_all_dirs()?;
282 Ok(self.all_dirs.as_ref().unwrap().contains(directory))
282 Ok(self.all_dirs.as_ref().unwrap().contains(directory))
283 }
283 }
@@ -43,13 +43,18 b' pub fn parse_dirstate(contents: &[u8]) -'
43 copies.push((path, source));
43 copies.push((path, source));
44 }
44 }
45 entries.push((path, *entry));
45 entries.push((path, *entry));
46 Ok(())
46 })?;
47 })?;
47 Ok((parents, entries, copies))
48 Ok((parents, entries, copies))
48 }
49 }
49
50
50 pub fn parse_dirstate_entries<'a>(
51 pub fn parse_dirstate_entries<'a>(
51 mut contents: &'a [u8],
52 mut contents: &'a [u8],
52 mut each_entry: impl FnMut(&'a HgPath, &DirstateEntry, Option<&'a HgPath>),
53 mut each_entry: impl FnMut(
54 &'a HgPath,
55 &DirstateEntry,
56 Option<&'a HgPath>,
57 ) -> Result<(), HgError>,
53 ) -> Result<&'a DirstateParents, HgError> {
58 ) -> Result<&'a DirstateParents, HgError> {
54 let (parents, rest) = DirstateParents::from_bytes(contents)
59 let (parents, rest) = DirstateParents::from_bytes(contents)
55 .map_err(|_| HgError::corrupted("Too little data for dirstate."))?;
60 .map_err(|_| HgError::corrupted("Too little data for dirstate."))?;
@@ -75,7 +80,7 b" pub fn parse_dirstate_entries<'a>("
75 iter.next().expect("splitn always yields at least one item"),
80 iter.next().expect("splitn always yields at least one item"),
76 );
81 );
77 let copy_source = iter.next().map(HgPath::new);
82 let copy_source = iter.next().map(HgPath::new);
78 each_entry(path, &entry, copy_source);
83 each_entry(path, &entry, copy_source)?;
79
84
80 contents = rest;
85 contents = rest;
81 }
86 }
@@ -9,6 +9,7 b''
9 //! It is currently missing a lot of functionality compared to the Python one
9 //! It is currently missing a lot of functionality compared to the Python one
10 //! and will only be triggered in narrow cases.
10 //! and will only be triggered in narrow cases.
11
11
12 use crate::dirstate_tree::on_disk::DirstateV2ParseError;
12 use crate::utils::path_auditor::PathAuditor;
13 use crate::utils::path_auditor::PathAuditor;
13 use crate::{
14 use crate::{
14 dirstate::SIZE_FROM_OTHER_PARENT,
15 dirstate::SIZE_FROM_OTHER_PARENT,
@@ -302,6 +303,8 b' pub enum StatusError {'
302 Path(HgPathError),
303 Path(HgPathError),
303 /// An invalid "ignore" pattern was found
304 /// An invalid "ignore" pattern was found
304 Pattern(PatternError),
305 Pattern(PatternError),
306 /// Corrupted dirstate
307 DirstateV2ParseError(DirstateV2ParseError),
305 }
308 }
306
309
307 pub type StatusResult<T> = Result<T, StatusError>;
310 pub type StatusResult<T> = Result<T, StatusError>;
@@ -312,6 +315,9 b' impl fmt::Display for StatusError {'
312 StatusError::IO(error) => error.fmt(f),
315 StatusError::IO(error) => error.fmt(f),
313 StatusError::Path(error) => error.fmt(f),
316 StatusError::Path(error) => error.fmt(f),
314 StatusError::Pattern(error) => error.fmt(f),
317 StatusError::Pattern(error) => error.fmt(f),
318 StatusError::DirstateV2ParseError(_) => {
319 f.write_str("dirstate-v2 parse error")
320 }
315 }
321 }
316 }
322 }
317 }
323 }
@@ -5,6 +5,7 b' use std::convert::TryInto;'
5 use std::path::PathBuf;
5 use std::path::PathBuf;
6
6
7 use super::on_disk;
7 use super::on_disk;
8 use super::on_disk::DirstateV2ParseError;
8 use super::path_with_basename::WithBasename;
9 use super::path_with_basename::WithBasename;
9 use crate::dirstate::parsers::pack_entry;
10 use crate::dirstate::parsers::pack_entry;
10 use crate::dirstate::parsers::packed_entry_size;
11 use crate::dirstate::parsers::packed_entry_size;
@@ -15,7 +16,6 b' use crate::utils::hg_path::{HgPath, HgPa'
15 use crate::CopyMapIter;
16 use crate::CopyMapIter;
16 use crate::DirstateEntry;
17 use crate::DirstateEntry;
17 use crate::DirstateError;
18 use crate::DirstateError;
18 use crate::DirstateMapError;
19 use crate::DirstateParents;
19 use crate::DirstateParents;
20 use crate::DirstateStatus;
20 use crate::DirstateStatus;
21 use crate::EntryState;
21 use crate::EntryState;
@@ -81,9 +81,12 b" impl<'on_disk> ChildNodes<'on_disk> {"
81
81
82 pub(super) fn make_mut(
82 pub(super) fn make_mut(
83 &mut self,
83 &mut self,
84 ) -> &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>> {
84 ) -> Result<
85 &mut FastHashMap<NodeKey<'on_disk>, Node<'on_disk>>,
86 DirstateV2ParseError,
87 > {
85 match self {
88 match self {
86 ChildNodes::InMemory(nodes) => nodes,
89 ChildNodes::InMemory(nodes) => Ok(nodes),
87 }
90 }
88 }
91 }
89 }
92 }
@@ -92,11 +95,11 b" impl<'tree, 'on_disk> ChildNodesRef<'tre"
92 pub(super) fn get(
95 pub(super) fn get(
93 &self,
96 &self,
94 base_name: &HgPath,
97 base_name: &HgPath,
95 ) -> Option<NodeRef<'tree, 'on_disk>> {
98 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
96 match self {
99 match self {
97 ChildNodesRef::InMemory(nodes) => nodes
100 ChildNodesRef::InMemory(nodes) => Ok(nodes
98 .get_key_value(base_name)
101 .get_key_value(base_name)
99 .map(|(k, v)| NodeRef::InMemory(k, v)),
102 .map(|(k, v)| NodeRef::InMemory(k, v))),
100 }
103 }
101 }
104 }
102
105
@@ -131,9 +134,14 b" impl<'tree, 'on_disk> ChildNodesRef<'tre"
131 .iter()
134 .iter()
132 .map(|(k, v)| NodeRef::InMemory(k, v))
135 .map(|(k, v)| NodeRef::InMemory(k, v))
133 .collect();
136 .collect();
137 fn sort_key<'a>(node: &'a NodeRef) -> &'a HgPath {
138 match node {
139 NodeRef::InMemory(path, _node) => path.base_name(),
140 }
141 }
134 // `sort_unstable_by_key` doesn’t allow keys borrowing from the
142 // `sort_unstable_by_key` doesn’t allow keys borrowing from the
135 // value: https://github.com/rust-lang/rust/issues/34162
143 // value: https://github.com/rust-lang/rust/issues/34162
136 vec.sort_unstable_by(|a, b| a.base_name().cmp(b.base_name()));
144 vec.sort_unstable_by(|a, b| sort_key(a).cmp(sort_key(b)));
137 vec
145 vec
138 }
146 }
139 }
147 }
@@ -141,35 +149,51 b" impl<'tree, 'on_disk> ChildNodesRef<'tre"
141 }
149 }
142
150
143 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> {
151 impl<'tree, 'on_disk> NodeRef<'tree, 'on_disk> {
144 pub(super) fn full_path(&self) -> &'tree HgPath {
152 pub(super) fn full_path(
153 &self,
154 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
145 match self {
155 match self {
146 NodeRef::InMemory(path, _node) => path.full_path(),
156 NodeRef::InMemory(path, _node) => Ok(path.full_path()),
147 }
157 }
148 }
158 }
149
159
150 /// Returns a `Cow` that can borrow 'on_disk but is detached from 'tree
160 /// Returns a `Cow` that can borrow 'on_disk but is detached from 'tree
151 pub(super) fn full_path_cow(&self) -> Cow<'on_disk, HgPath> {
161 pub(super) fn full_path_cow(
162 &self,
163 ) -> Result<Cow<'on_disk, HgPath>, DirstateV2ParseError> {
152 match self {
164 match self {
153 NodeRef::InMemory(path, _node) => path.full_path().clone(),
165 NodeRef::InMemory(path, _node) => Ok(path.full_path().clone()),
166 }
167 }
168
169 pub(super) fn base_name(
170 &self,
171 ) -> Result<&'tree HgPath, DirstateV2ParseError> {
172 match self {
173 NodeRef::InMemory(path, _node) => Ok(path.base_name()),
154 }
174 }
155 }
175 }
156
176
157 pub(super) fn base_name(&self) -> &'tree HgPath {
177 pub(super) fn children(
178 &self,
179 ) -> Result<ChildNodesRef<'tree, 'on_disk>, DirstateV2ParseError> {
158 match self {
180 match self {
159 NodeRef::InMemory(path, _node) => path.base_name(),
181 NodeRef::InMemory(_path, node) => Ok(node.children.as_ref()),
160 }
182 }
161 }
183 }
162
184
163 pub(super) fn children(&self) -> ChildNodesRef<'tree, 'on_disk> {
185 pub(super) fn has_copy_source(&self) -> bool {
164 match self {
186 match self {
165 NodeRef::InMemory(_path, node) => node.children.as_ref(),
187 NodeRef::InMemory(_path, node) => node.copy_source.is_some(),
166 }
188 }
167 }
189 }
168
190
169 pub(super) fn copy_source(&self) -> Option<&'tree HgPath> {
191 pub(super) fn copy_source(
192 &self,
193 ) -> Result<Option<&'tree HgPath>, DirstateV2ParseError> {
170 match self {
194 match self {
171 NodeRef::InMemory(_path, node) => {
195 NodeRef::InMemory(_path, node) => {
172 node.copy_source.as_ref().map(|s| &**s)
196 Ok(node.copy_source.as_ref().map(|s| &**s))
173 }
197 }
174 }
198 }
175 }
199 }
@@ -180,15 +204,20 b" impl<'tree, 'on_disk> NodeRef<'tree, 'on"
180 }
204 }
181 }
205 }
182
206
183 pub(super) fn entry(&self) -> Option<DirstateEntry> {
207 pub(super) fn entry(
208 &self,
209 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
184 match self {
210 match self {
185 NodeRef::InMemory(_path, node) => node.entry,
211 NodeRef::InMemory(_path, node) => Ok(node.entry),
186 }
212 }
187 }
213 }
188 pub(super) fn state(&self) -> Option<EntryState> {
214
215 pub(super) fn state(
216 &self,
217 ) -> Result<Option<EntryState>, DirstateV2ParseError> {
189 match self {
218 match self {
190 NodeRef::InMemory(_path, node) => {
219 NodeRef::InMemory(_path, node) => {
191 node.entry.as_ref().map(|entry| entry.state)
220 Ok(node.entry.as_ref().map(|entry| entry.state))
192 }
221 }
193 }
222 }
194 }
223 }
@@ -253,7 +282,7 b" impl<'on_disk> DirstateMap<'on_disk> {"
253 ancestor.tracked_descendants_count += 1
282 ancestor.tracked_descendants_count += 1
254 }
283 }
255 },
284 },
256 );
285 )?;
257 assert!(
286 assert!(
258 node.entry.is_none(),
287 node.entry.is_none(),
259 "duplicate dirstate entry in read"
288 "duplicate dirstate entry in read"
@@ -268,6 +297,7 b" impl<'on_disk> DirstateMap<'on_disk> {"
268 if copy_source.is_some() {
297 if copy_source.is_some() {
269 map.nodes_with_copy_source_count += 1
298 map.nodes_with_copy_source_count += 1
270 }
299 }
300 Ok(())
271 },
301 },
272 )?;
302 )?;
273 let parents = Some(parents.clone());
303 let parents = Some(parents.clone());
@@ -278,18 +308,21 b" impl<'on_disk> DirstateMap<'on_disk> {"
278 fn get_node<'tree>(
308 fn get_node<'tree>(
279 &'tree self,
309 &'tree self,
280 path: &HgPath,
310 path: &HgPath,
281 ) -> Option<NodeRef<'tree, 'on_disk>> {
311 ) -> Result<Option<NodeRef<'tree, 'on_disk>>, DirstateV2ParseError> {
282 let mut children = self.root.as_ref();
312 let mut children = self.root.as_ref();
283 let mut components = path.components();
313 let mut components = path.components();
284 let mut component =
314 let mut component =
285 components.next().expect("expected at least one components");
315 components.next().expect("expected at least one components");
286 loop {
316 loop {
287 let child = children.get(component)?;
317 if let Some(child) = children.get(component)? {
288 if let Some(next_component) = components.next() {
318 if let Some(next_component) = components.next() {
289 component = next_component;
319 component = next_component;
290 children = child.children();
320 children = child.children()?;
321 } else {
322 return Ok(Some(child));
323 }
291 } else {
324 } else {
292 return Some(child);
325 return Ok(None);
293 }
326 }
294 }
327 }
295 }
328 }
@@ -301,18 +334,21 b" impl<'on_disk> DirstateMap<'on_disk> {"
301 fn get_node_mut<'tree>(
334 fn get_node_mut<'tree>(
302 root: &'tree mut ChildNodes<'on_disk>,
335 root: &'tree mut ChildNodes<'on_disk>,
303 path: &HgPath,
336 path: &HgPath,
304 ) -> Option<&'tree mut Node<'on_disk>> {
337 ) -> Result<Option<&'tree mut Node<'on_disk>>, DirstateV2ParseError> {
305 let mut children = root;
338 let mut children = root;
306 let mut components = path.components();
339 let mut components = path.components();
307 let mut component =
340 let mut component =
308 components.next().expect("expected at least one components");
341 components.next().expect("expected at least one components");
309 loop {
342 loop {
310 let child = children.make_mut().get_mut(component)?;
343 if let Some(child) = children.make_mut()?.get_mut(component) {
311 if let Some(next_component) = components.next() {
344 if let Some(next_component) = components.next() {
312 component = next_component;
345 component = next_component;
313 children = &mut child.children;
346 children = &mut child.children;
347 } else {
348 return Ok(Some(child));
349 }
314 } else {
350 } else {
315 return Some(child);
351 return Ok(None);
316 }
352 }
317 }
353 }
318 }
354 }
@@ -324,7 +360,7 b" impl<'on_disk> DirstateMap<'on_disk> {"
324 WithBasename<&'path HgPath>,
360 WithBasename<&'path HgPath>,
325 ) -> WithBasename<Cow<'on_disk, HgPath>>,
361 ) -> WithBasename<Cow<'on_disk, HgPath>>,
326 mut each_ancestor: impl FnMut(&mut Node),
362 mut each_ancestor: impl FnMut(&mut Node),
327 ) -> &'tree mut Node<'on_disk> {
363 ) -> Result<&'tree mut Node<'on_disk>, DirstateV2ParseError> {
328 let mut child_nodes = root;
364 let mut child_nodes = root;
329 let mut inclusive_ancestor_paths =
365 let mut inclusive_ancestor_paths =
330 WithBasename::inclusive_ancestors_of(path);
366 WithBasename::inclusive_ancestors_of(path);
@@ -336,7 +372,7 b" impl<'on_disk> DirstateMap<'on_disk> {"
336 // map already contains that key, without introducing double
372 // map already contains that key, without introducing double
337 // lookup?
373 // lookup?
338 let child_node = child_nodes
374 let child_node = child_nodes
339 .make_mut()
375 .make_mut()?
340 .entry(to_cow(ancestor_path))
376 .entry(to_cow(ancestor_path))
341 .or_default();
377 .or_default();
342 if let Some(next) = inclusive_ancestor_paths.next() {
378 if let Some(next) = inclusive_ancestor_paths.next() {
@@ -344,7 +380,7 b" impl<'on_disk> DirstateMap<'on_disk> {"
344 ancestor_path = next;
380 ancestor_path = next;
345 child_nodes = &mut child_node.children;
381 child_nodes = &mut child_node.children;
346 } else {
382 } else {
347 return child_node;
383 return Ok(child_node);
348 }
384 }
349 }
385 }
350 }
386 }
@@ -354,7 +390,7 b" impl<'on_disk> DirstateMap<'on_disk> {"
354 path: &HgPath,
390 path: &HgPath,
355 old_state: EntryState,
391 old_state: EntryState,
356 new_entry: DirstateEntry,
392 new_entry: DirstateEntry,
357 ) {
393 ) -> Result<(), DirstateV2ParseError> {
358 let tracked_count_increment =
394 let tracked_count_increment =
359 match (old_state.is_tracked(), new_entry.state.is_tracked()) {
395 match (old_state.is_tracked(), new_entry.state.is_tracked()) {
360 (false, true) => 1,
396 (false, true) => 1,
@@ -376,16 +412,19 b" impl<'on_disk> DirstateMap<'on_disk> {"
376 _ => {}
412 _ => {}
377 }
413 }
378 },
414 },
379 );
415 )?;
380 if node.entry.is_none() {
416 if node.entry.is_none() {
381 self.nodes_with_entry_count += 1
417 self.nodes_with_entry_count += 1
382 }
418 }
383 node.entry = Some(new_entry)
419 node.entry = Some(new_entry);
420 Ok(())
384 }
421 }
385
422
386 fn iter_nodes<'tree>(
423 fn iter_nodes<'tree>(
387 &'tree self,
424 &'tree self,
388 ) -> impl Iterator<Item = NodeRef<'tree, 'on_disk>> + 'tree {
425 ) -> impl Iterator<
426 Item = Result<NodeRef<'tree, 'on_disk>, DirstateV2ParseError>,
427 > + 'tree {
389 // Depth first tree traversal.
428 // Depth first tree traversal.
390 //
429 //
391 // If we could afford internal iteration and recursion,
430 // If we could afford internal iteration and recursion,
@@ -409,8 +448,12 b" impl<'on_disk> DirstateMap<'on_disk> {"
409 let mut iter = self.root.as_ref().iter();
448 let mut iter = self.root.as_ref().iter();
410 std::iter::from_fn(move || {
449 std::iter::from_fn(move || {
411 while let Some(child_node) = iter.next() {
450 while let Some(child_node) = iter.next() {
451 let children = match child_node.children() {
452 Ok(children) => children,
453 Err(error) => return Some(Err(error)),
454 };
412 // Pseudo-recursion
455 // Pseudo-recursion
413 let new_iter = child_node.children().iter();
456 let new_iter = children.iter();
414 let old_iter = std::mem::replace(&mut iter, new_iter);
457 let old_iter = std::mem::replace(&mut iter, new_iter);
415 stack.push((child_node, old_iter));
458 stack.push((child_node, old_iter));
416 }
459 }
@@ -420,7 +463,7 b" impl<'on_disk> DirstateMap<'on_disk> {"
420 // explicit stack
463 // explicit stack
421 iter = next_iter;
464 iter = next_iter;
422
465
423 Some(child_node)
466 Some(Ok(child_node))
424 } else {
467 } else {
425 // Reached the bottom of the stack, we’re done
468 // Reached the bottom of the stack, we’re done
426 None
469 None
@@ -428,17 +471,62 b" impl<'on_disk> DirstateMap<'on_disk> {"
428 })
471 })
429 }
472 }
430
473
431 fn clear_known_ambiguous_mtimes(&mut self, paths: &[impl AsRef<HgPath>]) {
474 fn clear_known_ambiguous_mtimes(
475 &mut self,
476 paths: &[impl AsRef<HgPath>],
477 ) -> Result<(), DirstateV2ParseError> {
432 for path in paths {
478 for path in paths {
433 if let Some(node) =
479 if let Some(node) =
434 Self::get_node_mut(&mut self.root, path.as_ref())
480 Self::get_node_mut(&mut self.root, path.as_ref())?
435 {
481 {
436 if let Some(entry) = node.entry.as_mut() {
482 if let Some(entry) = node.entry.as_mut() {
437 entry.clear_mtime();
483 entry.clear_mtime();
438 }
484 }
439 }
485 }
440 }
486 }
487 Ok(())
441 }
488 }
489
490 /// Return a faillilble iterator of full paths of nodes that have an
491 /// `entry` for which the given `predicate` returns true.
492 ///
493 /// Fallibility means that each iterator item is a `Result`, which may
494 /// indicate a parse error of the on-disk dirstate-v2 format. Such errors
495 /// should only happen if Mercurial is buggy or a repository is corrupted.
496 fn filter_full_paths<'tree>(
497 &'tree self,
498 predicate: impl Fn(&DirstateEntry) -> bool + 'tree,
499 ) -> impl Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + 'tree
500 {
501 filter_map_results(self.iter_nodes(), move |node| {
502 if let Some(entry) = node.entry()? {
503 if predicate(&entry) {
504 return Ok(Some(node.full_path()?));
505 }
506 }
507 Ok(None)
508 })
509 }
510 }
511
512 /// Like `Iterator::filter_map`, but over a fallible iterator of `Result`s.
513 ///
514 /// The callback is only called for incoming `Ok` values. Errors are passed
515 /// through as-is. In order to let it use the `?` operator the callback is
516 /// expected to return a `Result` of `Option`, instead of an `Option` of
517 /// `Result`.
518 fn filter_map_results<'a, I, F, A, B, E>(
519 iter: I,
520 f: F,
521 ) -> impl Iterator<Item = Result<B, E>> + 'a
522 where
523 I: Iterator<Item = Result<A, E>> + 'a,
524 F: Fn(A) -> Result<Option<B>, E> + 'a,
525 {
526 iter.filter_map(move |result| match result {
527 Ok(node) => f(node).transpose(),
528 Err(e) => Some(Err(e)),
529 })
442 }
530 }
443
531
444 impl<'on_disk> super::dispatch::DirstateMapMethods for DirstateMap<'on_disk> {
532 impl<'on_disk> super::dispatch::DirstateMapMethods for DirstateMap<'on_disk> {
@@ -453,9 +541,8 b" impl<'on_disk> super::dispatch::Dirstate"
453 filename: &HgPath,
541 filename: &HgPath,
454 old_state: EntryState,
542 old_state: EntryState,
455 entry: DirstateEntry,
543 entry: DirstateEntry,
456 ) -> Result<(), DirstateMapError> {
544 ) -> Result<(), DirstateError> {
457 self.add_or_remove_file(filename, old_state, entry);
545 Ok(self.add_or_remove_file(filename, old_state, entry)?)
458 Ok(())
459 }
546 }
460
547
461 fn remove_file(
548 fn remove_file(
@@ -463,36 +550,48 b" impl<'on_disk> super::dispatch::Dirstate"
463 filename: &HgPath,
550 filename: &HgPath,
464 old_state: EntryState,
551 old_state: EntryState,
465 size: i32,
552 size: i32,
466 ) -> Result<(), DirstateMapError> {
553 ) -> Result<(), DirstateError> {
467 let entry = DirstateEntry {
554 let entry = DirstateEntry {
468 state: EntryState::Removed,
555 state: EntryState::Removed,
469 mode: 0,
556 mode: 0,
470 size,
557 size,
471 mtime: 0,
558 mtime: 0,
472 };
559 };
473 self.add_or_remove_file(filename, old_state, entry);
560 Ok(self.add_or_remove_file(filename, old_state, entry)?)
474 Ok(())
475 }
561 }
476
562
477 fn drop_file(
563 fn drop_file(
478 &mut self,
564 &mut self,
479 filename: &HgPath,
565 filename: &HgPath,
480 old_state: EntryState,
566 old_state: EntryState,
481 ) -> Result<bool, DirstateMapError> {
567 ) -> Result<bool, DirstateError> {
482 struct Dropped {
568 struct Dropped {
483 was_tracked: bool,
569 was_tracked: bool,
484 had_entry: bool,
570 had_entry: bool,
485 had_copy_source: bool,
571 had_copy_source: bool,
486 }
572 }
487 fn recur(nodes: &mut ChildNodes, path: &HgPath) -> Option<Dropped> {
573 fn recur(
574 nodes: &mut ChildNodes,
575 path: &HgPath,
576 ) -> Result<Option<Dropped>, DirstateV2ParseError> {
488 let (first_path_component, rest_of_path) =
577 let (first_path_component, rest_of_path) =
489 path.split_first_component();
578 path.split_first_component();
490 let node = nodes.make_mut().get_mut(first_path_component)?;
579 let node = if let Some(node) =
580 nodes.make_mut()?.get_mut(first_path_component)
581 {
582 node
583 } else {
584 return Ok(None);
585 };
491 let dropped;
586 let dropped;
492 if let Some(rest) = rest_of_path {
587 if let Some(rest) = rest_of_path {
493 dropped = recur(&mut node.children, rest)?;
588 if let Some(d) = recur(&mut node.children, rest)? {
494 if dropped.was_tracked {
589 dropped = d;
495 node.tracked_descendants_count -= 1;
590 if dropped.was_tracked {
591 node.tracked_descendants_count -= 1;
592 }
593 } else {
594 return Ok(None);
496 }
595 }
497 } else {
596 } else {
498 dropped = Dropped {
597 dropped = Dropped {
@@ -510,12 +609,12 b" impl<'on_disk> super::dispatch::Dirstate"
510 && node.copy_source.is_none()
609 && node.copy_source.is_none()
511 && node.children.is_empty()
610 && node.children.is_empty()
512 {
611 {
513 nodes.make_mut().remove(first_path_component);
612 nodes.make_mut()?.remove(first_path_component);
514 }
613 }
515 Some(dropped)
614 Ok(Some(dropped))
516 }
615 }
517
616
518 if let Some(dropped) = recur(&mut self.root, filename) {
617 if let Some(dropped) = recur(&mut self.root, filename)? {
519 if dropped.had_entry {
618 if dropped.had_entry {
520 self.nodes_with_entry_count -= 1
619 self.nodes_with_entry_count -= 1
521 }
620 }
@@ -529,20 +628,31 b" impl<'on_disk> super::dispatch::Dirstate"
529 }
628 }
530 }
629 }
531
630
532 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32) {
631 fn clear_ambiguous_times(
632 &mut self,
633 filenames: Vec<HgPathBuf>,
634 now: i32,
635 ) -> Result<(), DirstateV2ParseError> {
533 for filename in filenames {
636 for filename in filenames {
534 if let Some(node) = Self::get_node_mut(&mut self.root, &filename) {
637 if let Some(node) = Self::get_node_mut(&mut self.root, &filename)?
638 {
535 if let Some(entry) = node.entry.as_mut() {
639 if let Some(entry) = node.entry.as_mut() {
536 entry.clear_ambiguous_mtime(now);
640 entry.clear_ambiguous_mtime(now);
537 }
641 }
538 }
642 }
539 }
643 }
644 Ok(())
540 }
645 }
541
646
542 fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool {
647 fn non_normal_entries_contains(
543 self.get_node(key)
648 &mut self,
544 .and_then(|node| node.entry())
649 key: &HgPath,
545 .map_or(false, |entry| entry.is_non_normal())
650 ) -> Result<bool, DirstateV2ParseError> {
651 Ok(if let Some(node) = self.get_node(key)? {
652 node.entry()?.map_or(false, |entry| entry.is_non_normal())
653 } else {
654 false
655 })
546 }
656 }
547
657
548 fn non_normal_entries_remove(&mut self, _key: &HgPath) {
658 fn non_normal_entries_remove(&mut self, _key: &HgPath) {
@@ -552,13 +662,10 b" impl<'on_disk> super::dispatch::Dirstate"
552
662
553 fn non_normal_or_other_parent_paths(
663 fn non_normal_or_other_parent_paths(
554 &mut self,
664 &mut self,
555 ) -> Box<dyn Iterator<Item = &HgPath> + '_> {
665 ) -> Box<dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + '_>
556 Box::new(self.iter_nodes().filter_map(|node| {
666 {
557 node.entry()
667 Box::new(self.filter_full_paths(|entry| {
558 .filter(|entry| {
668 entry.is_non_normal() || entry.is_from_other_parent()
559 entry.is_non_normal() || entry.is_from_other_parent()
560 })
561 .map(|_| node.full_path())
562 }))
669 }))
563 }
670 }
564
671
@@ -569,35 +676,33 b" impl<'on_disk> super::dispatch::Dirstate"
569
676
570 fn iter_non_normal_paths(
677 fn iter_non_normal_paths(
571 &mut self,
678 &mut self,
572 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
679 ) -> Box<
680 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_,
681 > {
573 self.iter_non_normal_paths_panic()
682 self.iter_non_normal_paths_panic()
574 }
683 }
575
684
576 fn iter_non_normal_paths_panic(
685 fn iter_non_normal_paths_panic(
577 &self,
686 &self,
578 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
687 ) -> Box<
579 Box::new(self.iter_nodes().filter_map(|node| {
688 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_,
580 node.entry()
689 > {
581 .filter(|entry| entry.is_non_normal())
690 Box::new(self.filter_full_paths(|entry| entry.is_non_normal()))
582 .map(|_| node.full_path())
583 }))
584 }
691 }
585
692
586 fn iter_other_parent_paths(
693 fn iter_other_parent_paths(
587 &mut self,
694 &mut self,
588 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
695 ) -> Box<
589 Box::new(self.iter_nodes().filter_map(|node| {
696 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_,
590 node.entry()
697 > {
591 .filter(|entry| entry.is_from_other_parent())
698 Box::new(self.filter_full_paths(|entry| entry.is_from_other_parent()))
592 .map(|_| node.full_path())
593 }))
594 }
699 }
595
700
596 fn has_tracked_dir(
701 fn has_tracked_dir(
597 &mut self,
702 &mut self,
598 directory: &HgPath,
703 directory: &HgPath,
599 ) -> Result<bool, DirstateMapError> {
704 ) -> Result<bool, DirstateError> {
600 if let Some(node) = self.get_node(directory) {
705 if let Some(node) = self.get_node(directory)? {
601 // A node without a `DirstateEntry` was created to hold child
706 // A node without a `DirstateEntry` was created to hold child
602 // nodes, and is therefore a directory.
707 // nodes, and is therefore a directory.
603 Ok(!node.has_entry() && node.tracked_descendants_count() > 0)
708 Ok(!node.has_entry() && node.tracked_descendants_count() > 0)
@@ -606,11 +711,8 b" impl<'on_disk> super::dispatch::Dirstate"
606 }
711 }
607 }
712 }
608
713
609 fn has_dir(
714 fn has_dir(&mut self, directory: &HgPath) -> Result<bool, DirstateError> {
610 &mut self,
715 if let Some(node) = self.get_node(directory)? {
611 directory: &HgPath,
612 ) -> Result<bool, DirstateMapError> {
613 if let Some(node) = self.get_node(directory) {
614 // A node without a `DirstateEntry` was created to hold child
716 // A node without a `DirstateEntry` was created to hold child
615 // nodes, and is therefore a directory.
717 // nodes, and is therefore a directory.
616 Ok(!node.has_entry())
718 Ok(!node.has_entry())
@@ -631,25 +733,27 b" impl<'on_disk> super::dispatch::Dirstate"
631 // reallocations
733 // reallocations
632 let mut size = parents.as_bytes().len();
734 let mut size = parents.as_bytes().len();
633 for node in self.iter_nodes() {
735 for node in self.iter_nodes() {
634 if let Some(entry) = node.entry() {
736 let node = node?;
737 if let Some(entry) = node.entry()? {
635 size +=
738 size +=
636 packed_entry_size(node.full_path(), node.copy_source());
739 packed_entry_size(node.full_path()?, node.copy_source()?);
637 if entry.mtime_is_ambiguous(now) {
740 if entry.mtime_is_ambiguous(now) {
638 ambiguous_mtimes.push(node.full_path_cow())
741 ambiguous_mtimes.push(node.full_path_cow()?)
639 }
742 }
640 }
743 }
641 }
744 }
642 self.clear_known_ambiguous_mtimes(&ambiguous_mtimes);
745 self.clear_known_ambiguous_mtimes(&ambiguous_mtimes)?;
643
746
644 let mut packed = Vec::with_capacity(size);
747 let mut packed = Vec::with_capacity(size);
645 packed.extend(parents.as_bytes());
748 packed.extend(parents.as_bytes());
646
749
647 for node in self.iter_nodes() {
750 for node in self.iter_nodes() {
648 if let Some(entry) = node.entry() {
751 let node = node?;
752 if let Some(entry) = node.entry()? {
649 pack_entry(
753 pack_entry(
650 node.full_path(),
754 node.full_path()?,
651 &entry,
755 &entry,
652 node.copy_source(),
756 node.copy_source()?,
653 &mut packed,
757 &mut packed,
654 );
758 );
655 }
759 }
@@ -667,26 +771,27 b" impl<'on_disk> super::dispatch::Dirstate"
667 let now: i32 = now.0.try_into().expect("time overflow");
771 let now: i32 = now.0.try_into().expect("time overflow");
668 let mut paths = Vec::new();
772 let mut paths = Vec::new();
669 for node in self.iter_nodes() {
773 for node in self.iter_nodes() {
670 if let Some(entry) = node.entry() {
774 let node = node?;
775 if let Some(entry) = node.entry()? {
671 if entry.mtime_is_ambiguous(now) {
776 if entry.mtime_is_ambiguous(now) {
672 paths.push(node.full_path_cow())
777 paths.push(node.full_path_cow()?)
673 }
778 }
674 }
779 }
675 }
780 }
676 // Borrow of `self` ends here since we collect cloned paths
781 // Borrow of `self` ends here since we collect cloned paths
677
782
678 self.clear_known_ambiguous_mtimes(&paths);
783 self.clear_known_ambiguous_mtimes(&paths)?;
679
784
680 on_disk::write(self, parents)
785 on_disk::write(self, parents)
681 }
786 }
682
787
683 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
788 fn set_all_dirs(&mut self) -> Result<(), DirstateError> {
684 // Do nothing, this `DirstateMap` does not a separate `all_dirs` that
789 // Do nothing, this `DirstateMap` does not a separate `all_dirs` that
685 // needs to be recomputed
790 // needs to be recomputed
686 Ok(())
791 Ok(())
687 }
792 }
688
793
689 fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
794 fn set_dirs(&mut self) -> Result<(), DirstateError> {
690 // Do nothing, this `DirstateMap` does not a separate `dirs` that needs
795 // Do nothing, this `DirstateMap` does not a separate `dirs` that needs
691 // to be recomputed
796 // to be recomputed
692 Ok(())
797 Ok(())
@@ -708,66 +813,97 b" impl<'on_disk> super::dispatch::Dirstate"
708 }
813 }
709
814
710 fn copy_map_iter(&self) -> CopyMapIter<'_> {
815 fn copy_map_iter(&self) -> CopyMapIter<'_> {
711 Box::new(self.iter_nodes().filter_map(|node| {
816 Box::new(filter_map_results(self.iter_nodes(), |node| {
712 node.copy_source()
817 Ok(if let Some(source) = node.copy_source()? {
713 .map(|copy_source| (node.full_path(), copy_source))
818 Some((node.full_path()?, source))
819 } else {
820 None
821 })
714 }))
822 }))
715 }
823 }
716
824
717 fn copy_map_contains_key(&self, key: &HgPath) -> bool {
825 fn copy_map_contains_key(
718 if let Some(node) = self.get_node(key) {
826 &self,
719 node.copy_source().is_some()
827 key: &HgPath,
828 ) -> Result<bool, DirstateV2ParseError> {
829 Ok(if let Some(node) = self.get_node(key)? {
830 node.has_copy_source()
720 } else {
831 } else {
721 false
832 false
722 }
833 })
723 }
834 }
724
835
725 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPath> {
836 fn copy_map_get(
726 self.get_node(key)?.copy_source()
837 &self,
838 key: &HgPath,
839 ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
840 if let Some(node) = self.get_node(key)? {
841 if let Some(source) = node.copy_source()? {
842 return Ok(Some(source));
843 }
844 }
845 Ok(None)
727 }
846 }
728
847
729 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf> {
848 fn copy_map_remove(
849 &mut self,
850 key: &HgPath,
851 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
730 let count = &mut self.nodes_with_copy_source_count;
852 let count = &mut self.nodes_with_copy_source_count;
731 Self::get_node_mut(&mut self.root, key).and_then(|node| {
853 Ok(Self::get_node_mut(&mut self.root, key)?.and_then(|node| {
732 if node.copy_source.is_some() {
854 if node.copy_source.is_some() {
733 *count -= 1
855 *count -= 1
734 }
856 }
735 node.copy_source.take().map(Cow::into_owned)
857 node.copy_source.take().map(Cow::into_owned)
736 })
858 }))
737 }
859 }
738
860
739 fn copy_map_insert(
861 fn copy_map_insert(
740 &mut self,
862 &mut self,
741 key: HgPathBuf,
863 key: HgPathBuf,
742 value: HgPathBuf,
864 value: HgPathBuf,
743 ) -> Option<HgPathBuf> {
865 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
744 let node = Self::get_or_insert_node(
866 let node = Self::get_or_insert_node(
745 &mut self.root,
867 &mut self.root,
746 &key,
868 &key,
747 WithBasename::to_cow_owned,
869 WithBasename::to_cow_owned,
748 |_ancestor| {},
870 |_ancestor| {},
749 );
871 )?;
750 if node.copy_source.is_none() {
872 if node.copy_source.is_none() {
751 self.nodes_with_copy_source_count += 1
873 self.nodes_with_copy_source_count += 1
752 }
874 }
753 node.copy_source.replace(value.into()).map(Cow::into_owned)
875 Ok(node.copy_source.replace(value.into()).map(Cow::into_owned))
754 }
876 }
755
877
756 fn len(&self) -> usize {
878 fn len(&self) -> usize {
757 self.nodes_with_entry_count as usize
879 self.nodes_with_entry_count as usize
758 }
880 }
759
881
760 fn contains_key(&self, key: &HgPath) -> bool {
882 fn contains_key(
761 self.get(key).is_some()
883 &self,
884 key: &HgPath,
885 ) -> Result<bool, DirstateV2ParseError> {
886 Ok(self.get(key)?.is_some())
762 }
887 }
763
888
764 fn get(&self, key: &HgPath) -> Option<DirstateEntry> {
889 fn get(
765 self.get_node(key)?.entry()
890 &self,
891 key: &HgPath,
892 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
893 Ok(if let Some(node) = self.get_node(key)? {
894 node.entry()?
895 } else {
896 None
897 })
766 }
898 }
767
899
768 fn iter(&self) -> StateMapIter<'_> {
900 fn iter(&self) -> StateMapIter<'_> {
769 Box::new(self.iter_nodes().filter_map(|node| {
901 Box::new(filter_map_results(self.iter_nodes(), |node| {
770 node.entry().map(|entry| (node.full_path(), entry))
902 Ok(if let Some(entry) = node.entry()? {
903 Some((node.full_path()?, entry))
904 } else {
905 None
906 })
771 }))
907 }))
772 }
908 }
773 }
909 }
@@ -1,13 +1,13 b''
1 use std::path::PathBuf;
1 use std::path::PathBuf;
2
2
3 use crate::dirstate::parsers::Timestamp;
3 use crate::dirstate::parsers::Timestamp;
4 use crate::dirstate_tree::on_disk::DirstateV2ParseError;
4 use crate::matchers::Matcher;
5 use crate::matchers::Matcher;
5 use crate::utils::hg_path::{HgPath, HgPathBuf};
6 use crate::utils::hg_path::{HgPath, HgPathBuf};
6 use crate::CopyMapIter;
7 use crate::CopyMapIter;
7 use crate::DirstateEntry;
8 use crate::DirstateEntry;
8 use crate::DirstateError;
9 use crate::DirstateError;
9 use crate::DirstateMap;
10 use crate::DirstateMap;
10 use crate::DirstateMapError;
11 use crate::DirstateParents;
11 use crate::DirstateParents;
12 use crate::DirstateStatus;
12 use crate::DirstateStatus;
13 use crate::EntryState;
13 use crate::EntryState;
@@ -24,54 +24,64 b' pub trait DirstateMapMethods {'
24 filename: &HgPath,
24 filename: &HgPath,
25 old_state: EntryState,
25 old_state: EntryState,
26 entry: DirstateEntry,
26 entry: DirstateEntry,
27 ) -> Result<(), DirstateMapError>;
27 ) -> Result<(), DirstateError>;
28
28
29 fn remove_file(
29 fn remove_file(
30 &mut self,
30 &mut self,
31 filename: &HgPath,
31 filename: &HgPath,
32 old_state: EntryState,
32 old_state: EntryState,
33 size: i32,
33 size: i32,
34 ) -> Result<(), DirstateMapError>;
34 ) -> Result<(), DirstateError>;
35
35
36 fn drop_file(
36 fn drop_file(
37 &mut self,
37 &mut self,
38 filename: &HgPath,
38 filename: &HgPath,
39 old_state: EntryState,
39 old_state: EntryState,
40 ) -> Result<bool, DirstateMapError>;
40 ) -> Result<bool, DirstateError>;
41
41
42 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32);
42 fn clear_ambiguous_times(
43 &mut self,
44 filenames: Vec<HgPathBuf>,
45 now: i32,
46 ) -> Result<(), DirstateV2ParseError>;
43
47
44 fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool;
48 fn non_normal_entries_contains(
49 &mut self,
50 key: &HgPath,
51 ) -> Result<bool, DirstateV2ParseError>;
45
52
46 fn non_normal_entries_remove(&mut self, key: &HgPath);
53 fn non_normal_entries_remove(&mut self, key: &HgPath);
47
54
48 fn non_normal_or_other_parent_paths(
55 fn non_normal_or_other_parent_paths(
49 &mut self,
56 &mut self,
50 ) -> Box<dyn Iterator<Item = &HgPath> + '_>;
57 ) -> Box<dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + '_>;
51
58
52 fn set_non_normal_other_parent_entries(&mut self, force: bool);
59 fn set_non_normal_other_parent_entries(&mut self, force: bool);
53
60
54 fn iter_non_normal_paths(
61 fn iter_non_normal_paths(
55 &mut self,
62 &mut self,
56 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_>;
63 ) -> Box<
64 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_,
65 >;
57
66
58 fn iter_non_normal_paths_panic(
67 fn iter_non_normal_paths_panic(
59 &self,
68 &self,
60 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_>;
69 ) -> Box<
70 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_,
71 >;
61
72
62 fn iter_other_parent_paths(
73 fn iter_other_parent_paths(
63 &mut self,
74 &mut self,
64 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_>;
75 ) -> Box<
76 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_,
77 >;
65
78
66 fn has_tracked_dir(
79 fn has_tracked_dir(
67 &mut self,
80 &mut self,
68 directory: &HgPath,
81 directory: &HgPath,
69 ) -> Result<bool, DirstateMapError>;
82 ) -> Result<bool, DirstateError>;
70
83
71 fn has_dir(
84 fn has_dir(&mut self, directory: &HgPath) -> Result<bool, DirstateError>;
72 &mut self,
73 directory: &HgPath,
74 ) -> Result<bool, DirstateMapError>;
75
85
76 fn pack_v1(
86 fn pack_v1(
77 &mut self,
87 &mut self,
@@ -85,9 +95,9 b' pub trait DirstateMapMethods {'
85 now: Timestamp,
95 now: Timestamp,
86 ) -> Result<Vec<u8>, DirstateError>;
96 ) -> Result<Vec<u8>, DirstateError>;
87
97
88 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError>;
98 fn set_all_dirs(&mut self) -> Result<(), DirstateError>;
89
99
90 fn set_dirs(&mut self) -> Result<(), DirstateMapError>;
100 fn set_dirs(&mut self) -> Result<(), DirstateError>;
91
101
92 fn status<'a>(
102 fn status<'a>(
93 &'a mut self,
103 &'a mut self,
@@ -101,23 +111,36 b' pub trait DirstateMapMethods {'
101
111
102 fn copy_map_iter(&self) -> CopyMapIter<'_>;
112 fn copy_map_iter(&self) -> CopyMapIter<'_>;
103
113
104 fn copy_map_contains_key(&self, key: &HgPath) -> bool;
114 fn copy_map_contains_key(
115 &self,
116 key: &HgPath,
117 ) -> Result<bool, DirstateV2ParseError>;
105
118
106 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPath>;
119 fn copy_map_get(
120 &self,
121 key: &HgPath,
122 ) -> Result<Option<&HgPath>, DirstateV2ParseError>;
107
123
108 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf>;
124 fn copy_map_remove(
125 &mut self,
126 key: &HgPath,
127 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError>;
109
128
110 fn copy_map_insert(
129 fn copy_map_insert(
111 &mut self,
130 &mut self,
112 key: HgPathBuf,
131 key: HgPathBuf,
113 value: HgPathBuf,
132 value: HgPathBuf,
114 ) -> Option<HgPathBuf>;
133 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError>;
115
134
116 fn len(&self) -> usize;
135 fn len(&self) -> usize;
117
136
118 fn contains_key(&self, key: &HgPath) -> bool;
137 fn contains_key(&self, key: &HgPath)
138 -> Result<bool, DirstateV2ParseError>;
119
139
120 fn get(&self, key: &HgPath) -> Option<DirstateEntry>;
140 fn get(
141 &self,
142 key: &HgPath,
143 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError>;
121
144
122 fn iter(&self) -> StateMapIter<'_>;
145 fn iter(&self) -> StateMapIter<'_>;
123 }
146 }
@@ -132,7 +155,7 b' impl DirstateMapMethods for DirstateMap '
132 filename: &HgPath,
155 filename: &HgPath,
133 old_state: EntryState,
156 old_state: EntryState,
134 entry: DirstateEntry,
157 entry: DirstateEntry,
135 ) -> Result<(), DirstateMapError> {
158 ) -> Result<(), DirstateError> {
136 self.add_file(filename, old_state, entry)
159 self.add_file(filename, old_state, entry)
137 }
160 }
138
161
@@ -141,7 +164,7 b' impl DirstateMapMethods for DirstateMap '
141 filename: &HgPath,
164 filename: &HgPath,
142 old_state: EntryState,
165 old_state: EntryState,
143 size: i32,
166 size: i32,
144 ) -> Result<(), DirstateMapError> {
167 ) -> Result<(), DirstateError> {
145 self.remove_file(filename, old_state, size)
168 self.remove_file(filename, old_state, size)
146 }
169 }
147
170
@@ -149,18 +172,25 b' impl DirstateMapMethods for DirstateMap '
149 &mut self,
172 &mut self,
150 filename: &HgPath,
173 filename: &HgPath,
151 old_state: EntryState,
174 old_state: EntryState,
152 ) -> Result<bool, DirstateMapError> {
175 ) -> Result<bool, DirstateError> {
153 self.drop_file(filename, old_state)
176 self.drop_file(filename, old_state)
154 }
177 }
155
178
156 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32) {
179 fn clear_ambiguous_times(
157 self.clear_ambiguous_times(filenames, now)
180 &mut self,
181 filenames: Vec<HgPathBuf>,
182 now: i32,
183 ) -> Result<(), DirstateV2ParseError> {
184 Ok(self.clear_ambiguous_times(filenames, now))
158 }
185 }
159
186
160 fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool {
187 fn non_normal_entries_contains(
188 &mut self,
189 key: &HgPath,
190 ) -> Result<bool, DirstateV2ParseError> {
161 let (non_normal, _other_parent) =
191 let (non_normal, _other_parent) =
162 self.get_non_normal_other_parent_entries();
192 self.get_non_normal_other_parent_entries();
163 non_normal.contains(key)
193 Ok(non_normal.contains(key))
164 }
194 }
165
195
166 fn non_normal_entries_remove(&mut self, key: &HgPath) {
196 fn non_normal_entries_remove(&mut self, key: &HgPath) {
@@ -169,10 +199,11 b' impl DirstateMapMethods for DirstateMap '
169
199
170 fn non_normal_or_other_parent_paths(
200 fn non_normal_or_other_parent_paths(
171 &mut self,
201 &mut self,
172 ) -> Box<dyn Iterator<Item = &HgPath> + '_> {
202 ) -> Box<dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + '_>
203 {
173 let (non_normal, other_parent) =
204 let (non_normal, other_parent) =
174 self.get_non_normal_other_parent_entries();
205 self.get_non_normal_other_parent_entries();
175 Box::new(non_normal.union(other_parent).map(|p| &**p))
206 Box::new(non_normal.union(other_parent).map(|p| Ok(&**p)))
176 }
207 }
177
208
178 fn set_non_normal_other_parent_entries(&mut self, force: bool) {
209 fn set_non_normal_other_parent_entries(&mut self, force: bool) {
@@ -181,39 +212,42 b' impl DirstateMapMethods for DirstateMap '
181
212
182 fn iter_non_normal_paths(
213 fn iter_non_normal_paths(
183 &mut self,
214 &mut self,
184 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
215 ) -> Box<
216 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_,
217 > {
185 let (non_normal, _other_parent) =
218 let (non_normal, _other_parent) =
186 self.get_non_normal_other_parent_entries();
219 self.get_non_normal_other_parent_entries();
187 Box::new(non_normal.iter().map(|p| &**p))
220 Box::new(non_normal.iter().map(|p| Ok(&**p)))
188 }
221 }
189
222
190 fn iter_non_normal_paths_panic(
223 fn iter_non_normal_paths_panic(
191 &self,
224 &self,
192 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
225 ) -> Box<
226 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_,
227 > {
193 let (non_normal, _other_parent) =
228 let (non_normal, _other_parent) =
194 self.get_non_normal_other_parent_entries_panic();
229 self.get_non_normal_other_parent_entries_panic();
195 Box::new(non_normal.iter().map(|p| &**p))
230 Box::new(non_normal.iter().map(|p| Ok(&**p)))
196 }
231 }
197
232
198 fn iter_other_parent_paths(
233 fn iter_other_parent_paths(
199 &mut self,
234 &mut self,
200 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
235 ) -> Box<
236 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_,
237 > {
201 let (_non_normal, other_parent) =
238 let (_non_normal, other_parent) =
202 self.get_non_normal_other_parent_entries();
239 self.get_non_normal_other_parent_entries();
203 Box::new(other_parent.iter().map(|p| &**p))
240 Box::new(other_parent.iter().map(|p| Ok(&**p)))
204 }
241 }
205
242
206 fn has_tracked_dir(
243 fn has_tracked_dir(
207 &mut self,
244 &mut self,
208 directory: &HgPath,
245 directory: &HgPath,
209 ) -> Result<bool, DirstateMapError> {
246 ) -> Result<bool, DirstateError> {
210 self.has_tracked_dir(directory)
247 self.has_tracked_dir(directory)
211 }
248 }
212
249
213 fn has_dir(
250 fn has_dir(&mut self, directory: &HgPath) -> Result<bool, DirstateError> {
214 &mut self,
215 directory: &HgPath,
216 ) -> Result<bool, DirstateMapError> {
217 self.has_dir(directory)
251 self.has_dir(directory)
218 }
252 }
219
253
@@ -235,11 +269,11 b' impl DirstateMapMethods for DirstateMap '
235 )
269 )
236 }
270 }
237
271
238 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
272 fn set_all_dirs(&mut self) -> Result<(), DirstateError> {
239 self.set_all_dirs()
273 self.set_all_dirs()
240 }
274 }
241
275
242 fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
276 fn set_dirs(&mut self) -> Result<(), DirstateError> {
243 self.set_dirs()
277 self.set_dirs()
244 }
278 }
245
279
@@ -259,42 +293,61 b' impl DirstateMapMethods for DirstateMap '
259 }
293 }
260
294
261 fn copy_map_iter(&self) -> CopyMapIter<'_> {
295 fn copy_map_iter(&self) -> CopyMapIter<'_> {
262 Box::new(self.copy_map.iter().map(|(key, value)| (&**key, &**value)))
296 Box::new(
263 }
297 self.copy_map
264
298 .iter()
265 fn copy_map_contains_key(&self, key: &HgPath) -> bool {
299 .map(|(key, value)| Ok((&**key, &**value))),
266 self.copy_map.contains_key(key)
300 )
267 }
301 }
268
302
269 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPath> {
303 fn copy_map_contains_key(
270 self.copy_map.get(key).map(|p| &**p)
304 &self,
305 key: &HgPath,
306 ) -> Result<bool, DirstateV2ParseError> {
307 Ok(self.copy_map.contains_key(key))
271 }
308 }
272
309
273 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf> {
310 fn copy_map_get(
274 self.copy_map.remove(key)
311 &self,
312 key: &HgPath,
313 ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
314 Ok(self.copy_map.get(key).map(|p| &**p))
315 }
316
317 fn copy_map_remove(
318 &mut self,
319 key: &HgPath,
320 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
321 Ok(self.copy_map.remove(key))
275 }
322 }
276
323
277 fn copy_map_insert(
324 fn copy_map_insert(
278 &mut self,
325 &mut self,
279 key: HgPathBuf,
326 key: HgPathBuf,
280 value: HgPathBuf,
327 value: HgPathBuf,
281 ) -> Option<HgPathBuf> {
328 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
282 self.copy_map.insert(key, value)
329 Ok(self.copy_map.insert(key, value))
283 }
330 }
284
331
285 fn len(&self) -> usize {
332 fn len(&self) -> usize {
286 (&**self).len()
333 (&**self).len()
287 }
334 }
288
335
289 fn contains_key(&self, key: &HgPath) -> bool {
336 fn contains_key(
290 (&**self).contains_key(key)
337 &self,
338 key: &HgPath,
339 ) -> Result<bool, DirstateV2ParseError> {
340 Ok((&**self).contains_key(key))
291 }
341 }
292
342
293 fn get(&self, key: &HgPath) -> Option<DirstateEntry> {
343 fn get(
294 (&**self).get(key).cloned()
344 &self,
345 key: &HgPath,
346 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
347 Ok((&**self).get(key).cloned())
295 }
348 }
296
349
297 fn iter(&self) -> StateMapIter<'_> {
350 fn iter(&self) -> StateMapIter<'_> {
298 Box::new((&**self).iter().map(|(key, value)| (&**key, *value)))
351 Box::new((&**self).iter().map(|(key, value)| Ok((&**key, *value))))
299 }
352 }
300 }
353 }
@@ -109,7 +109,10 b' fn _static_assert_size_of() {'
109 }
109 }
110
110
111 /// Unexpected file format found in `.hg/dirstate` with the "v2" format.
111 /// Unexpected file format found in `.hg/dirstate` with the "v2" format.
112 pub(crate) struct DirstateV2ParseError;
112 ///
113 /// This should only happen if Mercurial is buggy or a repository is corrupted.
114 #[derive(Debug)]
115 pub struct DirstateV2ParseError;
113
116
114 impl From<DirstateV2ParseError> for HgError {
117 impl From<DirstateV2ParseError> for HgError {
115 fn from(_: DirstateV2ParseError) -> Self {
118 fn from(_: DirstateV2ParseError) -> Self {
@@ -295,9 +298,9 b' fn write_nodes('
295 // First accumulate serialized nodes in a `Vec`
298 // First accumulate serialized nodes in a `Vec`
296 let mut on_disk_nodes = Vec::with_capacity(nodes.len());
299 let mut on_disk_nodes = Vec::with_capacity(nodes.len());
297 for node in nodes {
300 for node in nodes {
298 let children = write_nodes(node.children(), out)?;
301 let children = write_nodes(node.children()?, out)?;
299 let full_path = write_slice::<u8>(node.full_path().as_bytes(), out);
302 let full_path = write_slice::<u8>(node.full_path()?.as_bytes(), out);
300 let copy_source = if let Some(source) = node.copy_source() {
303 let copy_source = if let Some(source) = node.copy_source()? {
301 write_slice::<u8>(source.as_bytes(), out)
304 write_slice::<u8>(source.as_bytes(), out)
302 } else {
305 } else {
303 Slice {
306 Slice {
@@ -2,6 +2,7 b' use crate::dirstate::status::IgnoreFnTyp'
2 use crate::dirstate_tree::dirstate_map::ChildNodesRef;
2 use crate::dirstate_tree::dirstate_map::ChildNodesRef;
3 use crate::dirstate_tree::dirstate_map::DirstateMap;
3 use crate::dirstate_tree::dirstate_map::DirstateMap;
4 use crate::dirstate_tree::dirstate_map::NodeRef;
4 use crate::dirstate_tree::dirstate_map::NodeRef;
5 use crate::dirstate_tree::on_disk::DirstateV2ParseError;
5 use crate::matchers::get_ignore_function;
6 use crate::matchers::get_ignore_function;
6 use crate::matchers::Matcher;
7 use crate::matchers::Matcher;
7 use crate::utils::files::get_bytes_from_os_string;
8 use crate::utils::files::get_bytes_from_os_string;
@@ -60,7 +61,7 b" pub fn status<'tree>("
60 hg_path,
61 hg_path,
61 &root_dir,
62 &root_dir,
62 is_at_repo_root,
63 is_at_repo_root,
63 );
64 )?;
64 Ok((common.outcome.into_inner().unwrap(), warnings))
65 Ok((common.outcome.into_inner().unwrap(), warnings))
65 }
66 }
66
67
@@ -97,7 +98,7 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
97 directory_hg_path: &'tree HgPath,
98 directory_hg_path: &'tree HgPath,
98 directory_fs_path: &Path,
99 directory_fs_path: &Path,
99 is_at_repo_root: bool,
100 is_at_repo_root: bool,
100 ) {
101 ) -> Result<(), DirstateV2ParseError> {
101 let mut fs_entries = if let Ok(entries) = self.read_dir(
102 let mut fs_entries = if let Ok(entries) = self.read_dir(
102 directory_hg_path,
103 directory_hg_path,
103 directory_fs_path,
104 directory_fs_path,
@@ -105,7 +106,7 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
105 ) {
106 ) {
106 entries
107 entries
107 } else {
108 } else {
108 return;
109 return Ok(());
109 };
110 };
110
111
111 // `merge_join_by` requires both its input iterators to be sorted:
112 // `merge_join_by` requires both its input iterators to be sorted:
@@ -115,34 +116,41 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
115 // https://github.com/rust-lang/rust/issues/34162
116 // https://github.com/rust-lang/rust/issues/34162
116 fs_entries.sort_unstable_by(|e1, e2| e1.base_name.cmp(&e2.base_name));
117 fs_entries.sort_unstable_by(|e1, e2| e1.base_name.cmp(&e2.base_name));
117
118
119 // Propagate here any error that would happen inside the comparison
120 // callback below
121 for dirstate_node in &dirstate_nodes {
122 dirstate_node.base_name()?;
123 }
118 itertools::merge_join_by(
124 itertools::merge_join_by(
119 dirstate_nodes,
125 dirstate_nodes,
120 &fs_entries,
126 &fs_entries,
121 |dirstate_node, fs_entry| {
127 |dirstate_node, fs_entry| {
122 dirstate_node.base_name().cmp(&fs_entry.base_name)
128 // This `unwrap` never panics because we already propagated
129 // those errors above
130 dirstate_node.base_name().unwrap().cmp(&fs_entry.base_name)
123 },
131 },
124 )
132 )
125 .par_bridge()
133 .par_bridge()
126 .for_each(|pair| {
134 .map(|pair| {
127 use itertools::EitherOrBoth::*;
135 use itertools::EitherOrBoth::*;
128 match pair {
136 match pair {
129 Both(dirstate_node, fs_entry) => {
137 Both(dirstate_node, fs_entry) => self
130 self.traverse_fs_and_dirstate(
138 .traverse_fs_and_dirstate(
131 fs_entry,
139 fs_entry,
132 dirstate_node,
140 dirstate_node,
133 has_ignored_ancestor,
141 has_ignored_ancestor,
134 );
142 ),
135 }
136 Left(dirstate_node) => {
143 Left(dirstate_node) => {
137 self.traverse_dirstate_only(dirstate_node)
144 self.traverse_dirstate_only(dirstate_node)
138 }
145 }
139 Right(fs_entry) => self.traverse_fs_only(
146 Right(fs_entry) => Ok(self.traverse_fs_only(
140 has_ignored_ancestor,
147 has_ignored_ancestor,
141 directory_hg_path,
148 directory_hg_path,
142 fs_entry,
149 fs_entry,
143 ),
150 )),
144 }
151 }
145 })
152 })
153 .collect()
146 }
154 }
147
155
148 fn traverse_fs_and_dirstate(
156 fn traverse_fs_and_dirstate(
@@ -150,8 +158,8 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
150 fs_entry: &DirEntry,
158 fs_entry: &DirEntry,
151 dirstate_node: NodeRef<'tree, '_>,
159 dirstate_node: NodeRef<'tree, '_>,
152 has_ignored_ancestor: bool,
160 has_ignored_ancestor: bool,
153 ) {
161 ) -> Result<(), DirstateV2ParseError> {
154 let hg_path = dirstate_node.full_path();
162 let hg_path = dirstate_node.full_path()?;
155 let file_type = fs_entry.metadata.file_type();
163 let file_type = fs_entry.metadata.file_type();
156 let file_or_symlink = file_type.is_file() || file_type.is_symlink();
164 let file_or_symlink = file_type.is_file() || file_type.is_symlink();
157 if !file_or_symlink {
165 if !file_or_symlink {
@@ -159,8 +167,8 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
159 // `hg rm` or similar) or deleted before it could be
167 // `hg rm` or similar) or deleted before it could be
160 // replaced by a directory or something else.
168 // replaced by a directory or something else.
161 self.mark_removed_or_deleted_if_file(
169 self.mark_removed_or_deleted_if_file(
162 dirstate_node.full_path(),
170 hg_path,
163 dirstate_node.state(),
171 dirstate_node.state()?,
164 );
172 );
165 }
173 }
166 if file_type.is_dir() {
174 if file_type.is_dir() {
@@ -171,15 +179,15 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
171 let is_at_repo_root = false;
179 let is_at_repo_root = false;
172 self.traverse_fs_directory_and_dirstate(
180 self.traverse_fs_directory_and_dirstate(
173 is_ignored,
181 is_ignored,
174 dirstate_node.children(),
182 dirstate_node.children()?,
175 hg_path,
183 hg_path,
176 &fs_entry.full_path,
184 &fs_entry.full_path,
177 is_at_repo_root,
185 is_at_repo_root,
178 );
186 )?
179 } else {
187 } else {
180 if file_or_symlink && self.matcher.matches(hg_path) {
188 if file_or_symlink && self.matcher.matches(hg_path) {
181 let full_path = Cow::from(hg_path);
189 let full_path = Cow::from(hg_path);
182 if let Some(state) = dirstate_node.state() {
190 if let Some(state) = dirstate_node.state()? {
183 match state {
191 match state {
184 EntryState::Added => {
192 EntryState::Added => {
185 self.outcome.lock().unwrap().added.push(full_path)
193 self.outcome.lock().unwrap().added.push(full_path)
@@ -197,7 +205,7 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
197 .modified
205 .modified
198 .push(full_path),
206 .push(full_path),
199 EntryState::Normal => {
207 EntryState::Normal => {
200 self.handle_normal_file(&dirstate_node, fs_entry);
208 self.handle_normal_file(&dirstate_node, fs_entry)?
201 }
209 }
202 // This variant is not used in DirstateMap
210 // This variant is not used in DirstateMap
203 // nodes
211 // nodes
@@ -213,10 +221,11 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
213 }
221 }
214 }
222 }
215
223
216 for child_node in dirstate_node.children().iter() {
224 for child_node in dirstate_node.children()?.iter() {
217 self.traverse_dirstate_only(child_node)
225 self.traverse_dirstate_only(child_node)?
218 }
226 }
219 }
227 }
228 Ok(())
220 }
229 }
221
230
222 /// A file with `EntryState::Normal` in the dirstate was found in the
231 /// A file with `EntryState::Normal` in the dirstate was found in the
@@ -225,7 +234,7 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
225 &self,
234 &self,
226 dirstate_node: &NodeRef<'tree, '_>,
235 dirstate_node: &NodeRef<'tree, '_>,
227 fs_entry: &DirEntry,
236 fs_entry: &DirEntry,
228 ) {
237 ) -> Result<(), DirstateV2ParseError> {
229 // Keep the low 31 bits
238 // Keep the low 31 bits
230 fn truncate_u64(value: u64) -> i32 {
239 fn truncate_u64(value: u64) -> i32 {
231 (value & 0x7FFF_FFFF) as i32
240 (value & 0x7FFF_FFFF) as i32
@@ -235,9 +244,9 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
235 }
244 }
236
245
237 let entry = dirstate_node
246 let entry = dirstate_node
238 .entry()
247 .entry()?
239 .expect("handle_normal_file called with entry-less node");
248 .expect("handle_normal_file called with entry-less node");
240 let full_path = Cow::from(dirstate_node.full_path());
249 let full_path = Cow::from(dirstate_node.full_path()?);
241 let mode_changed = || {
250 let mode_changed = || {
242 self.options.check_exec && entry.mode_changed(&fs_entry.metadata)
251 self.options.check_exec && entry.mode_changed(&fs_entry.metadata)
243 };
252 };
@@ -249,7 +258,7 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
249 // issue6456: Size returned may be longer due to encryption
258 // issue6456: Size returned may be longer due to encryption
250 // on EXT-4 fscrypt. TODO maybe only do it on EXT4?
259 // on EXT-4 fscrypt. TODO maybe only do it on EXT4?
251 self.outcome.lock().unwrap().unsure.push(full_path)
260 self.outcome.lock().unwrap().unsure.push(full_path)
252 } else if dirstate_node.copy_source().is_some()
261 } else if dirstate_node.has_copy_source()
253 || entry.is_from_other_parent()
262 || entry.is_from_other_parent()
254 || (entry.size >= 0 && (size_changed || mode_changed()))
263 || (entry.size >= 0 && (size_changed || mode_changed()))
255 {
264 {
@@ -264,18 +273,23 b" impl<'tree, 'a> StatusCommon<'tree, 'a> "
264 self.outcome.lock().unwrap().clean.push(full_path)
273 self.outcome.lock().unwrap().clean.push(full_path)
265 }
274 }
266 }
275 }
276 Ok(())
267 }
277 }
268
278
269 /// A node in the dirstate tree has no corresponding filesystem entry
279 /// A node in the dirstate tree has no corresponding filesystem entry
270 fn traverse_dirstate_only(&self, dirstate_node: NodeRef<'tree, '_>) {
280 fn traverse_dirstate_only(
281 &self,
282 dirstate_node: NodeRef<'tree, '_>,
283 ) -> Result<(), DirstateV2ParseError> {
271 self.mark_removed_or_deleted_if_file(
284 self.mark_removed_or_deleted_if_file(
272 dirstate_node.full_path(),
285 dirstate_node.full_path()?,
273 dirstate_node.state(),
286 dirstate_node.state()?,
274 );
287 );
275 dirstate_node
288 dirstate_node
276 .children()
289 .children()?
277 .par_iter()
290 .par_iter()
278 .for_each(|child_node| self.traverse_dirstate_only(child_node))
291 .map(|child_node| self.traverse_dirstate_only(child_node))
292 .collect()
279 }
293 }
280
294
281 /// A node in the dirstate tree has no corresponding *file* on the
295 /// A node in the dirstate tree has no corresponding *file* on the
@@ -83,6 +83,15 b' pub enum DirstateError {'
83 Common(errors::HgError),
83 Common(errors::HgError),
84 }
84 }
85
85
86 impl fmt::Display for DirstateError {
87 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
88 match self {
89 DirstateError::Map(error) => error.fmt(f),
90 DirstateError::Common(error) => error.fmt(f),
91 }
92 }
93 }
94
86 #[derive(Debug, derive_more::From)]
95 #[derive(Debug, derive_more::From)]
87 pub enum PatternError {
96 pub enum PatternError {
88 #[from]
97 #[from]
@@ -13,7 +13,9 b' use cpython::{'
13 };
13 };
14 use std::cell::RefCell;
14 use std::cell::RefCell;
15
15
16 use crate::dirstate::dirstate_map::v2_error;
16 use crate::dirstate::dirstate_map::DirstateMap;
17 use crate::dirstate::dirstate_map::DirstateMap;
18 use hg::dirstate_tree::on_disk::DirstateV2ParseError;
17 use hg::utils::hg_path::HgPath;
19 use hg::utils::hg_path::HgPath;
18 use hg::CopyMapIter;
20 use hg::CopyMapIter;
19
21
@@ -88,15 +90,16 b' impl CopyMap {'
88 }
90 }
89 fn translate_key(
91 fn translate_key(
90 py: Python,
92 py: Python,
91 res: (&HgPath, &HgPath),
93 res: Result<(&HgPath, &HgPath), DirstateV2ParseError>,
92 ) -> PyResult<Option<PyBytes>> {
94 ) -> PyResult<Option<PyBytes>> {
93 Ok(Some(PyBytes::new(py, res.0.as_bytes())))
95 let (k, _v) = res.map_err(|e| v2_error(py, e))?;
96 Ok(Some(PyBytes::new(py, k.as_bytes())))
94 }
97 }
95 fn translate_key_value(
98 fn translate_key_value(
96 py: Python,
99 py: Python,
97 res: (&HgPath, &HgPath),
100 res: Result<(&HgPath, &HgPath), DirstateV2ParseError>,
98 ) -> PyResult<Option<(PyBytes, PyBytes)>> {
101 ) -> PyResult<Option<(PyBytes, PyBytes)>> {
99 let (k, v) = res;
102 let (k, v) = res.map_err(|e| v2_error(py, e))?;
100 Ok(Some((
103 Ok(Some((
101 PyBytes::new(py, k.as_bytes()),
104 PyBytes::new(py, k.as_bytes()),
102 PyBytes::new(py, v.as_bytes()),
105 PyBytes::new(py, v.as_bytes()),
@@ -20,7 +20,8 b' use crate::dirstate::extract_dirstate;'
20 use hg::{
20 use hg::{
21 errors::HgError,
21 errors::HgError,
22 utils::hg_path::{HgPath, HgPathBuf},
22 utils::hg_path::{HgPath, HgPathBuf},
23 DirsMultiset, DirsMultisetIter, DirstateMapError, EntryState,
23 DirsMultiset, DirsMultisetIter, DirstateError, DirstateMapError,
24 EntryState,
24 };
25 };
25
26
26 py_class!(pub class Dirs |py| {
27 py_class!(pub class Dirs |py| {
@@ -45,9 +46,9 b' py_class!(pub class Dirs |py| {'
45 }
46 }
46 let inner = if let Ok(map) = map.cast_as::<PyDict>(py) {
47 let inner = if let Ok(map) = map.cast_as::<PyDict>(py) {
47 let dirstate = extract_dirstate(py, &map)?;
48 let dirstate = extract_dirstate(py, &map)?;
48 let dirstate = dirstate.iter().map(|(k, v)| (k, *v));
49 let dirstate = dirstate.iter().map(|(k, v)| Ok((k, *v)));
49 DirsMultiset::from_dirstate(dirstate, skip_state)
50 DirsMultiset::from_dirstate(dirstate, skip_state)
50 .map_err(|e: DirstateMapError| {
51 .map_err(|e: DirstateError| {
51 PyErr::new::<exc::ValueError, _>(py, e.to_string())
52 PyErr::new::<exc::ValueError, _>(py, e.to_string())
52 })?
53 })?
53 } else {
54 } else {
@@ -29,13 +29,13 b' use crate::{'
29 use hg::{
29 use hg::{
30 dirstate::parsers::Timestamp,
30 dirstate::parsers::Timestamp,
31 dirstate_tree::dispatch::DirstateMapMethods,
31 dirstate_tree::dispatch::DirstateMapMethods,
32 dirstate_tree::on_disk::DirstateV2ParseError,
32 errors::HgError,
33 errors::HgError,
33 revlog::Node,
34 revlog::Node,
34 utils::files::normalize_case,
35 utils::files::normalize_case,
35 utils::hg_path::{HgPath, HgPathBuf},
36 utils::hg_path::{HgPath, HgPathBuf},
36 DirsMultiset, DirstateEntry, DirstateError,
37 DirsMultiset, DirstateEntry, DirstateError,
37 DirstateMap as RustDirstateMap, DirstateMapError, DirstateParents,
38 DirstateMap as RustDirstateMap, DirstateParents, EntryState, StateMapIter,
38 EntryState, StateMapIter,
39 };
39 };
40
40
41 // TODO
41 // TODO
@@ -90,7 +90,12 b' py_class!(pub class DirstateMap |py| {'
90 default: Option<PyObject> = None
90 default: Option<PyObject> = None
91 ) -> PyResult<Option<PyObject>> {
91 ) -> PyResult<Option<PyObject>> {
92 let key = key.extract::<PyBytes>(py)?;
92 let key = key.extract::<PyBytes>(py)?;
93 match self.inner(py).borrow().get(HgPath::new(key.data(py))) {
93 match self
94 .inner(py)
95 .borrow()
96 .get(HgPath::new(key.data(py)))
97 .map_err(|e| v2_error(py, e))?
98 {
94 Some(entry) => {
99 Some(entry) => {
95 Ok(Some(make_dirstate_tuple(py, &entry)?))
100 Ok(Some(make_dirstate_tuple(py, &entry)?))
96 },
101 },
@@ -124,7 +129,7 b' py_class!(pub class DirstateMap |py| {'
124 size: size.extract(py)?,
129 size: size.extract(py)?,
125 mtime: mtime.extract(py)?,
130 mtime: mtime.extract(py)?,
126 },
131 },
127 ).and(Ok(py.None())).or_else(|e: DirstateMapError| {
132 ).and(Ok(py.None())).or_else(|e: DirstateError| {
128 Err(PyErr::new::<exc::ValueError, _>(py, e.to_string()))
133 Err(PyErr::new::<exc::ValueError, _>(py, e.to_string()))
129 })
134 })
130 }
135 }
@@ -190,8 +195,10 b' py_class!(pub class DirstateMap |py| {'
190 ))
195 ))
191 })
196 })
192 .collect();
197 .collect();
193 self.inner(py).borrow_mut()
198 self.inner(py)
194 .clear_ambiguous_times(files?, now.extract(py)?);
199 .borrow_mut()
200 .clear_ambiguous_times(files?, now.extract(py)?)
201 .map_err(|e| v2_error(py, e))?;
195 Ok(py.None())
202 Ok(py.None())
196 }
203 }
197
204
@@ -199,6 +206,7 b' py_class!(pub class DirstateMap |py| {'
199 let mut inner_shared = self.inner(py).borrow_mut();
206 let mut inner_shared = self.inner(py).borrow_mut();
200 let set = PySet::empty(py)?;
207 let set = PySet::empty(py)?;
201 for path in inner_shared.iter_other_parent_paths() {
208 for path in inner_shared.iter_other_parent_paths() {
209 let path = path.map_err(|e| v2_error(py, e))?;
202 set.add(py, PyBytes::new(py, path.as_bytes()))?;
210 set.add(py, PyBytes::new(py, path.as_bytes()))?;
203 }
211 }
204 Ok(set.into_object())
212 Ok(set.into_object())
@@ -210,28 +218,20 b' py_class!(pub class DirstateMap |py| {'
210
218
211 def non_normal_entries_contains(&self, key: PyObject) -> PyResult<bool> {
219 def non_normal_entries_contains(&self, key: PyObject) -> PyResult<bool> {
212 let key = key.extract::<PyBytes>(py)?;
220 let key = key.extract::<PyBytes>(py)?;
213 Ok(self
221 self.inner(py)
214 .inner(py)
215 .borrow_mut()
222 .borrow_mut()
216 .non_normal_entries_contains(HgPath::new(key.data(py))))
223 .non_normal_entries_contains(HgPath::new(key.data(py)))
224 .map_err(|e| v2_error(py, e))
217 }
225 }
218
226
219 def non_normal_entries_display(&self) -> PyResult<PyString> {
227 def non_normal_entries_display(&self) -> PyResult<PyString> {
220 Ok(
228 let mut inner = self.inner(py).borrow_mut();
221 PyString::new(
229 let paths = inner
222 py,
230 .iter_non_normal_paths()
223 &format!(
231 .collect::<Result<Vec<_>, _>>()
224 "NonNormalEntries: {}",
232 .map_err(|e| v2_error(py, e))?;
225 hg::utils::join_display(
233 let formatted = format!("NonNormalEntries: {}", hg::utils::join_display(paths, ", "));
226 self
234 Ok(PyString::new(py, &formatted))
227 .inner(py)
228 .borrow_mut()
229 .iter_non_normal_paths(),
230 ", "
231 )
232 )
233 )
234 )
235 }
235 }
236
236
237 def non_normal_entries_remove(&self, key: PyObject) -> PyResult<PyObject> {
237 def non_normal_entries_remove(&self, key: PyObject) -> PyResult<PyObject> {
@@ -248,6 +248,7 b' py_class!(pub class DirstateMap |py| {'
248
248
249 let ret = PyList::new(py, &[]);
249 let ret = PyList::new(py, &[]);
250 for filename in inner.non_normal_or_other_parent_paths() {
250 for filename in inner.non_normal_or_other_parent_paths() {
251 let filename = filename.map_err(|e| v2_error(py, e))?;
251 let as_pystring = PyBytes::new(py, filename.as_bytes());
252 let as_pystring = PyBytes::new(py, filename.as_bytes());
252 ret.append(py, as_pystring.into_object());
253 ret.append(py, as_pystring.into_object());
253 }
254 }
@@ -320,7 +321,8 b' py_class!(pub class DirstateMap |py| {'
320
321
321 def filefoldmapasdict(&self) -> PyResult<PyDict> {
322 def filefoldmapasdict(&self) -> PyResult<PyDict> {
322 let dict = PyDict::new(py);
323 let dict = PyDict::new(py);
323 for (path, entry) in self.inner(py).borrow_mut().iter() {
324 for item in self.inner(py).borrow_mut().iter() {
325 let (path, entry) = item.map_err(|e| v2_error(py, e))?;
324 if entry.state != EntryState::Removed {
326 if entry.state != EntryState::Removed {
325 let key = normalize_case(path);
327 let key = normalize_case(path);
326 let value = path;
328 let value = path;
@@ -340,13 +342,21 b' py_class!(pub class DirstateMap |py| {'
340
342
341 def __contains__(&self, key: PyObject) -> PyResult<bool> {
343 def __contains__(&self, key: PyObject) -> PyResult<bool> {
342 let key = key.extract::<PyBytes>(py)?;
344 let key = key.extract::<PyBytes>(py)?;
343 Ok(self.inner(py).borrow().contains_key(HgPath::new(key.data(py))))
345 self.inner(py)
346 .borrow()
347 .contains_key(HgPath::new(key.data(py)))
348 .map_err(|e| v2_error(py, e))
344 }
349 }
345
350
346 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
351 def __getitem__(&self, key: PyObject) -> PyResult<PyObject> {
347 let key = key.extract::<PyBytes>(py)?;
352 let key = key.extract::<PyBytes>(py)?;
348 let key = HgPath::new(key.data(py));
353 let key = HgPath::new(key.data(py));
349 match self.inner(py).borrow().get(key) {
354 match self
355 .inner(py)
356 .borrow()
357 .get(key)
358 .map_err(|e| v2_error(py, e))?
359 {
350 Some(entry) => {
360 Some(entry) => {
351 Ok(make_dirstate_tuple(py, &entry)?)
361 Ok(make_dirstate_tuple(py, &entry)?)
352 },
362 },
@@ -418,7 +428,8 b' py_class!(pub class DirstateMap |py| {'
418 // TODO all copymap* methods, see docstring above
428 // TODO all copymap* methods, see docstring above
419 def copymapcopy(&self) -> PyResult<PyDict> {
429 def copymapcopy(&self) -> PyResult<PyDict> {
420 let dict = PyDict::new(py);
430 let dict = PyDict::new(py);
421 for (key, value) in self.inner(py).borrow().copy_map_iter() {
431 for item in self.inner(py).borrow().copy_map_iter() {
432 let (key, value) = item.map_err(|e| v2_error(py, e))?;
422 dict.set_item(
433 dict.set_item(
423 py,
434 py,
424 PyBytes::new(py, key.as_bytes()),
435 PyBytes::new(py, key.as_bytes()),
@@ -430,7 +441,12 b' py_class!(pub class DirstateMap |py| {'
430
441
431 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
442 def copymapgetitem(&self, key: PyObject) -> PyResult<PyBytes> {
432 let key = key.extract::<PyBytes>(py)?;
443 let key = key.extract::<PyBytes>(py)?;
433 match self.inner(py).borrow().copy_map_get(HgPath::new(key.data(py))) {
444 match self
445 .inner(py)
446 .borrow()
447 .copy_map_get(HgPath::new(key.data(py)))
448 .map_err(|e| v2_error(py, e))?
449 {
434 Some(copy) => Ok(PyBytes::new(py, copy.as_bytes())),
450 Some(copy) => Ok(PyBytes::new(py, copy.as_bytes())),
435 None => Err(PyErr::new::<exc::KeyError, _>(
451 None => Err(PyErr::new::<exc::KeyError, _>(
436 py,
452 py,
@@ -447,10 +463,10 b' py_class!(pub class DirstateMap |py| {'
447 }
463 }
448 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
464 def copymapcontains(&self, key: PyObject) -> PyResult<bool> {
449 let key = key.extract::<PyBytes>(py)?;
465 let key = key.extract::<PyBytes>(py)?;
450 Ok(self
466 self.inner(py)
451 .inner(py)
452 .borrow()
467 .borrow()
453 .copy_map_contains_key(HgPath::new(key.data(py))))
468 .copy_map_contains_key(HgPath::new(key.data(py)))
469 .map_err(|e| v2_error(py, e))
454 }
470 }
455 def copymapget(
471 def copymapget(
456 &self,
472 &self,
@@ -462,6 +478,7 b' py_class!(pub class DirstateMap |py| {'
462 .inner(py)
478 .inner(py)
463 .borrow()
479 .borrow()
464 .copy_map_get(HgPath::new(key.data(py)))
480 .copy_map_get(HgPath::new(key.data(py)))
481 .map_err(|e| v2_error(py, e))?
465 {
482 {
466 Some(copy) => Ok(Some(
483 Some(copy) => Ok(Some(
467 PyBytes::new(py, copy.as_bytes()).into_object(),
484 PyBytes::new(py, copy.as_bytes()).into_object(),
@@ -476,10 +493,13 b' py_class!(pub class DirstateMap |py| {'
476 ) -> PyResult<PyObject> {
493 ) -> PyResult<PyObject> {
477 let key = key.extract::<PyBytes>(py)?;
494 let key = key.extract::<PyBytes>(py)?;
478 let value = value.extract::<PyBytes>(py)?;
495 let value = value.extract::<PyBytes>(py)?;
479 self.inner(py).borrow_mut().copy_map_insert(
496 self.inner(py)
480 HgPathBuf::from_bytes(key.data(py)),
497 .borrow_mut()
481 HgPathBuf::from_bytes(value.data(py)),
498 .copy_map_insert(
482 );
499 HgPathBuf::from_bytes(key.data(py)),
500 HgPathBuf::from_bytes(value.data(py)),
501 )
502 .map_err(|e| v2_error(py, e))?;
483 Ok(py.None())
503 Ok(py.None())
484 }
504 }
485 def copymappop(
505 def copymappop(
@@ -492,6 +512,7 b' py_class!(pub class DirstateMap |py| {'
492 .inner(py)
512 .inner(py)
493 .borrow_mut()
513 .borrow_mut()
494 .copy_map_remove(HgPath::new(key.data(py)))
514 .copy_map_remove(HgPath::new(key.data(py)))
515 .map_err(|e| v2_error(py, e))?
495 {
516 {
496 Some(_) => Ok(None),
517 Some(_) => Ok(None),
497 None => Ok(default),
518 None => Ok(default),
@@ -525,15 +546,16 b' impl DirstateMap {'
525 }
546 }
526 fn translate_key(
547 fn translate_key(
527 py: Python,
548 py: Python,
528 res: (&HgPath, DirstateEntry),
549 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
529 ) -> PyResult<Option<PyBytes>> {
550 ) -> PyResult<Option<PyBytes>> {
530 Ok(Some(PyBytes::new(py, res.0.as_bytes())))
551 let (f, _entry) = res.map_err(|e| v2_error(py, e))?;
552 Ok(Some(PyBytes::new(py, f.as_bytes())))
531 }
553 }
532 fn translate_key_value(
554 fn translate_key_value(
533 py: Python,
555 py: Python,
534 res: (&HgPath, DirstateEntry),
556 res: Result<(&HgPath, DirstateEntry), DirstateV2ParseError>,
535 ) -> PyResult<Option<(PyBytes, PyObject)>> {
557 ) -> PyResult<Option<(PyBytes, PyObject)>> {
536 let (f, entry) = res;
558 let (f, entry) = res.map_err(|e| v2_error(py, e))?;
537 Ok(Some((
559 Ok(Some((
538 PyBytes::new(py, f.as_bytes()),
560 PyBytes::new(py, f.as_bytes()),
539 make_dirstate_tuple(py, &entry)?,
561 make_dirstate_tuple(py, &entry)?,
@@ -562,3 +584,7 b' fn extract_node_id(py: Python, obj: &PyO'
562 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
584 Err(e) => Err(PyErr::new::<exc::ValueError, _>(py, e.to_string())),
563 }
585 }
564 }
586 }
587
588 pub(super) fn v2_error(py: Python<'_>, _: DirstateV2ParseError) -> PyErr {
589 PyErr::new::<exc::ValueError, _>(py, "corrupted dirstate-v2")
590 }
@@ -1,12 +1,12 b''
1 use crate::dirstate::owning::OwningDirstateMap;
1 use crate::dirstate::owning::OwningDirstateMap;
2 use hg::dirstate::parsers::Timestamp;
2 use hg::dirstate::parsers::Timestamp;
3 use hg::dirstate_tree::dispatch::DirstateMapMethods;
3 use hg::dirstate_tree::dispatch::DirstateMapMethods;
4 use hg::dirstate_tree::on_disk::DirstateV2ParseError;
4 use hg::matchers::Matcher;
5 use hg::matchers::Matcher;
5 use hg::utils::hg_path::{HgPath, HgPathBuf};
6 use hg::utils::hg_path::{HgPath, HgPathBuf};
6 use hg::CopyMapIter;
7 use hg::CopyMapIter;
7 use hg::DirstateEntry;
8 use hg::DirstateEntry;
8 use hg::DirstateError;
9 use hg::DirstateError;
9 use hg::DirstateMapError;
10 use hg::DirstateParents;
10 use hg::DirstateParents;
11 use hg::DirstateStatus;
11 use hg::DirstateStatus;
12 use hg::EntryState;
12 use hg::EntryState;
@@ -26,7 +26,7 b' impl DirstateMapMethods for OwningDirsta'
26 filename: &HgPath,
26 filename: &HgPath,
27 old_state: EntryState,
27 old_state: EntryState,
28 entry: DirstateEntry,
28 entry: DirstateEntry,
29 ) -> Result<(), DirstateMapError> {
29 ) -> Result<(), DirstateError> {
30 self.get_mut().add_file(filename, old_state, entry)
30 self.get_mut().add_file(filename, old_state, entry)
31 }
31 }
32
32
@@ -35,7 +35,7 b' impl DirstateMapMethods for OwningDirsta'
35 filename: &HgPath,
35 filename: &HgPath,
36 old_state: EntryState,
36 old_state: EntryState,
37 size: i32,
37 size: i32,
38 ) -> Result<(), DirstateMapError> {
38 ) -> Result<(), DirstateError> {
39 self.get_mut().remove_file(filename, old_state, size)
39 self.get_mut().remove_file(filename, old_state, size)
40 }
40 }
41
41
@@ -43,15 +43,22 b' impl DirstateMapMethods for OwningDirsta'
43 &mut self,
43 &mut self,
44 filename: &HgPath,
44 filename: &HgPath,
45 old_state: EntryState,
45 old_state: EntryState,
46 ) -> Result<bool, DirstateMapError> {
46 ) -> Result<bool, DirstateError> {
47 self.get_mut().drop_file(filename, old_state)
47 self.get_mut().drop_file(filename, old_state)
48 }
48 }
49
49
50 fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32) {
50 fn clear_ambiguous_times(
51 &mut self,
52 filenames: Vec<HgPathBuf>,
53 now: i32,
54 ) -> Result<(), DirstateV2ParseError> {
51 self.get_mut().clear_ambiguous_times(filenames, now)
55 self.get_mut().clear_ambiguous_times(filenames, now)
52 }
56 }
53
57
54 fn non_normal_entries_contains(&mut self, key: &HgPath) -> bool {
58 fn non_normal_entries_contains(
59 &mut self,
60 key: &HgPath,
61 ) -> Result<bool, DirstateV2ParseError> {
55 self.get_mut().non_normal_entries_contains(key)
62 self.get_mut().non_normal_entries_contains(key)
56 }
63 }
57
64
@@ -61,7 +68,8 b' impl DirstateMapMethods for OwningDirsta'
61
68
62 fn non_normal_or_other_parent_paths(
69 fn non_normal_or_other_parent_paths(
63 &mut self,
70 &mut self,
64 ) -> Box<dyn Iterator<Item = &HgPath> + '_> {
71 ) -> Box<dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + '_>
72 {
65 self.get_mut().non_normal_or_other_parent_paths()
73 self.get_mut().non_normal_or_other_parent_paths()
66 }
74 }
67
75
@@ -71,33 +79,36 b' impl DirstateMapMethods for OwningDirsta'
71
79
72 fn iter_non_normal_paths(
80 fn iter_non_normal_paths(
73 &mut self,
81 &mut self,
74 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
82 ) -> Box<
83 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_,
84 > {
75 self.get_mut().iter_non_normal_paths()
85 self.get_mut().iter_non_normal_paths()
76 }
86 }
77
87
78 fn iter_non_normal_paths_panic(
88 fn iter_non_normal_paths_panic(
79 &self,
89 &self,
80 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
90 ) -> Box<
91 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_,
92 > {
81 self.get().iter_non_normal_paths_panic()
93 self.get().iter_non_normal_paths_panic()
82 }
94 }
83
95
84 fn iter_other_parent_paths(
96 fn iter_other_parent_paths(
85 &mut self,
97 &mut self,
86 ) -> Box<dyn Iterator<Item = &HgPath> + Send + '_> {
98 ) -> Box<
99 dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + Send + '_,
100 > {
87 self.get_mut().iter_other_parent_paths()
101 self.get_mut().iter_other_parent_paths()
88 }
102 }
89
103
90 fn has_tracked_dir(
104 fn has_tracked_dir(
91 &mut self,
105 &mut self,
92 directory: &HgPath,
106 directory: &HgPath,
93 ) -> Result<bool, DirstateMapError> {
107 ) -> Result<bool, DirstateError> {
94 self.get_mut().has_tracked_dir(directory)
108 self.get_mut().has_tracked_dir(directory)
95 }
109 }
96
110
97 fn has_dir(
111 fn has_dir(&mut self, directory: &HgPath) -> Result<bool, DirstateError> {
98 &mut self,
99 directory: &HgPath,
100 ) -> Result<bool, DirstateMapError> {
101 self.get_mut().has_dir(directory)
112 self.get_mut().has_dir(directory)
102 }
113 }
103
114
@@ -117,11 +128,11 b' impl DirstateMapMethods for OwningDirsta'
117 self.get_mut().pack_v2(parents, now)
128 self.get_mut().pack_v2(parents, now)
118 }
129 }
119
130
120 fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
131 fn set_all_dirs(&mut self) -> Result<(), DirstateError> {
121 self.get_mut().set_all_dirs()
132 self.get_mut().set_all_dirs()
122 }
133 }
123
134
124 fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
135 fn set_dirs(&mut self) -> Result<(), DirstateError> {
125 self.get_mut().set_dirs()
136 self.get_mut().set_dirs()
126 }
137 }
127
138
@@ -145,15 +156,24 b' impl DirstateMapMethods for OwningDirsta'
145 self.get().copy_map_iter()
156 self.get().copy_map_iter()
146 }
157 }
147
158
148 fn copy_map_contains_key(&self, key: &HgPath) -> bool {
159 fn copy_map_contains_key(
160 &self,
161 key: &HgPath,
162 ) -> Result<bool, DirstateV2ParseError> {
149 self.get().copy_map_contains_key(key)
163 self.get().copy_map_contains_key(key)
150 }
164 }
151
165
152 fn copy_map_get(&self, key: &HgPath) -> Option<&HgPath> {
166 fn copy_map_get(
167 &self,
168 key: &HgPath,
169 ) -> Result<Option<&HgPath>, DirstateV2ParseError> {
153 self.get().copy_map_get(key)
170 self.get().copy_map_get(key)
154 }
171 }
155
172
156 fn copy_map_remove(&mut self, key: &HgPath) -> Option<HgPathBuf> {
173 fn copy_map_remove(
174 &mut self,
175 key: &HgPath,
176 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
157 self.get_mut().copy_map_remove(key)
177 self.get_mut().copy_map_remove(key)
158 }
178 }
159
179
@@ -161,7 +181,7 b' impl DirstateMapMethods for OwningDirsta'
161 &mut self,
181 &mut self,
162 key: HgPathBuf,
182 key: HgPathBuf,
163 value: HgPathBuf,
183 value: HgPathBuf,
164 ) -> Option<HgPathBuf> {
184 ) -> Result<Option<HgPathBuf>, DirstateV2ParseError> {
165 self.get_mut().copy_map_insert(key, value)
185 self.get_mut().copy_map_insert(key, value)
166 }
186 }
167
187
@@ -169,11 +189,17 b' impl DirstateMapMethods for OwningDirsta'
169 self.get().len()
189 self.get().len()
170 }
190 }
171
191
172 fn contains_key(&self, key: &HgPath) -> bool {
192 fn contains_key(
193 &self,
194 key: &HgPath,
195 ) -> Result<bool, DirstateV2ParseError> {
173 self.get().contains_key(key)
196 self.get().contains_key(key)
174 }
197 }
175
198
176 fn get(&self, key: &HgPath) -> Option<DirstateEntry> {
199 fn get(
200 &self,
201 key: &HgPath,
202 ) -> Result<Option<DirstateEntry>, DirstateV2ParseError> {
177 self.get().get(key)
203 self.get().get(key)
178 }
204 }
179
205
@@ -11,7 +11,9 b' use cpython::{'
11 UnsafePyLeaked,
11 UnsafePyLeaked,
12 };
12 };
13
13
14 use crate::dirstate::dirstate_map::v2_error;
14 use crate::dirstate::DirstateMap;
15 use crate::dirstate::DirstateMap;
16 use hg::dirstate_tree::on_disk::DirstateV2ParseError;
15 use hg::utils::hg_path::HgPath;
17 use hg::utils::hg_path::HgPath;
16 use std::cell::RefCell;
18 use std::cell::RefCell;
17
19
@@ -54,13 +56,18 b' impl NonNormalEntries {'
54 Ok(true)
56 Ok(true)
55 }
57 }
56
58
57 fn translate_key(py: Python, key: &HgPath) -> PyResult<Option<PyBytes>> {
59 fn translate_key(
60 py: Python,
61 key: Result<&HgPath, DirstateV2ParseError>,
62 ) -> PyResult<Option<PyBytes>> {
63 let key = key.map_err(|e| v2_error(py, e))?;
58 Ok(Some(PyBytes::new(py, key.as_bytes())))
64 Ok(Some(PyBytes::new(py, key.as_bytes())))
59 }
65 }
60 }
66 }
61
67
62 type NonNormalEntriesIter<'a> =
68 type NonNormalEntriesIter<'a> = Box<
63 Box<dyn Iterator<Item = &'a HgPath> + Send + 'a>;
69 dyn Iterator<Item = Result<&'a HgPath, DirstateV2ParseError>> + Send + 'a,
70 >;
64
71
65 py_shared_iterator!(
72 py_shared_iterator!(
66 NonNormalEntriesIterator,
73 NonNormalEntriesIterator,
General Comments 0
You need to be logged in to leave comments. Login now