##// END OF EJS Templates
rust-dirstatemap: add #[timed] to dirstatemap read for comparison...
Raphaël Gomès -
r46135:80bf7b1a default
parent child Browse files
Show More
@@ -1,503 +1,467
1 // dirstate_map.rs
1 // dirstate_map.rs
2 //
2 //
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
3 // Copyright 2019 Raphaël Gomès <rgomes@octobus.net>
4 //
4 //
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::revlog::node::NULL_NODE_ID;
8 use crate::revlog::node::NULL_NODE_ID;
9 use crate::{
9 use crate::{
10 dirstate::{parsers::PARENT_SIZE, EntryState, SIZE_FROM_OTHER_PARENT},
10 dirstate::{parsers::PARENT_SIZE, EntryState, SIZE_FROM_OTHER_PARENT},
11 pack_dirstate, parse_dirstate,
11 pack_dirstate, parse_dirstate,
12 utils::{
12 utils::{
13 files::normalize_case,
13 files::normalize_case,
14 hg_path::{HgPath, HgPathBuf},
14 hg_path::{HgPath, HgPathBuf},
15 },
15 },
16 CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateMapError,
16 CopyMap, DirsMultiset, DirstateEntry, DirstateError, DirstateMapError, DirstateParents,
17 DirstateParents, DirstateParseError, FastHashMap, StateMap,
17 DirstateParseError, FastHashMap, StateMap,
18 };
18 };
19 use core::borrow::Borrow;
19 use core::borrow::Borrow;
20 use micro_timer::timed;
20 use std::collections::HashSet;
21 use std::collections::HashSet;
21 use std::convert::TryInto;
22 use std::convert::TryInto;
22 use std::iter::FromIterator;
23 use std::iter::FromIterator;
23 use std::ops::Deref;
24 use std::ops::Deref;
24 use std::time::Duration;
25 use std::time::Duration;
25
26
26 pub type FileFoldMap = FastHashMap<HgPathBuf, HgPathBuf>;
27 pub type FileFoldMap = FastHashMap<HgPathBuf, HgPathBuf>;
27
28
28 const MTIME_UNSET: i32 = -1;
29 const MTIME_UNSET: i32 = -1;
29
30
30 #[derive(Default)]
31 #[derive(Default)]
31 pub struct DirstateMap {
32 pub struct DirstateMap {
32 state_map: StateMap,
33 state_map: StateMap,
33 pub copy_map: CopyMap,
34 pub copy_map: CopyMap,
34 file_fold_map: Option<FileFoldMap>,
35 file_fold_map: Option<FileFoldMap>,
35 pub dirs: Option<DirsMultiset>,
36 pub dirs: Option<DirsMultiset>,
36 pub all_dirs: Option<DirsMultiset>,
37 pub all_dirs: Option<DirsMultiset>,
37 non_normal_set: Option<HashSet<HgPathBuf>>,
38 non_normal_set: Option<HashSet<HgPathBuf>>,
38 other_parent_set: Option<HashSet<HgPathBuf>>,
39 other_parent_set: Option<HashSet<HgPathBuf>>,
39 parents: Option<DirstateParents>,
40 parents: Option<DirstateParents>,
40 dirty_parents: bool,
41 dirty_parents: bool,
41 }
42 }
42
43
43 /// Should only really be used in python interface code, for clarity
44 /// Should only really be used in python interface code, for clarity
44 impl Deref for DirstateMap {
45 impl Deref for DirstateMap {
45 type Target = StateMap;
46 type Target = StateMap;
46
47
47 fn deref(&self) -> &Self::Target {
48 fn deref(&self) -> &Self::Target {
48 &self.state_map
49 &self.state_map
49 }
50 }
50 }
51 }
51
52
52 impl FromIterator<(HgPathBuf, DirstateEntry)> for DirstateMap {
53 impl FromIterator<(HgPathBuf, DirstateEntry)> for DirstateMap {
53 fn from_iter<I: IntoIterator<Item = (HgPathBuf, DirstateEntry)>>(
54 fn from_iter<I: IntoIterator<Item = (HgPathBuf, DirstateEntry)>>(iter: I) -> Self {
54 iter: I,
55 ) -> Self {
56 Self {
55 Self {
57 state_map: iter.into_iter().collect(),
56 state_map: iter.into_iter().collect(),
58 ..Self::default()
57 ..Self::default()
59 }
58 }
60 }
59 }
61 }
60 }
62
61
63 impl DirstateMap {
62 impl DirstateMap {
64 pub fn new() -> Self {
63 pub fn new() -> Self {
65 Self::default()
64 Self::default()
66 }
65 }
67
66
68 pub fn clear(&mut self) {
67 pub fn clear(&mut self) {
69 self.state_map.clear();
68 self.state_map.clear();
70 self.copy_map.clear();
69 self.copy_map.clear();
71 self.file_fold_map = None;
70 self.file_fold_map = None;
72 self.non_normal_set = None;
71 self.non_normal_set = None;
73 self.other_parent_set = None;
72 self.other_parent_set = None;
74 self.set_parents(&DirstateParents {
73 self.set_parents(&DirstateParents {
75 p1: NULL_NODE_ID,
74 p1: NULL_NODE_ID,
76 p2: NULL_NODE_ID,
75 p2: NULL_NODE_ID,
77 })
76 })
78 }
77 }
79
78
80 /// Add a tracked file to the dirstate
79 /// Add a tracked file to the dirstate
81 pub fn add_file(
80 pub fn add_file(
82 &mut self,
81 &mut self,
83 filename: &HgPath,
82 filename: &HgPath,
84 old_state: EntryState,
83 old_state: EntryState,
85 entry: DirstateEntry,
84 entry: DirstateEntry,
86 ) -> Result<(), DirstateMapError> {
85 ) -> Result<(), DirstateMapError> {
87 if old_state == EntryState::Unknown || old_state == EntryState::Removed
86 if old_state == EntryState::Unknown || old_state == EntryState::Removed {
88 {
89 if let Some(ref mut dirs) = self.dirs {
87 if let Some(ref mut dirs) = self.dirs {
90 dirs.add_path(filename)?;
88 dirs.add_path(filename)?;
91 }
89 }
92 }
90 }
93 if old_state == EntryState::Unknown {
91 if old_state == EntryState::Unknown {
94 if let Some(ref mut all_dirs) = self.all_dirs {
92 if let Some(ref mut all_dirs) = self.all_dirs {
95 all_dirs.add_path(filename)?;
93 all_dirs.add_path(filename)?;
96 }
94 }
97 }
95 }
98 self.state_map.insert(filename.to_owned(), entry.to_owned());
96 self.state_map.insert(filename.to_owned(), entry.to_owned());
99
97
100 if entry.state != EntryState::Normal || entry.mtime == MTIME_UNSET {
98 if entry.state != EntryState::Normal || entry.mtime == MTIME_UNSET {
101 self.get_non_normal_other_parent_entries()
99 self.get_non_normal_other_parent_entries()
102 .0
100 .0
103 .insert(filename.to_owned());
101 .insert(filename.to_owned());
104 }
102 }
105
103
106 if entry.size == SIZE_FROM_OTHER_PARENT {
104 if entry.size == SIZE_FROM_OTHER_PARENT {
107 self.get_non_normal_other_parent_entries()
105 self.get_non_normal_other_parent_entries()
108 .1
106 .1
109 .insert(filename.to_owned());
107 .insert(filename.to_owned());
110 }
108 }
111 Ok(())
109 Ok(())
112 }
110 }
113
111
114 /// Mark a file as removed in the dirstate.
112 /// Mark a file as removed in the dirstate.
115 ///
113 ///
116 /// The `size` parameter is used to store sentinel values that indicate
114 /// The `size` parameter is used to store sentinel values that indicate
117 /// the file's previous state. In the future, we should refactor this
115 /// the file's previous state. In the future, we should refactor this
118 /// to be more explicit about what that state is.
116 /// to be more explicit about what that state is.
119 pub fn remove_file(
117 pub fn remove_file(
120 &mut self,
118 &mut self,
121 filename: &HgPath,
119 filename: &HgPath,
122 old_state: EntryState,
120 old_state: EntryState,
123 size: i32,
121 size: i32,
124 ) -> Result<(), DirstateMapError> {
122 ) -> Result<(), DirstateMapError> {
125 if old_state != EntryState::Unknown && old_state != EntryState::Removed
123 if old_state != EntryState::Unknown && old_state != EntryState::Removed {
126 {
127 if let Some(ref mut dirs) = self.dirs {
124 if let Some(ref mut dirs) = self.dirs {
128 dirs.delete_path(filename)?;
125 dirs.delete_path(filename)?;
129 }
126 }
130 }
127 }
131 if old_state == EntryState::Unknown {
128 if old_state == EntryState::Unknown {
132 if let Some(ref mut all_dirs) = self.all_dirs {
129 if let Some(ref mut all_dirs) = self.all_dirs {
133 all_dirs.add_path(filename)?;
130 all_dirs.add_path(filename)?;
134 }
131 }
135 }
132 }
136
133
137 if let Some(ref mut file_fold_map) = self.file_fold_map {
134 if let Some(ref mut file_fold_map) = self.file_fold_map {
138 file_fold_map.remove(&normalize_case(filename));
135 file_fold_map.remove(&normalize_case(filename));
139 }
136 }
140 self.state_map.insert(
137 self.state_map.insert(
141 filename.to_owned(),
138 filename.to_owned(),
142 DirstateEntry {
139 DirstateEntry {
143 state: EntryState::Removed,
140 state: EntryState::Removed,
144 mode: 0,
141 mode: 0,
145 size,
142 size,
146 mtime: 0,
143 mtime: 0,
147 },
144 },
148 );
145 );
149 self.get_non_normal_other_parent_entries()
146 self.get_non_normal_other_parent_entries()
150 .0
147 .0
151 .insert(filename.to_owned());
148 .insert(filename.to_owned());
152 Ok(())
149 Ok(())
153 }
150 }
154
151
155 /// Remove a file from the dirstate.
152 /// Remove a file from the dirstate.
156 /// Returns `true` if the file was previously recorded.
153 /// Returns `true` if the file was previously recorded.
157 pub fn drop_file(
154 pub fn drop_file(
158 &mut self,
155 &mut self,
159 filename: &HgPath,
156 filename: &HgPath,
160 old_state: EntryState,
157 old_state: EntryState,
161 ) -> Result<bool, DirstateMapError> {
158 ) -> Result<bool, DirstateMapError> {
162 let exists = self.state_map.remove(filename).is_some();
159 let exists = self.state_map.remove(filename).is_some();
163
160
164 if exists {
161 if exists {
165 if old_state != EntryState::Removed {
162 if old_state != EntryState::Removed {
166 if let Some(ref mut dirs) = self.dirs {
163 if let Some(ref mut dirs) = self.dirs {
167 dirs.delete_path(filename)?;
164 dirs.delete_path(filename)?;
168 }
165 }
169 }
166 }
170 if let Some(ref mut all_dirs) = self.all_dirs {
167 if let Some(ref mut all_dirs) = self.all_dirs {
171 all_dirs.delete_path(filename)?;
168 all_dirs.delete_path(filename)?;
172 }
169 }
173 }
170 }
174 if let Some(ref mut file_fold_map) = self.file_fold_map {
171 if let Some(ref mut file_fold_map) = self.file_fold_map {
175 file_fold_map.remove(&normalize_case(filename));
172 file_fold_map.remove(&normalize_case(filename));
176 }
173 }
177 self.get_non_normal_other_parent_entries()
174 self.get_non_normal_other_parent_entries()
178 .0
175 .0
179 .remove(filename);
176 .remove(filename);
180
177
181 Ok(exists)
178 Ok(exists)
182 }
179 }
183
180
184 pub fn clear_ambiguous_times(
181 pub fn clear_ambiguous_times(&mut self, filenames: Vec<HgPathBuf>, now: i32) {
185 &mut self,
186 filenames: Vec<HgPathBuf>,
187 now: i32,
188 ) {
189 for filename in filenames {
182 for filename in filenames {
190 let mut changed = false;
183 let mut changed = false;
191 self.state_map
184 self.state_map
192 .entry(filename.to_owned())
185 .entry(filename.to_owned())
193 .and_modify(|entry| {
186 .and_modify(|entry| {
194 if entry.state == EntryState::Normal && entry.mtime == now
187 if entry.state == EntryState::Normal && entry.mtime == now {
195 {
196 changed = true;
188 changed = true;
197 *entry = DirstateEntry {
189 *entry = DirstateEntry {
198 mtime: MTIME_UNSET,
190 mtime: MTIME_UNSET,
199 ..*entry
191 ..*entry
200 };
192 };
201 }
193 }
202 });
194 });
203 if changed {
195 if changed {
204 self.get_non_normal_other_parent_entries()
196 self.get_non_normal_other_parent_entries()
205 .0
197 .0
206 .insert(filename.to_owned());
198 .insert(filename.to_owned());
207 }
199 }
208 }
200 }
209 }
201 }
210
202
211 pub fn non_normal_entries_remove(
203 pub fn non_normal_entries_remove(&mut self, key: impl AsRef<HgPath>) -> bool {
212 &mut self,
213 key: impl AsRef<HgPath>,
214 ) -> bool {
215 self.get_non_normal_other_parent_entries()
204 self.get_non_normal_other_parent_entries()
216 .0
205 .0
217 .remove(key.as_ref())
206 .remove(key.as_ref())
218 }
207 }
219 pub fn non_normal_entries_union(
208 pub fn non_normal_entries_union(&mut self, other: HashSet<HgPathBuf>) -> Vec<HgPathBuf> {
220 &mut self,
221 other: HashSet<HgPathBuf>,
222 ) -> Vec<HgPathBuf> {
223 self.get_non_normal_other_parent_entries()
209 self.get_non_normal_other_parent_entries()
224 .0
210 .0
225 .union(&other)
211 .union(&other)
226 .map(ToOwned::to_owned)
212 .map(ToOwned::to_owned)
227 .collect()
213 .collect()
228 }
214 }
229
215
230 pub fn get_non_normal_other_parent_entries(
216 pub fn get_non_normal_other_parent_entries(
231 &mut self,
217 &mut self,
232 ) -> (&mut HashSet<HgPathBuf>, &mut HashSet<HgPathBuf>) {
218 ) -> (&mut HashSet<HgPathBuf>, &mut HashSet<HgPathBuf>) {
233 self.set_non_normal_other_parent_entries(false);
219 self.set_non_normal_other_parent_entries(false);
234 (
220 (
235 self.non_normal_set.as_mut().unwrap(),
221 self.non_normal_set.as_mut().unwrap(),
236 self.other_parent_set.as_mut().unwrap(),
222 self.other_parent_set.as_mut().unwrap(),
237 )
223 )
238 }
224 }
239
225
240 /// Useful to get immutable references to those sets in contexts where
226 /// Useful to get immutable references to those sets in contexts where
241 /// you only have an immutable reference to the `DirstateMap`, like when
227 /// you only have an immutable reference to the `DirstateMap`, like when
242 /// sharing references with Python.
228 /// sharing references with Python.
243 ///
229 ///
244 /// TODO, get rid of this along with the other "setter/getter" stuff when
230 /// TODO, get rid of this along with the other "setter/getter" stuff when
245 /// a nice typestate plan is defined.
231 /// a nice typestate plan is defined.
246 ///
232 ///
247 /// # Panics
233 /// # Panics
248 ///
234 ///
249 /// Will panic if either set is `None`.
235 /// Will panic if either set is `None`.
250 pub fn get_non_normal_other_parent_entries_panic(
236 pub fn get_non_normal_other_parent_entries_panic(
251 &self,
237 &self,
252 ) -> (&HashSet<HgPathBuf>, &HashSet<HgPathBuf>) {
238 ) -> (&HashSet<HgPathBuf>, &HashSet<HgPathBuf>) {
253 (
239 (
254 self.non_normal_set.as_ref().unwrap(),
240 self.non_normal_set.as_ref().unwrap(),
255 self.other_parent_set.as_ref().unwrap(),
241 self.other_parent_set.as_ref().unwrap(),
256 )
242 )
257 }
243 }
258
244
259 pub fn set_non_normal_other_parent_entries(&mut self, force: bool) {
245 pub fn set_non_normal_other_parent_entries(&mut self, force: bool) {
260 if !force
246 if !force && self.non_normal_set.is_some() && self.other_parent_set.is_some() {
261 && self.non_normal_set.is_some()
262 && self.other_parent_set.is_some()
263 {
264 return;
247 return;
265 }
248 }
266 let mut non_normal = HashSet::new();
249 let mut non_normal = HashSet::new();
267 let mut other_parent = HashSet::new();
250 let mut other_parent = HashSet::new();
268
251
269 for (
252 for (
270 filename,
253 filename,
271 DirstateEntry {
254 DirstateEntry {
272 state, size, mtime, ..
255 state, size, mtime, ..
273 },
256 },
274 ) in self.state_map.iter()
257 ) in self.state_map.iter()
275 {
258 {
276 if *state != EntryState::Normal || *mtime == MTIME_UNSET {
259 if *state != EntryState::Normal || *mtime == MTIME_UNSET {
277 non_normal.insert(filename.to_owned());
260 non_normal.insert(filename.to_owned());
278 }
261 }
279 if *state == EntryState::Normal && *size == SIZE_FROM_OTHER_PARENT
262 if *state == EntryState::Normal && *size == SIZE_FROM_OTHER_PARENT {
280 {
281 other_parent.insert(filename.to_owned());
263 other_parent.insert(filename.to_owned());
282 }
264 }
283 }
265 }
284 self.non_normal_set = Some(non_normal);
266 self.non_normal_set = Some(non_normal);
285 self.other_parent_set = Some(other_parent);
267 self.other_parent_set = Some(other_parent);
286 }
268 }
287
269
288 /// Both of these setters and their uses appear to be the simplest way to
270 /// Both of these setters and their uses appear to be the simplest way to
289 /// emulate a Python lazy property, but it is ugly and unidiomatic.
271 /// emulate a Python lazy property, but it is ugly and unidiomatic.
290 /// TODO One day, rewriting this struct using the typestate might be a
272 /// TODO One day, rewriting this struct using the typestate might be a
291 /// good idea.
273 /// good idea.
292 pub fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
274 pub fn set_all_dirs(&mut self) -> Result<(), DirstateMapError> {
293 if self.all_dirs.is_none() {
275 if self.all_dirs.is_none() {
294 self.all_dirs =
276 self.all_dirs = Some(DirsMultiset::from_dirstate(&self.state_map, None)?);
295 Some(DirsMultiset::from_dirstate(&self.state_map, None)?);
296 }
277 }
297 Ok(())
278 Ok(())
298 }
279 }
299
280
300 pub fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
281 pub fn set_dirs(&mut self) -> Result<(), DirstateMapError> {
301 if self.dirs.is_none() {
282 if self.dirs.is_none() {
302 self.dirs = Some(DirsMultiset::from_dirstate(
283 self.dirs = Some(DirsMultiset::from_dirstate(
303 &self.state_map,
284 &self.state_map,
304 Some(EntryState::Removed),
285 Some(EntryState::Removed),
305 )?);
286 )?);
306 }
287 }
307 Ok(())
288 Ok(())
308 }
289 }
309
290
310 pub fn has_tracked_dir(
291 pub fn has_tracked_dir(&mut self, directory: &HgPath) -> Result<bool, DirstateMapError> {
311 &mut self,
312 directory: &HgPath,
313 ) -> Result<bool, DirstateMapError> {
314 self.set_dirs()?;
292 self.set_dirs()?;
315 Ok(self.dirs.as_ref().unwrap().contains(directory))
293 Ok(self.dirs.as_ref().unwrap().contains(directory))
316 }
294 }
317
295
318 pub fn has_dir(
296 pub fn has_dir(&mut self, directory: &HgPath) -> Result<bool, DirstateMapError> {
319 &mut self,
320 directory: &HgPath,
321 ) -> Result<bool, DirstateMapError> {
322 self.set_all_dirs()?;
297 self.set_all_dirs()?;
323 Ok(self.all_dirs.as_ref().unwrap().contains(directory))
298 Ok(self.all_dirs.as_ref().unwrap().contains(directory))
324 }
299 }
325
300
326 pub fn parents(
301 pub fn parents(&mut self, file_contents: &[u8]) -> Result<&DirstateParents, DirstateError> {
327 &mut self,
328 file_contents: &[u8],
329 ) -> Result<&DirstateParents, DirstateError> {
330 if let Some(ref parents) = self.parents {
302 if let Some(ref parents) = self.parents {
331 return Ok(parents);
303 return Ok(parents);
332 }
304 }
333 let parents;
305 let parents;
334 if file_contents.len() == PARENT_SIZE * 2 {
306 if file_contents.len() == PARENT_SIZE * 2 {
335 parents = DirstateParents {
307 parents = DirstateParents {
336 p1: file_contents[..PARENT_SIZE].try_into().unwrap(),
308 p1: file_contents[..PARENT_SIZE].try_into().unwrap(),
337 p2: file_contents[PARENT_SIZE..PARENT_SIZE * 2]
309 p2: file_contents[PARENT_SIZE..PARENT_SIZE * 2]
338 .try_into()
310 .try_into()
339 .unwrap(),
311 .unwrap(),
340 };
312 };
341 } else if file_contents.is_empty() {
313 } else if file_contents.is_empty() {
342 parents = DirstateParents {
314 parents = DirstateParents {
343 p1: NULL_NODE_ID,
315 p1: NULL_NODE_ID,
344 p2: NULL_NODE_ID,
316 p2: NULL_NODE_ID,
345 };
317 };
346 } else {
318 } else {
347 return Err(DirstateError::Parse(DirstateParseError::Damaged));
319 return Err(DirstateError::Parse(DirstateParseError::Damaged));
348 }
320 }
349
321
350 self.parents = Some(parents);
322 self.parents = Some(parents);
351 Ok(self.parents.as_ref().unwrap())
323 Ok(self.parents.as_ref().unwrap())
352 }
324 }
353
325
354 pub fn set_parents(&mut self, parents: &DirstateParents) {
326 pub fn set_parents(&mut self, parents: &DirstateParents) {
355 self.parents = Some(parents.clone());
327 self.parents = Some(parents.clone());
356 self.dirty_parents = true;
328 self.dirty_parents = true;
357 }
329 }
358
330
359 pub fn read(
331 #[timed]
360 &mut self,
332 pub fn read(&mut self, file_contents: &[u8]) -> Result<Option<DirstateParents>, DirstateError> {
361 file_contents: &[u8],
362 ) -> Result<Option<DirstateParents>, DirstateError> {
363 if file_contents.is_empty() {
333 if file_contents.is_empty() {
364 return Ok(None);
334 return Ok(None);
365 }
335 }
366
336
367 let (parents, entries, copies) = parse_dirstate(file_contents)?;
337 let (parents, entries, copies) = parse_dirstate(file_contents)?;
368 self.state_map.extend(
338 self.state_map.extend(
369 entries
339 entries
370 .into_iter()
340 .into_iter()
371 .map(|(path, entry)| (path.to_owned(), entry)),
341 .map(|(path, entry)| (path.to_owned(), entry)),
372 );
342 );
373 self.copy_map.extend(
343 self.copy_map.extend(
374 copies
344 copies
375 .into_iter()
345 .into_iter()
376 .map(|(path, copy)| (path.to_owned(), copy.to_owned())),
346 .map(|(path, copy)| (path.to_owned(), copy.to_owned())),
377 );
347 );
378
348
379 if !self.dirty_parents {
349 if !self.dirty_parents {
380 self.set_parents(&parents);
350 self.set_parents(&parents);
381 }
351 }
382
352
383 Ok(Some(parents))
353 Ok(Some(parents))
384 }
354 }
385
355
386 pub fn pack(
356 pub fn pack(
387 &mut self,
357 &mut self,
388 parents: DirstateParents,
358 parents: DirstateParents,
389 now: Duration,
359 now: Duration,
390 ) -> Result<Vec<u8>, DirstateError> {
360 ) -> Result<Vec<u8>, DirstateError> {
391 let packed =
361 let packed = pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
392 pack_dirstate(&mut self.state_map, &self.copy_map, parents, now)?;
393
362
394 self.dirty_parents = false;
363 self.dirty_parents = false;
395
364
396 self.set_non_normal_other_parent_entries(true);
365 self.set_non_normal_other_parent_entries(true);
397 Ok(packed)
366 Ok(packed)
398 }
367 }
399
368
400 pub fn build_file_fold_map(&mut self) -> &FileFoldMap {
369 pub fn build_file_fold_map(&mut self) -> &FileFoldMap {
401 if let Some(ref file_fold_map) = self.file_fold_map {
370 if let Some(ref file_fold_map) = self.file_fold_map {
402 return file_fold_map;
371 return file_fold_map;
403 }
372 }
404 let mut new_file_fold_map = FileFoldMap::default();
373 let mut new_file_fold_map = FileFoldMap::default();
405 for (filename, DirstateEntry { state, .. }) in self.state_map.borrow()
374 for (filename, DirstateEntry { state, .. }) in self.state_map.borrow() {
406 {
407 if *state == EntryState::Removed {
375 if *state == EntryState::Removed {
408 new_file_fold_map
376 new_file_fold_map.insert(normalize_case(filename), filename.to_owned());
409 .insert(normalize_case(filename), filename.to_owned());
410 }
377 }
411 }
378 }
412 self.file_fold_map = Some(new_file_fold_map);
379 self.file_fold_map = Some(new_file_fold_map);
413 self.file_fold_map.as_ref().unwrap()
380 self.file_fold_map.as_ref().unwrap()
414 }
381 }
415 }
382 }
416
383
417 #[cfg(test)]
384 #[cfg(test)]
418 mod tests {
385 mod tests {
419 use super::*;
386 use super::*;
420
387
421 #[test]
388 #[test]
422 fn test_dirs_multiset() {
389 fn test_dirs_multiset() {
423 let mut map = DirstateMap::new();
390 let mut map = DirstateMap::new();
424 assert!(map.dirs.is_none());
391 assert!(map.dirs.is_none());
425 assert!(map.all_dirs.is_none());
392 assert!(map.all_dirs.is_none());
426
393
427 assert_eq!(map.has_dir(HgPath::new(b"nope")).unwrap(), false);
394 assert_eq!(map.has_dir(HgPath::new(b"nope")).unwrap(), false);
428 assert!(map.all_dirs.is_some());
395 assert!(map.all_dirs.is_some());
429 assert!(map.dirs.is_none());
396 assert!(map.dirs.is_none());
430
397
431 assert_eq!(map.has_tracked_dir(HgPath::new(b"nope")).unwrap(), false);
398 assert_eq!(map.has_tracked_dir(HgPath::new(b"nope")).unwrap(), false);
432 assert!(map.dirs.is_some());
399 assert!(map.dirs.is_some());
433 }
400 }
434
401
435 #[test]
402 #[test]
436 fn test_add_file() {
403 fn test_add_file() {
437 let mut map = DirstateMap::new();
404 let mut map = DirstateMap::new();
438
405
439 assert_eq!(0, map.len());
406 assert_eq!(0, map.len());
440
407
441 map.add_file(
408 map.add_file(
442 HgPath::new(b"meh"),
409 HgPath::new(b"meh"),
443 EntryState::Normal,
410 EntryState::Normal,
444 DirstateEntry {
411 DirstateEntry {
445 state: EntryState::Normal,
412 state: EntryState::Normal,
446 mode: 1337,
413 mode: 1337,
447 mtime: 1337,
414 mtime: 1337,
448 size: 1337,
415 size: 1337,
449 },
416 },
450 )
417 )
451 .unwrap();
418 .unwrap();
452
419
453 assert_eq!(1, map.len());
420 assert_eq!(1, map.len());
454 assert_eq!(0, map.get_non_normal_other_parent_entries().0.len());
421 assert_eq!(0, map.get_non_normal_other_parent_entries().0.len());
455 assert_eq!(0, map.get_non_normal_other_parent_entries().1.len());
422 assert_eq!(0, map.get_non_normal_other_parent_entries().1.len());
456 }
423 }
457
424
458 #[test]
425 #[test]
459 fn test_non_normal_other_parent_entries() {
426 fn test_non_normal_other_parent_entries() {
460 let mut map: DirstateMap = [
427 let mut map: DirstateMap = [
461 (b"f1", (EntryState::Removed, 1337, 1337, 1337)),
428 (b"f1", (EntryState::Removed, 1337, 1337, 1337)),
462 (b"f2", (EntryState::Normal, 1337, 1337, -1)),
429 (b"f2", (EntryState::Normal, 1337, 1337, -1)),
463 (b"f3", (EntryState::Normal, 1337, 1337, 1337)),
430 (b"f3", (EntryState::Normal, 1337, 1337, 1337)),
464 (b"f4", (EntryState::Normal, 1337, -2, 1337)),
431 (b"f4", (EntryState::Normal, 1337, -2, 1337)),
465 (b"f5", (EntryState::Added, 1337, 1337, 1337)),
432 (b"f5", (EntryState::Added, 1337, 1337, 1337)),
466 (b"f6", (EntryState::Added, 1337, 1337, -1)),
433 (b"f6", (EntryState::Added, 1337, 1337, -1)),
467 (b"f7", (EntryState::Merged, 1337, 1337, -1)),
434 (b"f7", (EntryState::Merged, 1337, 1337, -1)),
468 (b"f8", (EntryState::Merged, 1337, 1337, 1337)),
435 (b"f8", (EntryState::Merged, 1337, 1337, 1337)),
469 (b"f9", (EntryState::Merged, 1337, -2, 1337)),
436 (b"f9", (EntryState::Merged, 1337, -2, 1337)),
470 (b"fa", (EntryState::Added, 1337, -2, 1337)),
437 (b"fa", (EntryState::Added, 1337, -2, 1337)),
471 (b"fb", (EntryState::Removed, 1337, -2, 1337)),
438 (b"fb", (EntryState::Removed, 1337, -2, 1337)),
472 ]
439 ]
473 .iter()
440 .iter()
474 .map(|(fname, (state, mode, size, mtime))| {
441 .map(|(fname, (state, mode, size, mtime))| {
475 (
442 (
476 HgPathBuf::from_bytes(fname.as_ref()),
443 HgPathBuf::from_bytes(fname.as_ref()),
477 DirstateEntry {
444 DirstateEntry {
478 state: *state,
445 state: *state,
479 mode: *mode,
446 mode: *mode,
480 size: *size,
447 size: *size,
481 mtime: *mtime,
448 mtime: *mtime,
482 },
449 },
483 )
450 )
484 })
451 })
485 .collect();
452 .collect();
486
453
487 let mut non_normal = [
454 let mut non_normal = [
488 b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
455 b"f1", b"f2", b"f5", b"f6", b"f7", b"f8", b"f9", b"fa", b"fb",
489 ]
456 ]
490 .iter()
457 .iter()
491 .map(|x| HgPathBuf::from_bytes(x.as_ref()))
458 .map(|x| HgPathBuf::from_bytes(x.as_ref()))
492 .collect();
459 .collect();
493
460
494 let mut other_parent = HashSet::new();
461 let mut other_parent = HashSet::new();
495 other_parent.insert(HgPathBuf::from_bytes(b"f4"));
462 other_parent.insert(HgPathBuf::from_bytes(b"f4"));
496 let entries = map.get_non_normal_other_parent_entries();
463 let entries = map.get_non_normal_other_parent_entries();
497
464
498 assert_eq!(
465 assert_eq!((&mut non_normal, &mut other_parent), (entries.0, entries.1));
499 (&mut non_normal, &mut other_parent),
500 (entries.0, entries.1)
501 );
502 }
466 }
503 }
467 }
General Comments 0
You need to be logged in to leave comments. Login now