This tidies up the Rust side while simplifying the Python side.
Details
Details
Diff Detail
Diff Detail
- Repository
- rHG Mercurial
- Lint
Lint Skipped - Unit
Unit Tests Skipped
This tidies up the Rust side while simplifying the Python side.
| Lint Skipped |
| Unit Tests Skipped |
| Path | Packages | |||
|---|---|---|---|---|
| M | mercurial/dirstate.py (12 lines) | |||
| M | rust/hg-cpython/src/dirstate.rs (151 lines) | |||
| M | rust/hg-cpython/src/lib.rs (6 lines) | |||
| A | M | rust/hg-cpython/src/parsers.rs (177 lines) | ||
| M | tests/fakedirstatewritetime.py (20 lines) |
| Commit | Parents | Author | Summary | Date |
|---|---|---|---|---|
| Raphaël Gomès | Jul 10 2019, 4:16 AM |
| pathutil, | pathutil, | ||||
| policy, | policy, | ||||
| pycompat, | pycompat, | ||||
| scmutil, | scmutil, | ||||
| txnutil, | txnutil, | ||||
| util, | util, | ||||
| ) | ) | ||||
| parsers = policy.importmod(r'parsers') | orig_parsers = policy.importmod(r'parsers') | ||||
| dirstatemod = policy.importrust(r'dirstate', default=parsers) | parsers = policy.importrust(r'parsers', default=orig_parsers) | ||||
| propertycache = util.propertycache | propertycache = util.propertycache | ||||
| filecache = scmutil.filecache | filecache = scmutil.filecache | ||||
| _rangemask = 0x7fffffff | _rangemask = 0x7fffffff | ||||
| dirstatetuple = parsers.dirstatetuple | dirstatetuple = orig_parsers.dirstatetuple | ||||
| class repocache(filecache): | class repocache(filecache): | ||||
| """filecache for files in .hg/""" | """filecache for files in .hg/""" | ||||
| def join(self, obj, fname): | def join(self, obj, fname): | ||||
| return obj._opener.join(fname) | return obj._opener.join(fname) | ||||
| class rootcache(filecache): | class rootcache(filecache): | ||||
| """filecache for files in the repository root""" | """filecache for files in the repository root""" | ||||
| # them as not to be tracked by the collector. However, this has no | # them as not to be tracked by the collector. However, this has no | ||||
| # effect on when GCs are triggered, only on what objects the GC looks | # effect on when GCs are triggered, only on what objects the GC looks | ||||
| # into. This means that O(number of files) GCs are unavoidable. | # into. This means that O(number of files) GCs are unavoidable. | ||||
| # Depending on when in the process's lifetime the dirstate is parsed, | # Depending on when in the process's lifetime the dirstate is parsed, | ||||
| # this can get very expensive. As a workaround, disable GC while | # this can get very expensive. As a workaround, disable GC while | ||||
| # parsing the dirstate. | # parsing the dirstate. | ||||
| # | # | ||||
| # (we cannot decorate the function directly since it is in a C module) | # (we cannot decorate the function directly since it is in a C module) | ||||
| parse_dirstate = util.nogc(dirstatemod.parse_dirstate) | parse_dirstate = util.nogc(parsers.parse_dirstate) | ||||
| p = parse_dirstate(self._map, self.copymap, st) | p = parse_dirstate(self._map, self.copymap, st) | ||||
| if not self._dirtyparents: | if not self._dirtyparents: | ||||
| self.setparents(*p) | self.setparents(*p) | ||||
| # Avoid excess attribute lookups by fast pathing certain checks | # Avoid excess attribute lookups by fast pathing certain checks | ||||
| self.__contains__ = self._map.__contains__ | self.__contains__ = self._map.__contains__ | ||||
| self.__getitem__ = self._map.__getitem__ | self.__getitem__ = self._map.__getitem__ | ||||
| self.get = self._map.get | self.get = self._map.get | ||||
| def write(self, st, now): | def write(self, st, now): | ||||
| st.write(dirstatemod.pack_dirstate(self._map, self.copymap, | st.write(parsers.pack_dirstate(self._map, self.copymap, | ||||
| self.parents(), now)) | self.parents(), now)) | ||||
| st.close() | st.close() | ||||
| self._dirtyparents = False | self._dirtyparents = False | ||||
| self.nonnormalset, self.otherparentset = self.nonnormalentries() | self.nonnormalset, self.otherparentset = self.nonnormalentries() | ||||
| @propertycache | @propertycache | ||||
| def nonnormalset(self): | def nonnormalset(self): | ||||
| nonnorm, otherparents = self.nonnormalentries() | nonnorm, otherparents = self.nonnormalentries() | ||||
| self.otherparentset = otherparents | self.otherparentset = otherparents | ||||
| // dirstate.rs | // dirstate.rs | ||||
| // | // | ||||
| // Copyright 2019 Raphaël Gomès <rgomes@octobus.net> | // Copyright 2019 Raphaël Gomès <rgomes@octobus.net> | ||||
| // | // | ||||
| // This software may be used and distributed according to the terms of the | // This software may be used and distributed according to the terms of the | ||||
| // GNU General Public License version 2 or any later version. | // GNU General Public License version 2 or any later version. | ||||
| //! Bindings for the `hg::dirstate` module provided by the | //! Bindings for the `hg::dirstate` module provided by the | ||||
| //! `hg-core` package. | //! `hg-core` package. | ||||
| //! | //! | ||||
| //! From Python, this will be seen as `mercurial.rustext.dirstate` | //! From Python, this will be seen as `mercurial.rustext.dirstate` | ||||
| mod dirs_multiset; | mod dirs_multiset; | ||||
| use crate::dirstate::dirs_multiset::Dirs; | use crate::dirstate::dirs_multiset::Dirs; | ||||
| use cpython::{ | use cpython::{ | ||||
| exc, PyBytes, PyDict, PyErr, PyInt, PyModule, PyObject, PyResult, | PyBytes, PyDict, PyErr, PyModule, PyObject, PyResult, PySequence, Python, | ||||
| PySequence, PyTuple, Python, PythonObject, ToPyObject, | |||||
| }; | |||||
| use hg::{ | |||||
| pack_dirstate, parse_dirstate, CopyVecEntry, DirstateEntry, | |||||
| DirstatePackError, DirstateParents, DirstateParseError, DirstateVec, | |||||
| }; | }; | ||||
| use hg::{DirstateEntry, DirstateVec}; | |||||
| use libc::{c_char, c_int}; | use libc::{c_char, c_int}; | ||||
| #[cfg(feature = "python27")] | #[cfg(feature = "python27")] | ||||
| use python27_sys::PyCapsule_Import; | use python27_sys::PyCapsule_Import; | ||||
| #[cfg(feature = "python3")] | #[cfg(feature = "python3")] | ||||
| use python3_sys::PyCapsule_Import; | use python3_sys::PyCapsule_Import; | ||||
| use std::collections::HashMap; | |||||
| use std::ffi::CStr; | use std::ffi::CStr; | ||||
| use std::mem::transmute; | use std::mem::transmute; | ||||
| /// C code uses a custom `dirstate_tuple` type, checks in multiple instances | /// C code uses a custom `dirstate_tuple` type, checks in multiple instances | ||||
| /// for this type, and raises a Python `Exception` if the check does not pass. | /// for this type, and raises a Python `Exception` if the check does not pass. | ||||
| /// Because this type differs only in name from the regular Python tuple, it | /// Because this type differs only in name from the regular Python tuple, it | ||||
| /// would be a good idea in the near future to remove it entirely to allow | /// would be a good idea in the near future to remove it entirely to allow | ||||
| /// for a pure Python tuple of the same effective structure to be used, | /// for a pure Python tuple of the same effective structure to be used, | ||||
| /// rendering this type and the capsule below useless. | /// rendering this type and the capsule below useless. | ||||
| type MakeDirstateTupleFn = extern "C" fn( | type MakeDirstateTupleFn = extern "C" fn( | ||||
| state: c_char, | state: c_char, | ||||
| mode: c_int, | mode: c_int, | ||||
| size: c_int, | size: c_int, | ||||
| mtime: c_int, | mtime: c_int, | ||||
| ) -> PyObject; | ) -> PyObject; | ||||
| /// This is largely a copy/paste from cindex.rs, pending the merge of a | /// This is largely a copy/paste from cindex.rs, pending the merge of a | ||||
| /// `py_capsule_fn!` macro in the rust-cpython project: | /// `py_capsule_fn!` macro in the rust-cpython project: | ||||
| /// https://github.com/dgrunwald/rust-cpython/pull/169 | /// https://github.com/dgrunwald/rust-cpython/pull/169 | ||||
| fn decapsule_make_dirstate_tuple(py: Python) -> PyResult<MakeDirstateTupleFn> { | pub fn decapsule_make_dirstate_tuple( | ||||
| py: Python, | |||||
| ) -> PyResult<MakeDirstateTupleFn> { | |||||
| unsafe { | unsafe { | ||||
| let caps_name = CStr::from_bytes_with_nul_unchecked( | let caps_name = CStr::from_bytes_with_nul_unchecked( | ||||
| b"mercurial.cext.parsers.make_dirstate_tuple_CAPI\0", | b"mercurial.cext.parsers.make_dirstate_tuple_CAPI\0", | ||||
| ); | ); | ||||
| let from_caps = PyCapsule_Import(caps_name.as_ptr(), 0); | let from_caps = PyCapsule_Import(caps_name.as_ptr(), 0); | ||||
| if from_caps.is_null() { | if from_caps.is_null() { | ||||
| return Err(PyErr::fetch(py)); | return Err(PyErr::fetch(py)); | ||||
| } | } | ||||
| Ok(transmute(from_caps)) | Ok(transmute(from_caps)) | ||||
| } | } | ||||
| } | } | ||||
| fn parse_dirstate_wrapper( | pub fn extract_dirstate_vec( | ||||
| py: Python, | |||||
| dmap: PyDict, | |||||
| copymap: PyDict, | |||||
| st: PyBytes, | |||||
| ) -> PyResult<PyTuple> { | |||||
| match parse_dirstate(st.data(py)) { | |||||
| Ok((parents, dirstate_vec, copies)) => { | |||||
| for (filename, entry) in dirstate_vec { | |||||
| dmap.set_item( | |||||
| py, | |||||
| PyBytes::new(py, &filename[..]), | |||||
| decapsule_make_dirstate_tuple(py)?( | |||||
| entry.state as c_char, | |||||
| entry.mode, | |||||
| entry.size, | |||||
| entry.mtime, | |||||
| ), | |||||
| )?; | |||||
| } | |||||
| for CopyVecEntry { path, copy_path } in copies { | |||||
| copymap.set_item( | |||||
| py, | |||||
| PyBytes::new(py, path), | |||||
| PyBytes::new(py, copy_path), | |||||
| )?; | |||||
| } | |||||
| Ok((PyBytes::new(py, parents.p1), PyBytes::new(py, parents.p2)) | |||||
| .to_py_object(py)) | |||||
| } | |||||
| Err(e) => Err(PyErr::new::<exc::ValueError, _>( | |||||
| py, | |||||
| match e { | |||||
| DirstateParseError::TooLittleData => { | |||||
| "too little data for parents".to_string() | |||||
| } | |||||
| DirstateParseError::Overflow => { | |||||
| "overflow in dirstate".to_string() | |||||
| } | |||||
| DirstateParseError::CorruptedEntry(e) => e, | |||||
| }, | |||||
| )), | |||||
| } | |||||
| } | |||||
| fn extract_dirstate_vec( | |||||
| py: Python, | py: Python, | ||||
| dmap: &PyDict, | dmap: &PyDict, | ||||
| ) -> Result<DirstateVec, PyErr> { | ) -> Result<DirstateVec, PyErr> { | ||||
| dmap.items(py) | dmap.items(py) | ||||
| .iter() | .iter() | ||||
| .map(|(filename, stats)| { | .map(|(filename, stats)| { | ||||
| let stats = stats.extract::<PySequence>(py)?; | let stats = stats.extract::<PySequence>(py)?; | ||||
| let state = stats.get_item(py, 0)?.extract::<PyBytes>(py)?; | let state = stats.get_item(py, 0)?.extract::<PyBytes>(py)?; | ||||
| size, | size, | ||||
| mtime, | mtime, | ||||
| }, | }, | ||||
| )) | )) | ||||
| }) | }) | ||||
| .collect() | .collect() | ||||
| } | } | ||||
| fn pack_dirstate_wrapper( | |||||
| py: Python, | |||||
| dmap: PyDict, | |||||
| copymap: PyDict, | |||||
| pl: PyTuple, | |||||
| now: PyInt, | |||||
| ) -> PyResult<PyBytes> { | |||||
| let p1 = pl.get_item(py, 0).extract::<PyBytes>(py)?; | |||||
| let p1: &[u8] = p1.data(py); | |||||
| let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?; | |||||
| let p2: &[u8] = p2.data(py); | |||||
| let dirstate_vec = extract_dirstate_vec(py, &dmap)?; | |||||
| let copies: Result<HashMap<Vec<u8>, Vec<u8>>, PyErr> = copymap | |||||
| .items(py) | |||||
| .iter() | |||||
| .map(|(key, value)| { | |||||
| Ok(( | |||||
| key.extract::<PyBytes>(py)?.data(py).to_owned(), | |||||
| value.extract::<PyBytes>(py)?.data(py).to_owned(), | |||||
| )) | |||||
| }) | |||||
| .collect(); | |||||
| match pack_dirstate( | |||||
| &dirstate_vec, | |||||
| &copies?, | |||||
| DirstateParents { p1, p2 }, | |||||
| now.as_object().extract::<i32>(py)?, | |||||
| ) { | |||||
| Ok((packed, new_dirstate_vec)) => { | |||||
| for ( | |||||
| filename, | |||||
| DirstateEntry { | |||||
| state, | |||||
| mode, | |||||
| size, | |||||
| mtime, | |||||
| }, | |||||
| ) in new_dirstate_vec | |||||
| { | |||||
| dmap.set_item( | |||||
| py, | |||||
| PyBytes::new(py, &filename[..]), | |||||
| decapsule_make_dirstate_tuple(py)?( | |||||
| state as c_char, | |||||
| mode, | |||||
| size, | |||||
| mtime, | |||||
| ), | |||||
| )?; | |||||
| } | |||||
| Ok(PyBytes::new(py, &packed)) | |||||
| } | |||||
| Err(error) => Err(PyErr::new::<exc::ValueError, _>( | |||||
| py, | |||||
| match error { | |||||
| DirstatePackError::CorruptedParent => { | |||||
| "expected a 20-byte hash".to_string() | |||||
| } | |||||
| DirstatePackError::CorruptedEntry(e) => e, | |||||
| DirstatePackError::BadSize(expected, actual) => { | |||||
| format!("bad dirstate size: {} != {}", actual, expected) | |||||
| } | |||||
| }, | |||||
| )), | |||||
| } | |||||
| } | |||||
| /// Create the module, with `__package__` given from parent | /// Create the module, with `__package__` given from parent | ||||
| pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { | pub fn init_module(py: Python, package: &str) -> PyResult<PyModule> { | ||||
| let dotted_name = &format!("{}.dirstate", package); | let dotted_name = &format!("{}.dirstate", package); | ||||
| let m = PyModule::new(py, dotted_name)?; | let m = PyModule::new(py, dotted_name)?; | ||||
| m.add(py, "__package__", package)?; | m.add(py, "__package__", package)?; | ||||
| m.add(py, "__doc__", "Dirstate - Rust implementation")?; | m.add(py, "__doc__", "Dirstate - Rust implementation")?; | ||||
| m.add( | |||||
| py, | |||||
| "parse_dirstate", | |||||
| py_fn!( | |||||
| py, | |||||
| parse_dirstate_wrapper(dmap: PyDict, copymap: PyDict, st: PyBytes) | |||||
| ), | |||||
| )?; | |||||
| m.add( | |||||
| py, | |||||
| "pack_dirstate", | |||||
| py_fn!( | |||||
| py, | |||||
| pack_dirstate_wrapper( | |||||
| dmap: PyDict, | |||||
| copymap: PyDict, | |||||
| pl: PyTuple, | |||||
| now: PyInt | |||||
| ) | |||||
| ), | |||||
| )?; | |||||
| m.add_class::<Dirs>(py)?; | m.add_class::<Dirs>(py)?; | ||||
| let sys = PyModule::import(py, "sys")?; | let sys = PyModule::import(py, "sys")?; | ||||
| let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?; | let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?; | ||||
| sys_modules.set_item(py, dotted_name, &m)?; | sys_modules.set_item(py, dotted_name, &m)?; | ||||
| Ok(m) | Ok(m) | ||||
| } | } | ||||
| #[macro_use] | #[macro_use] | ||||
| extern crate cpython; | extern crate cpython; | ||||
| pub mod ancestors; | pub mod ancestors; | ||||
| mod cindex; | mod cindex; | ||||
| mod conversion; | mod conversion; | ||||
| pub mod dagops; | pub mod dagops; | ||||
| pub mod dirstate; | pub mod dirstate; | ||||
| pub mod parsers; | |||||
| pub mod discovery; | pub mod discovery; | ||||
| pub mod exceptions; | pub mod exceptions; | ||||
| pub mod filepatterns; | pub mod filepatterns; | ||||
| py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| { | py_module_initializer!(rustext, initrustext, PyInit_rustext, |py, m| { | ||||
| m.add( | m.add( | ||||
| py, | py, | ||||
| "__doc__", | "__doc__", | ||||
| "Mercurial core concepts - Rust implementation", | "Mercurial core concepts - Rust implementation", | ||||
| )?; | )?; | ||||
| let dotted_name: String = m.get(py, "__name__")?.extract(py)?; | let dotted_name: String = m.get(py, "__name__")?.extract(py)?; | ||||
| m.add(py, "ancestor", ancestors::init_module(py, &dotted_name)?)?; | m.add(py, "ancestor", ancestors::init_module(py, &dotted_name)?)?; | ||||
| m.add(py, "dagop", dagops::init_module(py, &dotted_name)?)?; | m.add(py, "dagop", dagops::init_module(py, &dotted_name)?)?; | ||||
| m.add(py, "discovery", discovery::init_module(py, &dotted_name)?)?; | m.add(py, "discovery", discovery::init_module(py, &dotted_name)?)?; | ||||
| m.add(py, "dirstate", dirstate::init_module(py, &dotted_name)?)?; | m.add(py, "dirstate", dirstate::init_module(py, &dotted_name)?)?; | ||||
| m.add( | m.add( | ||||
| py, | py, | ||||
| "filepatterns", | "filepatterns", | ||||
| filepatterns::init_module(py, &dotted_name)?, | filepatterns::init_module(py, &dotted_name)?, | ||||
| )?; | )?; | ||||
| m.add( | |||||
| py, | |||||
| "parsers", | |||||
| parsers::init_parsers_module(py, &dotted_name)?, | |||||
| )?; | |||||
| m.add(py, "GraphError", py.get_type::<exceptions::GraphError>())?; | m.add(py, "GraphError", py.get_type::<exceptions::GraphError>())?; | ||||
| m.add( | m.add( | ||||
| py, | py, | ||||
| "PatternFileError", | "PatternFileError", | ||||
| py.get_type::<exceptions::PatternFileError>(), | py.get_type::<exceptions::PatternFileError>(), | ||||
| )?; | )?; | ||||
| m.add( | m.add( | ||||
| py, | py, | ||||
| "PatternError", | "PatternError", | ||||
| py.get_type::<exceptions::PatternError>(), | py.get_type::<exceptions::PatternError>(), | ||||
| )?; | )?; | ||||
| Ok(()) | Ok(()) | ||||
| }); | }); | ||||
| // parsers.rs | |||||
| // | |||||
| // Copyright 2019 Raphaël Gomès <rgomes@octobus.net> | |||||
| // | |||||
| // This software may be used and distributed according to the terms of the | |||||
| // GNU General Public License version 2 or any later version. | |||||
| //! Bindings for the `hg::dirstate::parsers` module provided by the | |||||
| //! `hg-core` package. | |||||
| //! | |||||
| //! From Python, this will be seen as `mercurial.rustext.parsers` | |||||
| //! | |||||
| use cpython::{ | |||||
| exc, PyBytes, PyDict, PyErr, PyInt, PyModule, PyResult, PyTuple, Python, | |||||
| PythonObject, ToPyObject, | |||||
| }; | |||||
| use hg::{ | |||||
| pack_dirstate, parse_dirstate, CopyVecEntry, DirstateEntry, | |||||
| DirstatePackError, DirstateParents, DirstateParseError, | |||||
| }; | |||||
| use std::collections::HashMap; | |||||
| use libc::c_char; | |||||
| use crate::dirstate::{decapsule_make_dirstate_tuple, extract_dirstate_vec}; | |||||
| fn parse_dirstate_wrapper( | |||||
| py: Python, | |||||
| dmap: PyDict, | |||||
| copymap: PyDict, | |||||
| st: PyBytes, | |||||
| ) -> PyResult<PyTuple> { | |||||
| match parse_dirstate(st.data(py)) { | |||||
| Ok((parents, dirstate_vec, copies)) => { | |||||
| for (filename, entry) in dirstate_vec { | |||||
| dmap.set_item( | |||||
| py, | |||||
| PyBytes::new(py, &filename[..]), | |||||
| decapsule_make_dirstate_tuple(py)?( | |||||
| entry.state as c_char, | |||||
| entry.mode, | |||||
| entry.size, | |||||
| entry.mtime, | |||||
| ), | |||||
| )?; | |||||
| } | |||||
| for CopyVecEntry { path, copy_path } in copies { | |||||
| copymap.set_item( | |||||
| py, | |||||
| PyBytes::new(py, path), | |||||
| PyBytes::new(py, copy_path), | |||||
| )?; | |||||
| } | |||||
| Ok((PyBytes::new(py, parents.p1), PyBytes::new(py, parents.p2)) | |||||
| .to_py_object(py)) | |||||
| } | |||||
| Err(e) => Err(PyErr::new::<exc::ValueError, _>( | |||||
| py, | |||||
| match e { | |||||
| DirstateParseError::TooLittleData => { | |||||
| "too little data for parents".to_string() | |||||
| } | |||||
| DirstateParseError::Overflow => { | |||||
| "overflow in dirstate".to_string() | |||||
| } | |||||
| DirstateParseError::CorruptedEntry(e) => e, | |||||
| }, | |||||
| )), | |||||
| } | |||||
| } | |||||
| fn pack_dirstate_wrapper( | |||||
| py: Python, | |||||
| dmap: PyDict, | |||||
| copymap: PyDict, | |||||
| pl: PyTuple, | |||||
| now: PyInt, | |||||
| ) -> PyResult<PyBytes> { | |||||
| let p1 = pl.get_item(py, 0).extract::<PyBytes>(py)?; | |||||
| let p1: &[u8] = p1.data(py); | |||||
| let p2 = pl.get_item(py, 1).extract::<PyBytes>(py)?; | |||||
| let p2: &[u8] = p2.data(py); | |||||
| let dirstate_vec = extract_dirstate_vec(py, &dmap)?; | |||||
| let copies: Result<HashMap<Vec<u8>, Vec<u8>>, PyErr> = copymap | |||||
| .items(py) | |||||
| .iter() | |||||
| .map(|(key, value)| { | |||||
| Ok(( | |||||
| key.extract::<PyBytes>(py)?.data(py).to_owned(), | |||||
| value.extract::<PyBytes>(py)?.data(py).to_owned(), | |||||
| )) | |||||
| }) | |||||
| .collect(); | |||||
| match pack_dirstate( | |||||
| &dirstate_vec, | |||||
| &copies?, | |||||
| DirstateParents { p1, p2 }, | |||||
| now.as_object().extract::<i32>(py)?, | |||||
| ) { | |||||
| Ok((packed, new_dirstate_vec)) => { | |||||
| for ( | |||||
| filename, | |||||
| DirstateEntry { | |||||
| state, | |||||
| mode, | |||||
| size, | |||||
| mtime, | |||||
| }, | |||||
| ) in new_dirstate_vec | |||||
| { | |||||
| dmap.set_item( | |||||
| py, | |||||
| PyBytes::new(py, &filename[..]), | |||||
| decapsule_make_dirstate_tuple(py)?( | |||||
| state as c_char, | |||||
| mode, | |||||
| size, | |||||
| mtime, | |||||
| ), | |||||
| )?; | |||||
| } | |||||
| Ok(PyBytes::new(py, &packed)) | |||||
| } | |||||
| Err(error) => Err(PyErr::new::<exc::ValueError, _>( | |||||
| py, | |||||
| match error { | |||||
| DirstatePackError::CorruptedParent => { | |||||
| "expected a 20-byte hash".to_string() | |||||
| } | |||||
| DirstatePackError::CorruptedEntry(e) => e, | |||||
| DirstatePackError::BadSize(expected, actual) => { | |||||
| format!("bad dirstate size: {} != {}", actual, expected) | |||||
| } | |||||
| }, | |||||
| )), | |||||
| } | |||||
| } | |||||
| /// Create the module, with `__package__` given from parent | |||||
| pub fn init_parsers_module(py: Python, package: &str) -> PyResult<PyModule> { | |||||
| let dotted_name = &format!("{}.parsers", package); | |||||
| let m = PyModule::new(py, dotted_name)?; | |||||
| m.add(py, "__package__", package)?; | |||||
| m.add(py, "__doc__", "Parsers - Rust implementation")?; | |||||
| m.add( | |||||
| py, | |||||
| "parse_dirstate", | |||||
| py_fn!( | |||||
| py, | |||||
| parse_dirstate_wrapper(dmap: PyDict, copymap: PyDict, st: PyBytes) | |||||
| ), | |||||
| )?; | |||||
| m.add( | |||||
| py, | |||||
| "pack_dirstate", | |||||
| py_fn!( | |||||
| py, | |||||
| pack_dirstate_wrapper( | |||||
| dmap: PyDict, | |||||
| copymap: PyDict, | |||||
| pl: PyTuple, | |||||
| now: PyInt | |||||
| ) | |||||
| ), | |||||
| )?; | |||||
| let sys = PyModule::import(py, "sys")?; | |||||
| let sys_modules: PyDict = sys.get(py, "modules")?.extract(py)?; | |||||
| sys_modules.set_item(py, dotted_name, &m)?; | |||||
| Ok(m) | |||||
| } | |||||
| configtable = {} | configtable = {} | ||||
| configitem = registrar.configitem(configtable) | configitem = registrar.configitem(configtable) | ||||
| configitem(b'fakedirstatewritetime', b'fakenow', | configitem(b'fakedirstatewritetime', b'fakenow', | ||||
| default=None, | default=None, | ||||
| ) | ) | ||||
| parsers = policy.importmod(r'parsers') | parsers = policy.importmod(r'parsers') | ||||
| rustmod = policy.importrust(r'parsers') | |||||
| def pack_dirstate(fakenow, orig, dmap, copymap, pl, now): | def pack_dirstate(fakenow, orig, dmap, copymap, pl, now): | ||||
| # execute what original parsers.pack_dirstate should do actually | # execute what original parsers.pack_dirstate should do actually | ||||
| # for consistency | # for consistency | ||||
| actualnow = int(now) | actualnow = int(now) | ||||
| for f, e in dmap.items(): | for f, e in dmap.items(): | ||||
| if e[0] == 'n' and e[3] == actualnow: | if e[0] == 'n' and e[3] == actualnow: | ||||
| e = parsers.dirstatetuple(e[0], e[1], e[2], -1) | e = parsers.dirstatetuple(e[0], e[1], e[2], -1) | ||||
| # because replacing 'parsers.pack_dirstate' is also effective | # because replacing 'parsers.pack_dirstate' is also effective | ||||
| # in subrepos. | # in subrepos. | ||||
| return func() | return func() | ||||
| # parsing 'fakenow' in YYYYmmddHHMM format makes comparison between | # parsing 'fakenow' in YYYYmmddHHMM format makes comparison between | ||||
| # 'fakenow' value and 'touch -t YYYYmmddHHMM' argument easy | # 'fakenow' value and 'touch -t YYYYmmddHHMM' argument easy | ||||
| fakenow = dateutil.parsedate(fakenow, [b'%Y%m%d%H%M'])[0] | fakenow = dateutil.parsedate(fakenow, [b'%Y%m%d%H%M'])[0] | ||||
| if rustext is not None: | if rustmod is not None: | ||||
| orig_module = rustext.dirstate | # The Rust implementation does not use public parse/pack dirstate | ||||
| orig_pack_dirstate = rustext.dirstate.pack_dirstate | # to prevent conversion round-trips | ||||
| else: | orig_dirstatemap_write = dirstate.dirstatemap.write | ||||
| orig_module = parsers | wrapper = lambda self, st, now: orig_dirstatemap_write(self, | ||||
| orig_pack_dirstate = parsers.pack_dirstate | st, | ||||
| fakenow) | |||||
| dirstate.dirstatemap.write = wrapper | |||||
| orig_dirstate_getfsnow = dirstate._getfsnow | orig_dirstate_getfsnow = dirstate._getfsnow | ||||
| wrapper = lambda *args: pack_dirstate(fakenow, orig_pack_dirstate, *args) | wrapper = lambda *args: pack_dirstate(fakenow, orig_pack_dirstate, *args) | ||||
| orig_module = parsers | |||||
| orig_pack_dirstate = parsers.pack_dirstate | |||||
| orig_module.pack_dirstate = wrapper | orig_module.pack_dirstate = wrapper | ||||
| dirstate._getfsnow = lambda *args: fakenow | dirstate._getfsnow = lambda *args: fakenow | ||||
| try: | try: | ||||
| return func() | return func() | ||||
| finally: | finally: | ||||
| orig_module.pack_dirstate = orig_pack_dirstate | orig_module.pack_dirstate = orig_pack_dirstate | ||||
| dirstate._getfsnow = orig_dirstate_getfsnow | dirstate._getfsnow = orig_dirstate_getfsnow | ||||
| if rustmod is not None: | |||||
| dirstate.dirstatemap.write = orig_dirstatemap_write | |||||
| def _poststatusfixup(orig, workingctx, status, fixup): | def _poststatusfixup(orig, workingctx, status, fixup): | ||||
| ui = workingctx.repo().ui | ui = workingctx.repo().ui | ||||
| return fakewrite(ui, lambda : orig(workingctx, status, fixup)) | return fakewrite(ui, lambda : orig(workingctx, status, fixup)) | ||||
| def markcommitted(orig, committablectx, node): | def markcommitted(orig, committablectx, node): | ||||
| ui = committablectx.repo().ui | ui = committablectx.repo().ui | ||||
| return fakewrite(ui, lambda : orig(committablectx, node)) | return fakewrite(ui, lambda : orig(committablectx, node)) | ||||
| def extsetup(ui): | def extsetup(ui): | ||||
| extensions.wrapfunction(context.workingctx, '_poststatusfixup', | extensions.wrapfunction(context.workingctx, '_poststatusfixup', | ||||
| _poststatusfixup) | _poststatusfixup) | ||||
| extensions.wrapfunction(context.workingctx, 'markcommitted', | extensions.wrapfunction(context.workingctx, 'markcommitted', | ||||
| markcommitted) | markcommitted) | ||||