Show More
@@ -320,21 +320,6 b' static PyObject *dirstate_item_v1_mtime(' | |||||
320 | return PyInt_FromLong(dirstate_item_c_v1_mtime(self)); |
|
320 | return PyInt_FromLong(dirstate_item_c_v1_mtime(self)); | |
321 | }; |
|
321 | }; | |
322 |
|
322 | |||
323 | static PyObject *dirstate_item_need_delay(dirstateItemObject *self, |
|
|||
324 | PyObject *now) |
|
|||
325 | { |
|
|||
326 | int now_s; |
|
|||
327 | int now_ns; |
|
|||
328 | if (!PyArg_ParseTuple(now, "ii", &now_s, &now_ns)) { |
|
|||
329 | return NULL; |
|
|||
330 | } |
|
|||
331 | if (dirstate_item_c_v1_state(self) == 'n' && self->mtime_s == now_s) { |
|
|||
332 | Py_RETURN_TRUE; |
|
|||
333 | } else { |
|
|||
334 | Py_RETURN_FALSE; |
|
|||
335 | } |
|
|||
336 | }; |
|
|||
337 |
|
||||
338 | static PyObject *dirstate_item_mtime_likely_equal_to(dirstateItemObject *self, |
|
323 | static PyObject *dirstate_item_mtime_likely_equal_to(dirstateItemObject *self, | |
339 | PyObject *other) |
|
324 | PyObject *other) | |
340 | { |
|
325 | { | |
@@ -548,8 +533,6 b' static PyMethodDef dirstate_item_methods' | |||||
548 | "return a \"size\" suitable for v1 serialization"}, |
|
533 | "return a \"size\" suitable for v1 serialization"}, | |
549 | {"v1_mtime", (PyCFunction)dirstate_item_v1_mtime, METH_NOARGS, |
|
534 | {"v1_mtime", (PyCFunction)dirstate_item_v1_mtime, METH_NOARGS, | |
550 | "return a \"mtime\" suitable for v1 serialization"}, |
|
535 | "return a \"mtime\" suitable for v1 serialization"}, | |
551 | {"need_delay", (PyCFunction)dirstate_item_need_delay, METH_O, |
|
|||
552 | "True if the stored mtime would be ambiguous with the current time"}, |
|
|||
553 | {"mtime_likely_equal_to", (PyCFunction)dirstate_item_mtime_likely_equal_to, |
|
536 | {"mtime_likely_equal_to", (PyCFunction)dirstate_item_mtime_likely_equal_to, | |
554 | METH_O, "True if the stored mtime is likely equal to the given mtime"}, |
|
537 | METH_O, "True if the stored mtime is likely equal to the given mtime"}, | |
555 | {"from_v1_data", (PyCFunction)dirstate_item_from_v1_meth, |
|
538 | {"from_v1_data", (PyCFunction)dirstate_item_from_v1_meth, | |
@@ -922,12 +905,9 b' static PyObject *pack_dirstate(PyObject ' | |||||
922 | Py_ssize_t nbytes, pos, l; |
|
905 | Py_ssize_t nbytes, pos, l; | |
923 | PyObject *k, *v = NULL, *pn; |
|
906 | PyObject *k, *v = NULL, *pn; | |
924 | char *p, *s; |
|
907 | char *p, *s; | |
925 | int now_s; |
|
|||
926 | int now_ns; |
|
|||
927 |
|
908 | |||
928 |
if (!PyArg_ParseTuple(args, "O!O!O! |
|
909 | if (!PyArg_ParseTuple(args, "O!O!O!:pack_dirstate", &PyDict_Type, &map, | |
929 |
|
|
910 | &PyDict_Type, ©map, &PyTuple_Type, &pl)) { | |
930 | &now_s, &now_ns)) { |
|
|||
931 | return NULL; |
|
911 | return NULL; | |
932 | } |
|
912 | } | |
933 |
|
913 | |||
@@ -996,21 +976,6 b' static PyObject *pack_dirstate(PyObject ' | |||||
996 | mode = dirstate_item_c_v1_mode(tuple); |
|
976 | mode = dirstate_item_c_v1_mode(tuple); | |
997 | size = dirstate_item_c_v1_size(tuple); |
|
977 | size = dirstate_item_c_v1_size(tuple); | |
998 | mtime = dirstate_item_c_v1_mtime(tuple); |
|
978 | mtime = dirstate_item_c_v1_mtime(tuple); | |
999 | if (state == 'n' && tuple->mtime_s == now_s) { |
|
|||
1000 | /* See pure/parsers.py:pack_dirstate for why we do |
|
|||
1001 | * this. */ |
|
|||
1002 | mtime = -1; |
|
|||
1003 | mtime_unset = (PyObject *)dirstate_item_from_v1_data( |
|
|||
1004 | state, mode, size, mtime); |
|
|||
1005 | if (!mtime_unset) { |
|
|||
1006 | goto bail; |
|
|||
1007 | } |
|
|||
1008 | if (PyDict_SetItem(map, k, mtime_unset) == -1) { |
|
|||
1009 | goto bail; |
|
|||
1010 | } |
|
|||
1011 | Py_DECREF(mtime_unset); |
|
|||
1012 | mtime_unset = NULL; |
|
|||
1013 | } |
|
|||
1014 | *p++ = state; |
|
979 | *p++ = state; | |
1015 | putbe32((uint32_t)mode, p); |
|
980 | putbe32((uint32_t)mode, p); | |
1016 | putbe32((uint32_t)size, p + 4); |
|
981 | putbe32((uint32_t)size, p + 4); |
@@ -779,25 +779,6 b' class dirstate(object):' | |||||
779 | # filesystem's notion of 'now' |
|
779 | # filesystem's notion of 'now' | |
780 | now = timestamp.mtime_of(util.fstat(st)) |
|
780 | now = timestamp.mtime_of(util.fstat(st)) | |
781 |
|
781 | |||
782 | # enough 'delaywrite' prevents 'pack_dirstate' from dropping |
|
|||
783 | # timestamp of each entries in dirstate, because of 'now > mtime' |
|
|||
784 | delaywrite = self._ui.configint(b'debug', b'dirstate.delaywrite') |
|
|||
785 | if delaywrite > 0: |
|
|||
786 | # do we have any files to delay for? |
|
|||
787 | for f, e in pycompat.iteritems(self._map): |
|
|||
788 | if e.need_delay(now): |
|
|||
789 | import time # to avoid useless import |
|
|||
790 |
|
||||
791 | # rather than sleep n seconds, sleep until the next |
|
|||
792 | # multiple of n seconds |
|
|||
793 | clock = time.time() |
|
|||
794 | start = int(clock) - (int(clock) % delaywrite) |
|
|||
795 | end = start + delaywrite |
|
|||
796 | time.sleep(end - clock) |
|
|||
797 | # trust our estimate that the end is near now |
|
|||
798 | now = timestamp.timestamp((end, 0)) |
|
|||
799 | break |
|
|||
800 |
|
||||
801 | self._map.write(tr, st, now) |
|
782 | self._map.write(tr, st, now) | |
802 | self._dirty = False |
|
783 | self._dirty = False | |
803 |
|
784 |
@@ -446,11 +446,11 b' class dirstatemap(_dirstatemapcommon):' | |||||
446 |
|
446 | |||
447 | def write(self, tr, st, now): |
|
447 | def write(self, tr, st, now): | |
448 | if self._use_dirstate_v2: |
|
448 | if self._use_dirstate_v2: | |
449 |
packed, meta = v2.pack_dirstate(self._map, self.copymap |
|
449 | packed, meta = v2.pack_dirstate(self._map, self.copymap) | |
450 | self.write_v2_no_append(tr, st, meta, packed) |
|
450 | self.write_v2_no_append(tr, st, meta, packed) | |
451 | else: |
|
451 | else: | |
452 | packed = parsers.pack_dirstate( |
|
452 | packed = parsers.pack_dirstate( | |
453 |
self._map, self.copymap, self.parents() |
|
453 | self._map, self.copymap, self.parents() | |
454 | ) |
|
454 | ) | |
455 | st.write(packed) |
|
455 | st.write(packed) | |
456 | st.close() |
|
456 | st.close() | |
@@ -658,7 +658,7 b' if rustmod is not None:' | |||||
658 | def write(self, tr, st, now): |
|
658 | def write(self, tr, st, now): | |
659 | if not self._use_dirstate_v2: |
|
659 | if not self._use_dirstate_v2: | |
660 | p1, p2 = self.parents() |
|
660 | p1, p2 = self.parents() | |
661 |
packed = self._map.write_v1(p1, p2 |
|
661 | packed = self._map.write_v1(p1, p2) | |
662 | st.write(packed) |
|
662 | st.write(packed) | |
663 | st.close() |
|
663 | st.close() | |
664 | self._dirtyparents = False |
|
664 | self._dirtyparents = False | |
@@ -666,7 +666,7 b' if rustmod is not None:' | |||||
666 |
|
666 | |||
667 | # We can only append to an existing data file if there is one |
|
667 | # We can only append to an existing data file if there is one | |
668 | can_append = self.docket.uuid is not None |
|
668 | can_append = self.docket.uuid is not None | |
669 |
packed, meta, append = self._map.write_v2( |
|
669 | packed, meta, append = self._map.write_v2(can_append) | |
670 | if append: |
|
670 | if append: | |
671 | docket = self.docket |
|
671 | docket = self.docket | |
672 | data_filename = docket.data_filename() |
|
672 | data_filename = docket.data_filename() |
@@ -174,12 +174,10 b' class Node(object):' | |||||
174 | ) |
|
174 | ) | |
175 |
|
175 | |||
176 |
|
176 | |||
177 |
def pack_dirstate(map, copy_map |
|
177 | def pack_dirstate(map, copy_map): | |
178 | """ |
|
178 | """ | |
179 | Pack `map` and `copy_map` into the dirstate v2 binary format and return |
|
179 | Pack `map` and `copy_map` into the dirstate v2 binary format and return | |
180 | the bytearray. |
|
180 | the bytearray. | |
181 | `now` is a timestamp of the current filesystem time used to detect race |
|
|||
182 | conditions in writing the dirstate to disk, see inline comment. |
|
|||
183 |
|
181 | |||
184 | The on-disk format expects a tree-like structure where the leaves are |
|
182 | The on-disk format expects a tree-like structure where the leaves are | |
185 | written first (and sorted per-directory), going up levels until the root |
|
183 | written first (and sorted per-directory), going up levels until the root | |
@@ -284,17 +282,6 b' def pack_dirstate(map, copy_map, now):' | |||||
284 | stack.append(current_node) |
|
282 | stack.append(current_node) | |
285 |
|
283 | |||
286 | for index, (path, entry) in enumerate(sorted_map, 1): |
|
284 | for index, (path, entry) in enumerate(sorted_map, 1): | |
287 | if entry.need_delay(now): |
|
|||
288 | # The file was last modified "simultaneously" with the current |
|
|||
289 | # write to dirstate (i.e. within the same second for file- |
|
|||
290 | # systems with a granularity of 1 sec). This commonly happens |
|
|||
291 | # for at least a couple of files on 'update'. |
|
|||
292 | # The user could change the file without changing its size |
|
|||
293 | # within the same second. Invalidate the file's mtime in |
|
|||
294 | # dirstate, forcing future 'status' calls to compare the |
|
|||
295 | # contents of the file if the size is the same. This prevents |
|
|||
296 | # mistakenly treating such files as clean. |
|
|||
297 | entry.set_possibly_dirty() |
|
|||
298 | nodes_with_entry_count += 1 |
|
285 | nodes_with_entry_count += 1 | |
299 | if path in copy_map: |
|
286 | if path in copy_map: | |
300 | nodes_with_copy_source_count += 1 |
|
287 | nodes_with_copy_source_count += 1 |
@@ -536,10 +536,6 b' class DirstateItem(object):' | |||||
536 | else: |
|
536 | else: | |
537 | return self._mtime_s |
|
537 | return self._mtime_s | |
538 |
|
538 | |||
539 | def need_delay(self, now): |
|
|||
540 | """True if the stored mtime would be ambiguous with the current time""" |
|
|||
541 | return self.v1_state() == b'n' and self._mtime_s == now[0] |
|
|||
542 |
|
||||
543 |
|
539 | |||
544 | def gettype(q): |
|
540 | def gettype(q): | |
545 | return int(q & 0xFFFF) |
|
541 | return int(q & 0xFFFF) | |
@@ -905,23 +901,11 b' def parse_dirstate(dmap, copymap, st):' | |||||
905 | return parents |
|
901 | return parents | |
906 |
|
902 | |||
907 |
|
903 | |||
908 |
def pack_dirstate(dmap, copymap, pl |
|
904 | def pack_dirstate(dmap, copymap, pl): | |
909 | cs = stringio() |
|
905 | cs = stringio() | |
910 | write = cs.write |
|
906 | write = cs.write | |
911 | write(b"".join(pl)) |
|
907 | write(b"".join(pl)) | |
912 | for f, e in pycompat.iteritems(dmap): |
|
908 | for f, e in pycompat.iteritems(dmap): | |
913 | if e.need_delay(now): |
|
|||
914 | # The file was last modified "simultaneously" with the current |
|
|||
915 | # write to dirstate (i.e. within the same second for file- |
|
|||
916 | # systems with a granularity of 1 sec). This commonly happens |
|
|||
917 | # for at least a couple of files on 'update'. |
|
|||
918 | # The user could change the file without changing its size |
|
|||
919 | # within the same second. Invalidate the file's mtime in |
|
|||
920 | # dirstate, forcing future 'status' calls to compare the |
|
|||
921 | # contents of the file if the size is the same. This prevents |
|
|||
922 | # mistakenly treating such files as clean. |
|
|||
923 | e.set_possibly_dirty() |
|
|||
924 |
|
||||
925 | if f in copymap: |
|
909 | if f in copymap: | |
926 | f = b"%s\0%s" % (f, copymap[f]) |
|
910 | f = b"%s\0%s" % (f, copymap[f]) | |
927 | e = _pack( |
|
911 | e = _pack( |
@@ -590,16 +590,6 b' impl DirstateEntry {' | |||||
590 | pub fn debug_tuple(&self) -> (u8, i32, i32, i32) { |
|
590 | pub fn debug_tuple(&self) -> (u8, i32, i32, i32) { | |
591 | (self.state().into(), self.mode(), self.size(), self.mtime()) |
|
591 | (self.state().into(), self.mode(), self.size(), self.mtime()) | |
592 | } |
|
592 | } | |
593 |
|
||||
594 | /// True if the stored mtime would be ambiguous with the current time |
|
|||
595 | pub fn need_delay(&self, now: TruncatedTimestamp) -> bool { |
|
|||
596 | if let Some(mtime) = self.mtime { |
|
|||
597 | self.state() == EntryState::Normal |
|
|||
598 | && mtime.truncated_seconds() == now.truncated_seconds() |
|
|||
599 | } else { |
|
|||
600 | false |
|
|||
601 | } |
|
|||
602 | } |
|
|||
603 | } |
|
593 | } | |
604 |
|
594 | |||
605 | impl EntryState { |
|
595 | impl EntryState { |
@@ -677,25 +677,6 b" impl<'on_disk> DirstateMap<'on_disk> {" | |||||
677 | }) |
|
677 | }) | |
678 | } |
|
678 | } | |
679 |
|
679 | |||
680 | fn clear_known_ambiguous_mtimes( |
|
|||
681 | &mut self, |
|
|||
682 | paths: &[impl AsRef<HgPath>], |
|
|||
683 | ) -> Result<(), DirstateV2ParseError> { |
|
|||
684 | for path in paths { |
|
|||
685 | if let Some(node) = Self::get_node_mut( |
|
|||
686 | self.on_disk, |
|
|||
687 | &mut self.unreachable_bytes, |
|
|||
688 | &mut self.root, |
|
|||
689 | path.as_ref(), |
|
|||
690 | )? { |
|
|||
691 | if let NodeData::Entry(entry) = &mut node.data { |
|
|||
692 | entry.set_possibly_dirty(); |
|
|||
693 | } |
|
|||
694 | } |
|
|||
695 | } |
|
|||
696 | Ok(()) |
|
|||
697 | } |
|
|||
698 |
|
||||
699 | fn count_dropped_path(unreachable_bytes: &mut u32, path: &Cow<HgPath>) { |
|
680 | fn count_dropped_path(unreachable_bytes: &mut u32, path: &Cow<HgPath>) { | |
700 | if let Cow::Borrowed(path) = path { |
|
681 | if let Cow::Borrowed(path) = path { | |
701 | *unreachable_bytes += path.len() as u32 |
|
682 | *unreachable_bytes += path.len() as u32 | |
@@ -930,29 +911,20 b' impl OwningDirstateMap {' | |||||
930 | pub fn pack_v1( |
|
911 | pub fn pack_v1( | |
931 | &mut self, |
|
912 | &mut self, | |
932 | parents: DirstateParents, |
|
913 | parents: DirstateParents, | |
933 | now: TruncatedTimestamp, |
|
|||
934 | ) -> Result<Vec<u8>, DirstateError> { |
|
914 | ) -> Result<Vec<u8>, DirstateError> { | |
935 | let map = self.get_map_mut(); |
|
915 | let map = self.get_map_mut(); | |
936 | let mut ambiguous_mtimes = Vec::new(); |
|
|||
937 | // Optizimation (to be measured?): pre-compute size to avoid `Vec` |
|
916 | // Optizimation (to be measured?): pre-compute size to avoid `Vec` | |
938 | // reallocations |
|
917 | // reallocations | |
939 | let mut size = parents.as_bytes().len(); |
|
918 | let mut size = parents.as_bytes().len(); | |
940 | for node in map.iter_nodes() { |
|
919 | for node in map.iter_nodes() { | |
941 | let node = node?; |
|
920 | let node = node?; | |
942 |
if |
|
921 | if node.entry()?.is_some() { | |
943 | size += packed_entry_size( |
|
922 | size += packed_entry_size( | |
944 | node.full_path(map.on_disk)?, |
|
923 | node.full_path(map.on_disk)?, | |
945 | node.copy_source(map.on_disk)?, |
|
924 | node.copy_source(map.on_disk)?, | |
946 | ); |
|
925 | ); | |
947 | if entry.need_delay(now) { |
|
|||
948 | ambiguous_mtimes.push( |
|
|||
949 | node.full_path_borrowed(map.on_disk)? |
|
|||
950 | .detach_from_tree(), |
|
|||
951 | ) |
|
|||
952 |
|
|
926 | } | |
953 |
|
|
927 | } | |
954 | } |
|
|||
955 | map.clear_known_ambiguous_mtimes(&ambiguous_mtimes)?; |
|
|||
956 |
|
928 | |||
957 | let mut packed = Vec::with_capacity(size); |
|
929 | let mut packed = Vec::with_capacity(size); | |
958 | packed.extend(parents.as_bytes()); |
|
930 | packed.extend(parents.as_bytes()); | |
@@ -978,26 +950,9 b' impl OwningDirstateMap {' | |||||
978 | #[timed] |
|
950 | #[timed] | |
979 | pub fn pack_v2( |
|
951 | pub fn pack_v2( | |
980 | &mut self, |
|
952 | &mut self, | |
981 | now: TruncatedTimestamp, |
|
|||
982 | can_append: bool, |
|
953 | can_append: bool, | |
983 | ) -> Result<(Vec<u8>, Vec<u8>, bool), DirstateError> { |
|
954 | ) -> Result<(Vec<u8>, Vec<u8>, bool), DirstateError> { | |
984 | let map = self.get_map_mut(); |
|
955 | let map = self.get_map_mut(); | |
985 | let mut paths = Vec::new(); |
|
|||
986 | for node in map.iter_nodes() { |
|
|||
987 | let node = node?; |
|
|||
988 | if let Some(entry) = node.entry()? { |
|
|||
989 | if entry.need_delay(now) { |
|
|||
990 | paths.push( |
|
|||
991 | node.full_path_borrowed(map.on_disk)? |
|
|||
992 | .detach_from_tree(), |
|
|||
993 | ) |
|
|||
994 | } |
|
|||
995 | } |
|
|||
996 | } |
|
|||
997 | // Borrow of `self` ends here since we collect cloned paths |
|
|||
998 |
|
||||
999 | map.clear_known_ambiguous_mtimes(&paths)?; |
|
|||
1000 |
|
||||
1001 | on_disk::write(map, can_append) |
|
956 | on_disk::write(map, can_append) | |
1002 | } |
|
957 | } | |
1003 |
|
958 |
@@ -18,7 +18,7 b' use cpython::{' | |||||
18 |
|
18 | |||
19 | use crate::{ |
|
19 | use crate::{ | |
20 | dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator}, |
|
20 | dirstate::copymap::{CopyMap, CopyMapItemsIterator, CopyMapKeysIterator}, | |
21 |
dirstate::item:: |
|
21 | dirstate::item::DirstateItem, | |
22 | pybytes_deref::PyBytesDeref, |
|
22 | pybytes_deref::PyBytesDeref, | |
23 | }; |
|
23 | }; | |
24 | use hg::{ |
|
24 | use hg::{ | |
@@ -194,16 +194,13 b' py_class!(pub class DirstateMap |py| {' | |||||
194 | &self, |
|
194 | &self, | |
195 | p1: PyObject, |
|
195 | p1: PyObject, | |
196 | p2: PyObject, |
|
196 | p2: PyObject, | |
197 | now: (u32, u32) |
|
|||
198 | ) -> PyResult<PyBytes> { |
|
197 | ) -> PyResult<PyBytes> { | |
199 | let now = timestamp(py, now)?; |
|
|||
200 |
|
||||
201 | let mut inner = self.inner(py).borrow_mut(); |
|
198 | let mut inner = self.inner(py).borrow_mut(); | |
202 | let parents = DirstateParents { |
|
199 | let parents = DirstateParents { | |
203 | p1: extract_node_id(py, &p1)?, |
|
200 | p1: extract_node_id(py, &p1)?, | |
204 | p2: extract_node_id(py, &p2)?, |
|
201 | p2: extract_node_id(py, &p2)?, | |
205 | }; |
|
202 | }; | |
206 |
let result = inner.pack_v1(parents |
|
203 | let result = inner.pack_v1(parents); | |
207 | match result { |
|
204 | match result { | |
208 | Ok(packed) => Ok(PyBytes::new(py, &packed)), |
|
205 | Ok(packed) => Ok(PyBytes::new(py, &packed)), | |
209 | Err(_) => Err(PyErr::new::<exc::OSError, _>( |
|
206 | Err(_) => Err(PyErr::new::<exc::OSError, _>( | |
@@ -218,13 +215,10 b' py_class!(pub class DirstateMap |py| {' | |||||
218 | /// instead of written to a new data file (False). |
|
215 | /// instead of written to a new data file (False). | |
219 | def write_v2( |
|
216 | def write_v2( | |
220 | &self, |
|
217 | &self, | |
221 | now: (u32, u32), |
|
|||
222 | can_append: bool, |
|
218 | can_append: bool, | |
223 | ) -> PyResult<PyObject> { |
|
219 | ) -> PyResult<PyObject> { | |
224 | let now = timestamp(py, now)?; |
|
|||
225 |
|
||||
226 | let mut inner = self.inner(py).borrow_mut(); |
|
220 | let mut inner = self.inner(py).borrow_mut(); | |
227 |
let result = inner.pack_v2( |
|
221 | let result = inner.pack_v2(can_append); | |
228 | match result { |
|
222 | match result { | |
229 | Ok((packed, tree_metadata, append)) => { |
|
223 | Ok((packed, tree_metadata, append)) => { | |
230 | let packed = PyBytes::new(py, &packed); |
|
224 | let packed = PyBytes::new(py, &packed); |
@@ -194,11 +194,6 b' py_class!(pub class DirstateItem |py| {' | |||||
194 | Ok(mtime) |
|
194 | Ok(mtime) | |
195 | } |
|
195 | } | |
196 |
|
196 | |||
197 | def need_delay(&self, now: (u32, u32)) -> PyResult<bool> { |
|
|||
198 | let now = timestamp(py, now)?; |
|
|||
199 | Ok(self.entry(py).get().need_delay(now)) |
|
|||
200 | } |
|
|||
201 |
|
||||
202 | def mtime_likely_equal_to(&self, other: (u32, u32)) -> PyResult<bool> { |
|
197 | def mtime_likely_equal_to(&self, other: (u32, u32)) -> PyResult<bool> { | |
203 | if let Some(mtime) = self.entry(py).get().truncated_mtime() { |
|
198 | if let Some(mtime) = self.entry(py).get().truncated_mtime() { | |
204 | Ok(mtime.likely_equal(timestamp(py, other)?)) |
|
199 | Ok(mtime.likely_equal(timestamp(py, other)?)) |
@@ -37,14 +37,8 b" parsers = policy.importmod('parsers')" | |||||
37 | has_rust_dirstate = policy.importrust('dirstate') is not None |
|
37 | has_rust_dirstate = policy.importrust('dirstate') is not None | |
38 |
|
38 | |||
39 |
|
39 | |||
40 |
def pack_dirstate( |
|
40 | def pack_dirstate(orig, dmap, copymap, pl): | |
41 | # execute what original parsers.pack_dirstate should do actually |
|
41 | return orig(dmap, copymap, pl) | |
42 | # for consistency |
|
|||
43 | for f, e in dmap.items(): |
|
|||
44 | if e.need_delay(now): |
|
|||
45 | e.set_possibly_dirty() |
|
|||
46 |
|
||||
47 | return orig(dmap, copymap, pl, fakenow) |
|
|||
48 |
|
42 | |||
49 |
|
43 | |||
50 | def fakewrite(ui, func): |
|
44 | def fakewrite(ui, func): | |
@@ -67,19 +61,19 b' def fakewrite(ui, func):' | |||||
67 | # The Rust implementation does not use public parse/pack dirstate |
|
61 | # The Rust implementation does not use public parse/pack dirstate | |
68 | # to prevent conversion round-trips |
|
62 | # to prevent conversion round-trips | |
69 | orig_dirstatemap_write = dirstatemapmod.dirstatemap.write |
|
63 | orig_dirstatemap_write = dirstatemapmod.dirstatemap.write | |
70 |
wrapper = lambda self, tr, st |
|
64 | wrapper = lambda self, tr, st: orig_dirstatemap_write(self, tr, st) | |
71 | self, tr, st, fakenow |
|
|||
72 | ) |
|
|||
73 | dirstatemapmod.dirstatemap.write = wrapper |
|
65 | dirstatemapmod.dirstatemap.write = wrapper | |
74 |
|
66 | |||
75 | orig_get_fs_now = timestamp.get_fs_now |
|
67 | orig_get_fs_now = timestamp.get_fs_now | |
76 |
wrapper = lambda *args: pack_dirstate( |
|
68 | wrapper = lambda *args: pack_dirstate(orig_pack_dirstate, *args) | |
77 |
|
69 | |||
78 | orig_module = parsers |
|
70 | orig_module = parsers | |
79 | orig_pack_dirstate = parsers.pack_dirstate |
|
71 | orig_pack_dirstate = parsers.pack_dirstate | |
80 |
|
72 | |||
81 | orig_module.pack_dirstate = wrapper |
|
73 | orig_module.pack_dirstate = wrapper | |
82 |
timestamp.get_fs_now = |
|
74 | timestamp.get_fs_now = ( | |
|
75 | lambda *args: fakenow | |||
|
76 | ) # XXX useless for this purpose now | |||
83 | try: |
|
77 | try: | |
84 | return func() |
|
78 | return func() | |
85 | finally: |
|
79 | finally: |
@@ -349,6 +349,10 b' Test that updated files are treated as "' | |||||
349 | aren't changed), even if none of mode, size and timestamp of them |
|
349 | aren't changed), even if none of mode, size and timestamp of them | |
350 | isn't changed on the filesystem (see also issue4583). |
|
350 | isn't changed on the filesystem (see also issue4583). | |
351 |
|
351 | |||
|
352 | This test is now "best effort" as the mechanism to prevent such race are | |||
|
353 | getting better, it get more complicated to test a specific scenario that would | |||
|
354 | trigger it. If you see flakyness here, there is a race. | |||
|
355 | ||||
352 | $ cat > $TESTTMP/abort.py <<EOF |
|
356 | $ cat > $TESTTMP/abort.py <<EOF | |
353 | > from __future__ import absolute_import |
|
357 | > from __future__ import absolute_import | |
354 | > # emulate aborting before "recordupdates()". in this case, files |
|
358 | > # emulate aborting before "recordupdates()". in this case, files | |
@@ -365,13 +369,6 b" isn't changed on the filesystem (see als" | |||||
365 | > extensions.wrapfunction(merge, "applyupdates", applyupdates) |
|
369 | > extensions.wrapfunction(merge, "applyupdates", applyupdates) | |
366 | > EOF |
|
370 | > EOF | |
367 |
|
371 | |||
368 | $ cat >> .hg/hgrc <<EOF |
|
|||
369 | > [fakedirstatewritetime] |
|
|||
370 | > # emulate invoking dirstate.write() via repo.status() |
|
|||
371 | > # at 2000-01-01 00:00 |
|
|||
372 | > fakenow = 200001010000 |
|
|||
373 | > EOF |
|
|||
374 |
|
||||
375 | (file gotten from other revision) |
|
372 | (file gotten from other revision) | |
376 |
|
373 | |||
377 | $ hg update -q -C 2 |
|
374 | $ hg update -q -C 2 | |
@@ -381,12 +378,8 b" isn't changed on the filesystem (see als" | |||||
381 | $ hg update -q -C 3 |
|
378 | $ hg update -q -C 3 | |
382 | $ cat b |
|
379 | $ cat b | |
383 | This is file b1 |
|
380 | This is file b1 | |
384 | $ touch -t 200001010000 b |
|
|||
385 | $ hg debugrebuildstate |
|
|||
386 |
|
||||
387 | $ cat >> .hg/hgrc <<EOF |
|
381 | $ cat >> .hg/hgrc <<EOF | |
388 | > [extensions] |
|
382 | > [extensions] | |
389 | > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py |
|
|||
390 | > abort = $TESTTMP/abort.py |
|
383 | > abort = $TESTTMP/abort.py | |
391 | > EOF |
|
384 | > EOF | |
392 | $ hg merge 5 |
|
385 | $ hg merge 5 | |
@@ -394,13 +387,11 b" isn't changed on the filesystem (see als" | |||||
394 | [255] |
|
387 | [255] | |
395 | $ cat >> .hg/hgrc <<EOF |
|
388 | $ cat >> .hg/hgrc <<EOF | |
396 | > [extensions] |
|
389 | > [extensions] | |
397 | > fakedirstatewritetime = ! |
|
|||
398 | > abort = ! |
|
390 | > abort = ! | |
399 | > EOF |
|
391 | > EOF | |
400 |
|
392 | |||
401 | $ cat b |
|
393 | $ cat b | |
402 | THIS IS FILE B5 |
|
394 | THIS IS FILE B5 | |
403 | $ touch -t 200001010000 b |
|
|||
404 | $ hg status -A b |
|
395 | $ hg status -A b | |
405 | M b |
|
396 | M b | |
406 |
|
397 | |||
@@ -413,12 +404,10 b" isn't changed on the filesystem (see als" | |||||
413 |
|
404 | |||
414 | $ cat b |
|
405 | $ cat b | |
415 | this is file b6 |
|
406 | this is file b6 | |
416 | $ touch -t 200001010000 b |
|
407 | $ hg status | |
417 | $ hg debugrebuildstate |
|
|||
418 |
|
408 | |||
419 | $ cat >> .hg/hgrc <<EOF |
|
409 | $ cat >> .hg/hgrc <<EOF | |
420 | > [extensions] |
|
410 | > [extensions] | |
421 | > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py |
|
|||
422 | > abort = $TESTTMP/abort.py |
|
411 | > abort = $TESTTMP/abort.py | |
423 | > EOF |
|
412 | > EOF | |
424 | $ hg merge --tool internal:other 5 |
|
413 | $ hg merge --tool internal:other 5 | |
@@ -426,13 +415,11 b" isn't changed on the filesystem (see als" | |||||
426 | [255] |
|
415 | [255] | |
427 | $ cat >> .hg/hgrc <<EOF |
|
416 | $ cat >> .hg/hgrc <<EOF | |
428 | > [extensions] |
|
417 | > [extensions] | |
429 | > fakedirstatewritetime = ! |
|
|||
430 | > abort = ! |
|
418 | > abort = ! | |
431 | > EOF |
|
419 | > EOF | |
432 |
|
420 | |||
433 | $ cat b |
|
421 | $ cat b | |
434 | THIS IS FILE B5 |
|
422 | THIS IS FILE B5 | |
435 | $ touch -t 200001010000 b |
|
|||
436 | $ hg status -A b |
|
423 | $ hg status -A b | |
437 | M b |
|
424 | M b | |
438 |
|
425 |
@@ -1021,37 +1021,21 b' Issue1977: multirepo push should fail if' | |||||
1021 |
|
1021 | |||
1022 | test if untracked file is not overwritten |
|
1022 | test if untracked file is not overwritten | |
1023 |
|
1023 | |||
1024 | (this also tests that updated .hgsubstate is treated as "modified", |
|
1024 | (this tests also has a change to update .hgsubstate and merge it within the | |
1025 | when 'merge.update()' is aborted before 'merge.recordupdates()', even |
|
1025 | same second. It should mark is are modified , even if none of mode, size and | |
1026 | if none of mode, size and timestamp of it isn't changed on the |
|
1026 | timestamp of it isn't changed on the filesystem (see also issue4583)) | |
1027 | filesystem (see also issue4583)) |
|
|||
1028 |
|
1027 | |||
1029 | $ echo issue3276_ok > repo/s/b |
|
1028 | $ echo issue3276_ok > repo/s/b | |
1030 | $ hg -R repo2 push -f -q |
|
1029 | $ hg -R repo2 push -f -q | |
1031 | $ touch -t 200001010000 repo/.hgsubstate |
|
|||
1032 |
|
1030 | |||
1033 | $ cat >> repo/.hg/hgrc <<EOF |
|
|||
1034 | > [fakedirstatewritetime] |
|
|||
1035 | > # emulate invoking dirstate.write() via repo.status() |
|
|||
1036 | > # at 2000-01-01 00:00 |
|
|||
1037 | > fakenow = 200001010000 |
|
|||
1038 | > |
|
|||
1039 | > [extensions] |
|
|||
1040 | > fakedirstatewritetime = $TESTDIR/fakedirstatewritetime.py |
|
|||
1041 | > EOF |
|
|||
1042 | $ hg -R repo update |
|
1031 | $ hg -R repo update | |
1043 | b: untracked file differs |
|
1032 | b: untracked file differs | |
1044 | abort: untracked files in working directory differ from files in requested revision (in subrepository "s") |
|
1033 | abort: untracked files in working directory differ from files in requested revision (in subrepository "s") | |
1045 | [255] |
|
1034 | [255] | |
1046 | $ cat >> repo/.hg/hgrc <<EOF |
|
|||
1047 | > [extensions] |
|
|||
1048 | > fakedirstatewritetime = ! |
|
|||
1049 | > EOF |
|
|||
1050 |
|
1035 | |||
1051 | $ cat repo/s/b |
|
1036 | $ cat repo/s/b | |
1052 | issue3276_ok |
|
1037 | issue3276_ok | |
1053 | $ rm repo/s/b |
|
1038 | $ rm repo/s/b | |
1054 | $ touch -t 200001010000 repo/.hgsubstate |
|
|||
1055 | $ hg -R repo revert --all |
|
1039 | $ hg -R repo revert --all | |
1056 | reverting repo/.hgsubstate |
|
1040 | reverting repo/.hgsubstate | |
1057 | reverting subrepo s |
|
1041 | reverting subrepo s |
General Comments 0
You need to be logged in to leave comments.
Login now