##// END OF EJS Templates
dirstate-map: move most of `dirstate.update_file` logic in the dsmap...
marmoute -
r48492:e5fb14a0 default
parent child Browse files
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 elif clean_p2 and wc_tracked:
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 self.normal(filename, parentfiledata=parentfiledata)
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(&mut self, key: impl AsRef<HgPath>) {
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, _key: &HgPath) {
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