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