##// 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 if merged and (clean_p1 or clean_p2):
564 if merged and (clean_p1 or clean_p2):
565 msg = b'`merged` argument incompatible with `clean_p1`/`clean_p2`'
565 msg = b'`merged` argument incompatible with `clean_p1`/`clean_p2`'
566 raise error.ProgrammingError(msg)
566 raise error.ProgrammingError(msg)
567 if not (p1_tracked or p2_tracked or wc_tracked):
567
568 self._drop(filename)
568 # note: I do not think we need to double check name clash here since we
569 elif merged:
569 # are in a update/merge case that should already have taken care of
570 assert wc_tracked
570 # this. The test agrees
571 assert self.in_merge # we are never in the "normallookup" case
571
572 self.otherparent(filename)
572 self._dirty = True
573 elif not (p1_tracked or p2_tracked) and wc_tracked:
573 self._updatedfiles.add(filename)
574 self._addpath(filename, added=True, possibly_dirty=possibly_dirty)
574
575 self._map.copymap.pop(filename, None)
575 need_parent_file_data = (
576 elif (p1_tracked or p2_tracked) and not wc_tracked:
576 not (possibly_dirty or clean_p2 or merged)
577 self._remove(filename)
577 and wc_tracked
578 elif clean_p2 and wc_tracked:
578 and p1_tracked
579 assert p2_tracked
579 )
580 self.otherparent(filename)
580
581 elif not p1_tracked and p2_tracked and wc_tracked:
581 # this mean we are doing call for file we do not really care about the
582 self._addpath(filename, from_p2=True, possibly_dirty=possibly_dirty)
582 # data (eg: added or removed), however this should be a minor overhead
583 self._map.copymap.pop(filename, None)
583 # compared to the overall update process calling this.
584 elif possibly_dirty:
584 if need_parent_file_data:
585 self._addpath(filename, possibly_dirty=possibly_dirty)
585 if parentfiledata is None:
586 elif wc_tracked:
586 parentfiledata = self._get_filedata(filename)
587 self.normal(filename, parentfiledata=parentfiledata)
587 mtime = parentfiledata[2]
588 # XXX We need something for file that are dirty after an update
588
589 else:
589 if mtime > self._lastnormaltime:
590 assert False, 'unreachable'
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 def _addpath(
608 def _addpath(
593 self,
609 self,
@@ -219,6 +219,83 b' class dirstatemap(object):'
219 if e.dm_otherparent:
219 if e.dm_otherparent:
220 self.otherparentset.add(f)
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 def removefile(self, f, in_merge=False):
299 def removefile(self, f, in_merge=False):
223 """
300 """
224 Mark a file as removed in the dirstate.
301 Mark a file as removed in the dirstate.
@@ -499,6 +576,87 b' if rustmod is not None:'
499 possibly_dirty,
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 def removefile(self, *args, **kwargs):
660 def removefile(self, *args, **kwargs):
503 return self._rustmap.removefile(*args, **kwargs)
661 return self._rustmap.removefile(*args, **kwargs)
504
662
@@ -748,3 +906,7 b' if rustmod is not None:'
748 for name in self._rustmap.tracked_dirs():
906 for name in self._rustmap.tracked_dirs():
749 f[normcase(name)] = name
907 f[normcase(name)] = name
750 return f
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 self.other_parent_set = None;
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 /// Add a tracked file to the dirstate
71 /// Add a tracked file to the dirstate
68 pub fn add_file(
72 pub fn add_file(
69 &mut self,
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 self.get_non_normal_other_parent_entries()
256 self.get_non_normal_other_parent_entries()
250 .0
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 pub fn non_normal_entries_union(
267 pub fn non_normal_entries_union(
@@ -757,6 +757,16 b" impl<'on_disk> super::dispatch::Dirstate"
757 self.nodes_with_copy_source_count = 0;
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 fn add_file(
770 fn add_file(
761 &mut self,
771 &mut self,
762 filename: &HgPath,
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 // Do nothing, this `DirstateMap` does not have a separate "non normal
1005 // Do nothing, this `DirstateMap` does not have a separate "non normal
987 // entries" set that need to be kept up to date
1006 // entries" set that need to be kept up to date
988 }
1007 }
@@ -37,6 +37,8 b' pub trait DirstateMapMethods {'
37 /// Remove information about all files in this map
37 /// Remove information about all files in this map
38 fn clear(&mut self);
38 fn clear(&mut self);
39
39
40 fn set_v1(&mut self, filename: &HgPath, entry: DirstateEntry);
41
40 /// Add or change the information associated to a given file.
42 /// Add or change the information associated to a given file.
41 ///
43 ///
42 /// `old_state` is the state in the entry that `get` would have returned
44 /// `old_state` is the state in the entry that `get` would have returned
@@ -95,7 +97,13 b' pub trait DirstateMapMethods {'
95 /// Mark the given path as "normal" file. This is only relevant in the flat
97 /// Mark the given path as "normal" file. This is only relevant in the flat
96 /// dirstate map where there is a separate `HashSet` that needs to be kept
98 /// dirstate map where there is a separate `HashSet` that needs to be kept
97 /// up to date.
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 /// Return an iterator of paths whose respective entry are either
108 /// Return an iterator of paths whose respective entry are either
101 /// "non-normal" (see `non_normal_entries_contains`) or "from other
109 /// "non-normal" (see `non_normal_entries_contains`) or "from other
@@ -305,6 +313,14 b' impl DirstateMapMethods for DirstateMap '
305 self.clear()
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 fn add_file(
324 fn add_file(
309 &mut self,
325 &mut self,
310 filename: &HgPath,
326 filename: &HgPath,
@@ -346,10 +362,14 b' impl DirstateMapMethods for DirstateMap '
346 Ok(non_normal.contains(key))
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 self.non_normal_entries_remove(key)
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 fn non_normal_or_other_parent_paths(
373 fn non_normal_or_other_parent_paths(
354 &mut self,
374 &mut self,
355 ) -> Box<dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + '_>
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 def addfile(
135 def addfile(
121 &self,
136 &self,
122 f: PyObject,
137 f: PyObject,
@@ -261,6 +276,22 b' py_class!(pub class DirstateMap |py| {'
261
276
262 def non_normal_entries_remove(&self, key: PyObject) -> PyResult<PyObject> {
277 def non_normal_entries_remove(&self, key: PyObject) -> PyResult<PyObject> {
263 let key = key.extract::<PyBytes>(py)?;
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 self
295 self
265 .inner(py)
296 .inner(py)
266 .borrow_mut()
297 .borrow_mut()
@@ -268,6 +299,15 b' py_class!(pub class DirstateMap |py| {'
268 Ok(py.None())
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 def non_normal_or_other_parent_paths(&self) -> PyResult<PyList> {
311 def non_normal_or_other_parent_paths(&self) -> PyResult<PyList> {
272 let mut inner = self.inner(py).borrow_mut();
312 let mut inner = self.inner(py).borrow_mut();
273
313
@@ -20,6 +20,10 b' impl DirstateMapMethods for OwningDirsta'
20 self.get_mut().clear()
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 fn add_file(
27 fn add_file(
24 &mut self,
28 &mut self,
25 filename: &HgPath,
29 filename: &HgPath,
@@ -66,10 +70,14 b' impl DirstateMapMethods for OwningDirsta'
66 self.get_mut().non_normal_entries_contains(key)
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 self.get_mut().non_normal_entries_remove(key)
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 fn non_normal_or_other_parent_paths(
81 fn non_normal_or_other_parent_paths(
74 &mut self,
82 &mut self,
75 ) -> Box<dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + '_>
83 ) -> Box<dyn Iterator<Item = Result<&HgPath, DirstateV2ParseError>> + '_>
@@ -26,6 +26,12 b' py_class!(pub class NonNormalEntries |py'
26 def remove(&self, key: PyObject) -> PyResult<PyObject> {
26 def remove(&self, key: PyObject) -> PyResult<PyObject> {
27 self.dmap(py).non_normal_entries_remove(py, key)
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 def __richcmp__(&self, other: PyObject, op: CompareOp) -> PyResult<bool> {
35 def __richcmp__(&self, other: PyObject, op: CompareOp) -> PyResult<bool> {
30 match op {
36 match op {
31 CompareOp::Eq => self.is_equal_to(py, other),
37 CompareOp::Eq => self.is_equal_to(py, other),
General Comments 0
You need to be logged in to leave comments. Login now