Show More
@@ -564,30 +564,46 b' class dirstate(object):' | |||
|
564 | 564 | if merged and (clean_p1 or clean_p2): |
|
565 | 565 | msg = b'`merged` argument incompatible with `clean_p1`/`clean_p2`' |
|
566 | 566 | raise error.ProgrammingError(msg) |
|
567 | if not (p1_tracked or p2_tracked or wc_tracked): | |
|
568 | self._drop(filename) | |
|
569 | elif merged: | |
|
570 | assert wc_tracked | |
|
571 | assert self.in_merge # we are never in the "normallookup" case | |
|
572 | self.otherparent(filename) | |
|
573 | elif not (p1_tracked or p2_tracked) and wc_tracked: | |
|
574 | self._addpath(filename, added=True, possibly_dirty=possibly_dirty) | |
|
575 | self._map.copymap.pop(filename, None) | |
|
576 | elif (p1_tracked or p2_tracked) and not wc_tracked: | |
|
577 | self._remove(filename) | |
|
578 |
|
|
|
579 | assert p2_tracked | |
|
580 | self.otherparent(filename) | |
|
581 | elif not p1_tracked and p2_tracked and wc_tracked: | |
|
582 | self._addpath(filename, from_p2=True, possibly_dirty=possibly_dirty) | |
|
583 | self._map.copymap.pop(filename, None) | |
|
584 | elif possibly_dirty: | |
|
585 | self._addpath(filename, possibly_dirty=possibly_dirty) | |
|
586 | elif wc_tracked: | |
|
587 |
|
|
|
588 | # XXX We need something for file that are dirty after an update | |
|
589 | else: | |
|
590 | assert False, 'unreachable' | |
|
567 | ||
|
568 | # note: I do not think we need to double check name clash here since we | |
|
569 | # are in a update/merge case that should already have taken care of | |
|
570 | # this. The test agrees | |
|
571 | ||
|
572 | self._dirty = True | |
|
573 | self._updatedfiles.add(filename) | |
|
574 | ||
|
575 | need_parent_file_data = ( | |
|
576 | not (possibly_dirty or clean_p2 or merged) | |
|
577 | and wc_tracked | |
|
578 | and p1_tracked | |
|
579 | ) | |
|
580 | ||
|
581 | # this mean we are doing call for file we do not really care about the | |
|
582 | # data (eg: added or removed), however this should be a minor overhead | |
|
583 | # compared to the overall update process calling this. | |
|
584 | if need_parent_file_data: | |
|
585 | if parentfiledata is None: | |
|
586 | parentfiledata = self._get_filedata(filename) | |
|
587 | mtime = parentfiledata[2] | |
|
588 | ||
|
589 | if mtime > self._lastnormaltime: | |
|
590 | # Remember the most recent modification timeslot for | |
|
591 | # status(), to make sure we won't miss future | |
|
592 | # size-preserving file content modifications that happen | |
|
593 | # within the same timeslot. | |
|
594 | self._lastnormaltime = mtime | |
|
595 | ||
|
596 | self._map.reset_state( | |
|
597 | filename, | |
|
598 | wc_tracked, | |
|
599 | p1_tracked, | |
|
600 | p2_tracked=p2_tracked, | |
|
601 | merged=merged, | |
|
602 | clean_p1=clean_p1, | |
|
603 | clean_p2=clean_p2, | |
|
604 | possibly_dirty=possibly_dirty, | |
|
605 | parentfiledata=parentfiledata, | |
|
606 | ) | |
|
591 | 607 | |
|
592 | 608 | def _addpath( |
|
593 | 609 | self, |
@@ -219,6 +219,83 b' class dirstatemap(object):' | |||
|
219 | 219 | if e.dm_otherparent: |
|
220 | 220 | self.otherparentset.add(f) |
|
221 | 221 | |
|
222 | def reset_state( | |
|
223 | self, | |
|
224 | filename, | |
|
225 | wc_tracked, | |
|
226 | p1_tracked, | |
|
227 | p2_tracked=False, | |
|
228 | merged=False, | |
|
229 | clean_p1=False, | |
|
230 | clean_p2=False, | |
|
231 | possibly_dirty=False, | |
|
232 | parentfiledata=None, | |
|
233 | ): | |
|
234 | """Set a entry to a given state, diregarding all previous state | |
|
235 | ||
|
236 | This is to be used by the part of the dirstate API dedicated to | |
|
237 | adjusting the dirstate after a update/merge. | |
|
238 | ||
|
239 | note: calling this might result to no entry existing at all if the | |
|
240 | dirstate map does not see any point at having one for this file | |
|
241 | anymore. | |
|
242 | """ | |
|
243 | if merged and (clean_p1 or clean_p2): | |
|
244 | msg = b'`merged` argument incompatible with `clean_p1`/`clean_p2`' | |
|
245 | raise error.ProgrammingError(msg) | |
|
246 | # copy information are now outdated | |
|
247 | # (maybe new information should be in directly passed to this function) | |
|
248 | self.copymap.pop(filename, None) | |
|
249 | ||
|
250 | if not (p1_tracked or p2_tracked or wc_tracked): | |
|
251 | self.dropfile(filename) | |
|
252 | elif merged: | |
|
253 | # XXX might be merged and removed ? | |
|
254 | entry = self.get(filename) | |
|
255 | if entry is not None and entry.tracked: | |
|
256 | # XXX mostly replicate dirstate.other parent. We should get | |
|
257 | # the higher layer to pass us more reliable data where `merged` | |
|
258 | # actually mean merged. Dropping the else clause will show | |
|
259 | # failure in `test-graft.t` | |
|
260 | self.addfile(filename, merged=True) | |
|
261 | else: | |
|
262 | self.addfile(filename, from_p2=True) | |
|
263 | elif not (p1_tracked or p2_tracked) and wc_tracked: | |
|
264 | self.addfile(filename, added=True, possibly_dirty=possibly_dirty) | |
|
265 | elif (p1_tracked or p2_tracked) and not wc_tracked: | |
|
266 | # XXX might be merged and removed ? | |
|
267 | old_entry = self._map.get(filename) | |
|
268 | self._dirs_decr(filename, old_entry=old_entry, remove_variant=True) | |
|
269 | self._map[filename] = DirstateItem(b'r', 0, 0, 0) | |
|
270 | self.nonnormalset.add(filename) | |
|
271 | elif clean_p2 and wc_tracked: | |
|
272 | if p1_tracked or self.get(filename) is not None: | |
|
273 | # XXX the `self.get` call is catching some case in | |
|
274 | # `test-merge-remove.t` where the file is tracked in p1, the | |
|
275 | # p1_tracked argument is False. | |
|
276 | # | |
|
277 | # In addition, this seems to be a case where the file is marked | |
|
278 | # as merged without actually being the result of a merge | |
|
279 | # action. So thing are not ideal here. | |
|
280 | self.addfile(filename, merged=True) | |
|
281 | else: | |
|
282 | self.addfile(filename, from_p2=True) | |
|
283 | elif not p1_tracked and p2_tracked and wc_tracked: | |
|
284 | self.addfile(filename, from_p2=True, possibly_dirty=possibly_dirty) | |
|
285 | elif possibly_dirty: | |
|
286 | self.addfile(filename, possibly_dirty=possibly_dirty) | |
|
287 | elif wc_tracked: | |
|
288 | # this is a "normal" file | |
|
289 | if parentfiledata is None: | |
|
290 | msg = b'failed to pass parentfiledata for a normal file: %s' | |
|
291 | msg %= filename | |
|
292 | raise error.ProgrammingError(msg) | |
|
293 | mode, size, mtime = parentfiledata | |
|
294 | self.addfile(filename, mode=mode, size=size, mtime=mtime) | |
|
295 | self.nonnormalset.discard(filename) | |
|
296 | else: | |
|
297 | assert False, 'unreachable' | |
|
298 | ||
|
222 | 299 | def removefile(self, f, in_merge=False): |
|
223 | 300 | """ |
|
224 | 301 | Mark a file as removed in the dirstate. |
@@ -499,6 +576,87 b' if rustmod is not None:' | |||
|
499 | 576 | possibly_dirty, |
|
500 | 577 | ) |
|
501 | 578 | |
|
579 | def reset_state( | |
|
580 | self, | |
|
581 | filename, | |
|
582 | wc_tracked, | |
|
583 | p1_tracked, | |
|
584 | p2_tracked=False, | |
|
585 | merged=False, | |
|
586 | clean_p1=False, | |
|
587 | clean_p2=False, | |
|
588 | possibly_dirty=False, | |
|
589 | parentfiledata=None, | |
|
590 | ): | |
|
591 | """Set a entry to a given state, disregarding all previous state | |
|
592 | ||
|
593 | This is to be used by the part of the dirstate API dedicated to | |
|
594 | adjusting the dirstate after a update/merge. | |
|
595 | ||
|
596 | note: calling this might result to no entry existing at all if the | |
|
597 | dirstate map does not see any point at having one for this file | |
|
598 | anymore. | |
|
599 | """ | |
|
600 | if merged and (clean_p1 or clean_p2): | |
|
601 | msg = ( | |
|
602 | b'`merged` argument incompatible with `clean_p1`/`clean_p2`' | |
|
603 | ) | |
|
604 | raise error.ProgrammingError(msg) | |
|
605 | # copy information are now outdated | |
|
606 | # (maybe new information should be in directly passed to this function) | |
|
607 | self.copymap.pop(filename, None) | |
|
608 | ||
|
609 | if not (p1_tracked or p2_tracked or wc_tracked): | |
|
610 | self.dropfile(filename) | |
|
611 | elif merged: | |
|
612 | # XXX might be merged and removed ? | |
|
613 | entry = self.get(filename) | |
|
614 | if entry is not None and entry.tracked: | |
|
615 | # XXX mostly replicate dirstate.other parent. We should get | |
|
616 | # the higher layer to pass us more reliable data where `merged` | |
|
617 | # actually mean merged. Dropping the else clause will show | |
|
618 | # failure in `test-graft.t` | |
|
619 | self.addfile(filename, merged=True) | |
|
620 | else: | |
|
621 | self.addfile(filename, from_p2=True) | |
|
622 | elif not (p1_tracked or p2_tracked) and wc_tracked: | |
|
623 | self.addfile( | |
|
624 | filename, added=True, possibly_dirty=possibly_dirty | |
|
625 | ) | |
|
626 | elif (p1_tracked or p2_tracked) and not wc_tracked: | |
|
627 | # XXX might be merged and removed ? | |
|
628 | self[filename] = DirstateItem(b'r', 0, 0, 0) | |
|
629 | self.nonnormalset.add(filename) | |
|
630 | elif clean_p2 and wc_tracked: | |
|
631 | if p1_tracked or self.get(filename) is not None: | |
|
632 | # XXX the `self.get` call is catching some case in | |
|
633 | # `test-merge-remove.t` where the file is tracked in p1, the | |
|
634 | # p1_tracked argument is False. | |
|
635 | # | |
|
636 | # In addition, this seems to be a case where the file is marked | |
|
637 | # as merged without actually being the result of a merge | |
|
638 | # action. So thing are not ideal here. | |
|
639 | self.addfile(filename, merged=True) | |
|
640 | else: | |
|
641 | self.addfile(filename, from_p2=True) | |
|
642 | elif not p1_tracked and p2_tracked and wc_tracked: | |
|
643 | self.addfile( | |
|
644 | filename, from_p2=True, possibly_dirty=possibly_dirty | |
|
645 | ) | |
|
646 | elif possibly_dirty: | |
|
647 | self.addfile(filename, possibly_dirty=possibly_dirty) | |
|
648 | elif wc_tracked: | |
|
649 | # this is a "normal" file | |
|
650 | if parentfiledata is None: | |
|
651 | msg = b'failed to pass parentfiledata for a normal file: %s' | |
|
652 | msg %= filename | |
|
653 | raise error.ProgrammingError(msg) | |
|
654 | mode, size, mtime = parentfiledata | |
|
655 | self.addfile(filename, mode=mode, size=size, mtime=mtime) | |
|
656 | self.nonnormalset.discard(filename) | |
|
657 | else: | |
|
658 | assert False, 'unreachable' | |
|
659 | ||
|
502 | 660 | def removefile(self, *args, **kwargs): |
|
503 | 661 | return self._rustmap.removefile(*args, **kwargs) |
|
504 | 662 | |
@@ -748,3 +906,7 b' if rustmod is not None:' | |||
|
748 | 906 | for name in self._rustmap.tracked_dirs(): |
|
749 | 907 | f[normcase(name)] = name |
|
750 | 908 | return f |
|
909 | ||
|
910 | def __setitem__(self, key, value): | |
|
911 | assert isinstance(value, DirstateItem) | |
|
912 | self._rustmap.set_v1(key, value) |
@@ -64,6 +64,10 b' impl DirstateMap {' | |||
|
64 | 64 | self.other_parent_set = None; |
|
65 | 65 | } |
|
66 | 66 | |
|
67 | pub fn set_v1_inner(&mut self, filename: &HgPath, entry: DirstateEntry) { | |
|
68 | self.state_map.insert(filename.to_owned(), entry); | |
|
69 | } | |
|
70 | ||
|
67 | 71 | /// Add a tracked file to the dirstate |
|
68 | 72 | pub fn add_file( |
|
69 | 73 | &mut self, |
@@ -245,10 +249,19 b' impl DirstateMap {' | |||
|
245 | 249 | } |
|
246 | 250 | } |
|
247 | 251 | |
|
248 |
pub fn non_normal_entries_remove( |
|
|
252 | pub fn non_normal_entries_remove( | |
|
253 | &mut self, | |
|
254 | key: impl AsRef<HgPath>, | |
|
255 | ) -> bool { | |
|
249 | 256 | self.get_non_normal_other_parent_entries() |
|
250 | 257 | .0 |
|
251 |
.remove(key.as_ref()) |
|
|
258 | .remove(key.as_ref()) | |
|
259 | } | |
|
260 | ||
|
261 | pub fn non_normal_entries_add(&mut self, key: impl AsRef<HgPath>) { | |
|
262 | self.get_non_normal_other_parent_entries() | |
|
263 | .0 | |
|
264 | .insert(key.as_ref().into()); | |
|
252 | 265 | } |
|
253 | 266 | |
|
254 | 267 | pub fn non_normal_entries_union( |
@@ -757,6 +757,16 b" impl<'on_disk> super::dispatch::Dirstate" | |||
|
757 | 757 | self.nodes_with_copy_source_count = 0; |
|
758 | 758 | } |
|
759 | 759 | |
|
760 | fn set_v1(&mut self, filename: &HgPath, entry: DirstateEntry) { | |
|
761 | let node = | |
|
762 | self.get_or_insert(&filename).expect("no parse error in v1"); | |
|
763 | node.data = NodeData::Entry(entry); | |
|
764 | node.children = ChildNodes::default(); | |
|
765 | node.copy_source = None; | |
|
766 | node.descendants_with_entry_count = 0; | |
|
767 | node.tracked_descendants_count = 0; | |
|
768 | } | |
|
769 | ||
|
760 | 770 | fn add_file( |
|
761 | 771 | &mut self, |
|
762 | 772 | filename: &HgPath, |
@@ -982,7 +992,16 b" impl<'on_disk> super::dispatch::Dirstate" | |||
|
982 | 992 | }) |
|
983 | 993 | } |
|
984 | 994 | |
|
985 |
fn non_normal_entries_remove(&mut self, |
|
|
995 | fn non_normal_entries_remove(&mut self, key: &HgPath) -> bool { | |
|
996 | // Do nothing, this `DirstateMap` does not have a separate "non normal | |
|
997 | // entries" set that need to be kept up to date. | |
|
998 | if let Ok(Some(v)) = self.get(key) { | |
|
999 | return v.is_non_normal(); | |
|
1000 | } | |
|
1001 | false | |
|
1002 | } | |
|
1003 | ||
|
1004 | fn non_normal_entries_add(&mut self, _key: &HgPath) { | |
|
986 | 1005 | // Do nothing, this `DirstateMap` does not have a separate "non normal |
|
987 | 1006 | // entries" set that need to be kept up to date |
|
988 | 1007 | } |
@@ -37,6 +37,8 b' pub trait DirstateMapMethods {' | |||
|
37 | 37 | /// Remove information about all files in this map |
|
38 | 38 | fn clear(&mut self); |
|
39 | 39 | |
|
40 | fn set_v1(&mut self, filename: &HgPath, entry: DirstateEntry); | |
|
41 | ||
|
40 | 42 | /// Add or change the information associated to a given file. |
|
41 | 43 | /// |
|
42 | 44 | /// `old_state` is the state in the entry that `get` would have returned |
@@ -95,7 +97,13 b' pub trait DirstateMapMethods {' | |||
|
95 | 97 | /// Mark the given path as "normal" file. This is only relevant in the flat |
|
96 | 98 | /// dirstate map where there is a separate `HashSet` that needs to be kept |
|
97 | 99 | /// up to date. |
|
98 | fn non_normal_entries_remove(&mut self, key: &HgPath); | |
|
100 | /// Returns whether the key was present in the set. | |
|
101 | fn non_normal_entries_remove(&mut self, key: &HgPath) -> bool; | |
|
102 | ||
|
103 | /// Mark the given path as "non-normal" file. | |
|
104 | /// This is only relevant in the flat dirstate map where there is a | |
|
105 | /// separate `HashSet` that needs to be kept up to date. | |
|
106 | fn non_normal_entries_add(&mut self, key: &HgPath); | |
|
99 | 107 | |
|
100 | 108 | /// Return an iterator of paths whose respective entry are either |
|
101 | 109 | /// "non-normal" (see `non_normal_entries_contains`) or "from other |
@@ -305,6 +313,14 b' impl DirstateMapMethods for DirstateMap ' | |||
|
305 | 313 | self.clear() |
|
306 | 314 | } |
|
307 | 315 | |
|
316 | /// Used to set a value directory. | |
|
317 | /// | |
|
318 | /// XXX Is temporary during a refactor of V1 dirstate and will disappear | |
|
319 | /// shortly. | |
|
320 | fn set_v1(&mut self, filename: &HgPath, entry: DirstateEntry) { | |
|
321 | self.set_v1_inner(&filename, entry) | |
|
322 | } | |
|
323 | ||
|
308 | 324 | fn add_file( |
|
309 | 325 | &mut self, |
|
310 | 326 | filename: &HgPath, |
@@ -346,10 +362,14 b' impl DirstateMapMethods for DirstateMap ' | |||
|
346 | 362 | Ok(non_normal.contains(key)) |
|
347 | 363 | } |
|
348 | 364 | |
|
349 | fn non_normal_entries_remove(&mut self, key: &HgPath) { | |
|
365 | fn non_normal_entries_remove(&mut self, key: &HgPath) -> bool { | |
|
350 | 366 | self.non_normal_entries_remove(key) |
|
351 | 367 | } |
|
352 | 368 | |
|
369 | fn non_normal_entries_add(&mut self, key: &HgPath) { | |
|
370 | self.non_normal_entries_add(key) | |
|
371 | } | |
|
372 | ||
|
353 | 373 | fn non_normal_or_other_parent_paths( |
|
354 | 374 | &mut self, |
|
355 | 375 | ) -> Box<dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + '_> |
@@ -117,6 +117,21 b' py_class!(pub class DirstateMap |py| {' | |||
|
117 | 117 | } |
|
118 | 118 | } |
|
119 | 119 | |
|
120 | def set_v1(&self, path: PyObject, item: PyObject) -> PyResult<PyObject> { | |
|
121 | let f = path.extract::<PyBytes>(py)?; | |
|
122 | let filename = HgPath::new(f.data(py)); | |
|
123 | let state = item.getattr(py, "state")?.extract::<PyBytes>(py)?; | |
|
124 | let state = state.data(py)[0]; | |
|
125 | let entry = DirstateEntry { | |
|
126 | state: state.try_into().expect("state is always valid"), | |
|
127 | mtime: item.getattr(py, "mtime")?.extract(py)?, | |
|
128 | size: item.getattr(py, "size")?.extract(py)?, | |
|
129 | mode: item.getattr(py, "mode")?.extract(py)?, | |
|
130 | }; | |
|
131 | self.inner(py).borrow_mut().set_v1(filename, entry); | |
|
132 | Ok(py.None()) | |
|
133 | } | |
|
134 | ||
|
120 | 135 | def addfile( |
|
121 | 136 | &self, |
|
122 | 137 | f: PyObject, |
@@ -261,6 +276,22 b' py_class!(pub class DirstateMap |py| {' | |||
|
261 | 276 | |
|
262 | 277 | def non_normal_entries_remove(&self, key: PyObject) -> PyResult<PyObject> { |
|
263 | 278 | let key = key.extract::<PyBytes>(py)?; |
|
279 | let key = key.data(py); | |
|
280 | let was_present = self | |
|
281 | .inner(py) | |
|
282 | .borrow_mut() | |
|
283 | .non_normal_entries_remove(HgPath::new(key)); | |
|
284 | if !was_present { | |
|
285 | let msg = String::from_utf8_lossy(key); | |
|
286 | Err(PyErr::new::<exc::KeyError, _>(py, msg)) | |
|
287 | } else { | |
|
288 | Ok(py.None()) | |
|
289 | } | |
|
290 | } | |
|
291 | ||
|
292 | def non_normal_entries_discard(&self, key: PyObject) -> PyResult<PyObject> | |
|
293 | { | |
|
294 | let key = key.extract::<PyBytes>(py)?; | |
|
264 | 295 | self |
|
265 | 296 | .inner(py) |
|
266 | 297 | .borrow_mut() |
@@ -268,6 +299,15 b' py_class!(pub class DirstateMap |py| {' | |||
|
268 | 299 | Ok(py.None()) |
|
269 | 300 | } |
|
270 | 301 | |
|
302 | def non_normal_entries_add(&self, key: PyObject) -> PyResult<PyObject> { | |
|
303 | let key = key.extract::<PyBytes>(py)?; | |
|
304 | self | |
|
305 | .inner(py) | |
|
306 | .borrow_mut() | |
|
307 | .non_normal_entries_add(HgPath::new(key.data(py))); | |
|
308 | Ok(py.None()) | |
|
309 | } | |
|
310 | ||
|
271 | 311 | def non_normal_or_other_parent_paths(&self) -> PyResult<PyList> { |
|
272 | 312 | let mut inner = self.inner(py).borrow_mut(); |
|
273 | 313 |
@@ -20,6 +20,10 b' impl DirstateMapMethods for OwningDirsta' | |||
|
20 | 20 | self.get_mut().clear() |
|
21 | 21 | } |
|
22 | 22 | |
|
23 | fn set_v1(&mut self, filename: &HgPath, entry: DirstateEntry) { | |
|
24 | self.get_mut().set_v1(filename, entry) | |
|
25 | } | |
|
26 | ||
|
23 | 27 | fn add_file( |
|
24 | 28 | &mut self, |
|
25 | 29 | filename: &HgPath, |
@@ -66,10 +70,14 b' impl DirstateMapMethods for OwningDirsta' | |||
|
66 | 70 | self.get_mut().non_normal_entries_contains(key) |
|
67 | 71 | } |
|
68 | 72 | |
|
69 | fn non_normal_entries_remove(&mut self, key: &HgPath) { | |
|
73 | fn non_normal_entries_remove(&mut self, key: &HgPath) -> bool { | |
|
70 | 74 | self.get_mut().non_normal_entries_remove(key) |
|
71 | 75 | } |
|
72 | 76 | |
|
77 | fn non_normal_entries_add(&mut self, key: &HgPath) { | |
|
78 | self.get_mut().non_normal_entries_add(key) | |
|
79 | } | |
|
80 | ||
|
73 | 81 | fn non_normal_or_other_parent_paths( |
|
74 | 82 | &mut self, |
|
75 | 83 | ) -> Box<dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + '_> |
@@ -26,6 +26,12 b' py_class!(pub class NonNormalEntries |py' | |||
|
26 | 26 | def remove(&self, key: PyObject) -> PyResult<PyObject> { |
|
27 | 27 | self.dmap(py).non_normal_entries_remove(py, key) |
|
28 | 28 | } |
|
29 | def add(&self, key: PyObject) -> PyResult<PyObject> { | |
|
30 | self.dmap(py).non_normal_entries_add(py, key) | |
|
31 | } | |
|
32 | def discard(&self, key: PyObject) -> PyResult<PyObject> { | |
|
33 | self.dmap(py).non_normal_entries_discard(py, key) | |
|
34 | } | |
|
29 | 35 | def __richcmp__(&self, other: PyObject, op: CompareOp) -> PyResult<bool> { |
|
30 | 36 | match op { |
|
31 | 37 | CompareOp::Eq => self.is_equal_to(py, other), |
General Comments 0
You need to be logged in to leave comments.
Login now