We are not doing any is_ancestor call anymore. So we can drop that logic and
associated arguments.
Details
Details
Diff Detail
Diff Detail
- Repository
- rHG Mercurial
- Branch
- default
- Lint
No Linters Available - Unit
No Unit Test Coverage
( )
We are not doing any is_ancestor call anymore. So we can drop that logic and
associated arguments.
| No Linters Available |
| No Unit Test Coverage |
| Path | Packages | |||
|---|---|---|---|---|
| M | mercurial/copies.py (2 lines) | |||
| M | rust/hg-core/src/copy_tracing.rs (95 lines) | |||
| M | rust/hg-cpython/src/copy_tracing.rs (27 lines) |
| Commit | Parents | Author | Summary | Date |
|---|---|---|---|---|
| 6c03b423f7dd | a632383f4604 | Pierre-Yves David | Dec 15 2020, 12:22 PM |
| It returns the aggregated copies information for `targetrev`. | It returns the aggregated copies information for `targetrev`. | ||||
| """ | """ | ||||
| alwaysmatch = match.always() | alwaysmatch = match.always() | ||||
| if rustmod is not None: | if rustmod is not None: | ||||
| final_copies = rustmod.combine_changeset_copies( | final_copies = rustmod.combine_changeset_copies( | ||||
| list(revs), children_count, targetrev, revinfo, isancestor | list(revs), children_count, targetrev, revinfo | ||||
| ) | ) | ||||
| else: | else: | ||||
| isancestor = cached_is_ancestor(isancestor) | isancestor = cached_is_ancestor(isancestor) | ||||
| all_copies = {} | all_copies = {} | ||||
| # iterate over all the "children" side of copy tracing "edge" | # iterate over all the "children" side of copy tracing "edge" | ||||
| for current_rev in revs: | for current_rev in revs: | ||||
| p1, p2, changes = revinfo(current_rev) | p1, p2, changes = revinfo(current_rev) | ||||
| }; | }; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| MergeCase::Normal | MergeCase::Normal | ||||
| } | } | ||||
| } | } | ||||
| /// A struct responsible for answering "is X ancestors of Y" quickly | |||||
| /// | |||||
| /// The structure will delegate ancestors call to a callback, and cache the | |||||
| /// result. | |||||
| #[derive(Debug)] | |||||
| struct AncestorOracle<'a, A: Fn(Revision, Revision) -> bool> { | |||||
| inner: &'a A, | |||||
| pairs: HashMap<(Revision, Revision), bool>, | |||||
| } | |||||
| impl<'a, A: Fn(Revision, Revision) -> bool> AncestorOracle<'a, A> { | |||||
| fn new(func: &'a A) -> Self { | |||||
| Self { | |||||
| inner: func, | |||||
| pairs: HashMap::default(), | |||||
| } | |||||
| } | |||||
| fn record_overwrite(&mut self, anc: Revision, desc: Revision) { | |||||
| self.pairs.insert((anc, desc), true); | |||||
| } | |||||
| /// returns `true` if `anc` is an ancestors of `desc`, `false` otherwise | |||||
| fn is_overwrite(&mut self, anc: Revision, desc: Revision) -> bool { | |||||
| if anc > desc { | |||||
| false | |||||
| } else if anc == desc { | |||||
| true | |||||
| } else { | |||||
| if let Some(b) = self.pairs.get(&(anc, desc)) { | |||||
| *b | |||||
| } else { | |||||
| let b = (self.inner)(anc, desc); | |||||
| self.pairs.insert((anc, desc), b); | |||||
| b | |||||
| } | |||||
| } | |||||
| } | |||||
| } | |||||
| struct ActionsIterator<'a> { | struct ActionsIterator<'a> { | ||||
| changes: &'a ChangedFiles<'a>, | changes: &'a ChangedFiles<'a>, | ||||
| parent: Parent, | parent: Parent, | ||||
| current: u32, | current: u32, | ||||
| } | } | ||||
| impl<'a> Iterator for ActionsIterator<'a> { | impl<'a> Iterator for ActionsIterator<'a> { | ||||
| type Item = Action<'a>; | type Item = Action<'a>; | ||||
| /// children: a {parent ? [childrens]} mapping | /// children: a {parent ? [childrens]} mapping | ||||
| /// target_rev: the final revision we are combining copies to | /// target_rev: the final revision we are combining copies to | ||||
| /// rev_info(rev): callback to get revision information: | /// rev_info(rev): callback to get revision information: | ||||
| /// * first parent | /// * first parent | ||||
| /// * second parent | /// * second parent | ||||
| /// * ChangedFiles | /// * ChangedFiles | ||||
| /// isancestors(low_rev, high_rev): callback to check if a revision is an | /// isancestors(low_rev, high_rev): callback to check if a revision is an | ||||
| /// ancestor of another | /// ancestor of another | ||||
| pub fn combine_changeset_copies<A: Fn(Revision, Revision) -> bool, D>( | pub fn combine_changeset_copies<D>( | ||||
| revs: Vec<Revision>, | revs: Vec<Revision>, | ||||
| mut children_count: HashMap<Revision, usize>, | mut children_count: HashMap<Revision, usize>, | ||||
| target_rev: Revision, | target_rev: Revision, | ||||
| rev_info: RevInfoMaker<D>, | rev_info: RevInfoMaker<D>, | ||||
| is_ancestor: &A, | |||||
| ) -> PathCopies { | ) -> PathCopies { | ||||
| let mut all_copies = HashMap::new(); | let mut all_copies = HashMap::new(); | ||||
| let mut oracle = AncestorOracle::new(is_ancestor); | |||||
| let mut path_map = TwoWayPathMap::default(); | let mut path_map = TwoWayPathMap::default(); | ||||
| for rev in revs { | for rev in revs { | ||||
| let mut d: DataHolder<D> = DataHolder { data: None }; | let mut d: DataHolder<D> = DataHolder { data: None }; | ||||
| let (p1, p2, changes) = rev_info(rev, &mut d); | let (p1, p2, changes) = rev_info(rev, &mut d); | ||||
| // We will chain the copies information accumulated for the parent with | // We will chain the copies information accumulated for the parent with | ||||
| // the individual copies information the curent revision. Creating a | // the individual copies information the curent revision. Creating a | ||||
| // new TimeStampedPath for each `rev` โ `children` vertex. | // new TimeStampedPath for each `rev` โ `children` vertex. | ||||
| let mut copies: Option<InternalPathCopies> = None; | let mut copies: Option<InternalPathCopies> = None; | ||||
| if p1 != NULL_REVISION { | if p1 != NULL_REVISION { | ||||
| // Retrieve data computed in a previous iteration | // Retrieve data computed in a previous iteration | ||||
| let parent_copies = get_and_clean_parent_copies( | let parent_copies = get_and_clean_parent_copies( | ||||
| &mut all_copies, | &mut all_copies, | ||||
| &mut children_count, | &mut children_count, | ||||
| p1, | p1, | ||||
| ); | ); | ||||
| if let Some(parent_copies) = parent_copies { | if let Some(parent_copies) = parent_copies { | ||||
| // combine it with data for that revision | // combine it with data for that revision | ||||
| let vertex_copies = add_from_changes( | let vertex_copies = add_from_changes( | ||||
| &mut path_map, | &mut path_map, | ||||
| &mut oracle, | |||||
| &parent_copies, | &parent_copies, | ||||
| &changes, | &changes, | ||||
| Parent::FirstParent, | Parent::FirstParent, | ||||
| rev, | rev, | ||||
| ); | ); | ||||
| // keep that data around for potential later combination | // keep that data around for potential later combination | ||||
| copies = Some(vertex_copies); | copies = Some(vertex_copies); | ||||
| } | } | ||||
| } | } | ||||
| if p2 != NULL_REVISION { | if p2 != NULL_REVISION { | ||||
| // Retrieve data computed in a previous iteration | // Retrieve data computed in a previous iteration | ||||
| let parent_copies = get_and_clean_parent_copies( | let parent_copies = get_and_clean_parent_copies( | ||||
| &mut all_copies, | &mut all_copies, | ||||
| &mut children_count, | &mut children_count, | ||||
| p2, | p2, | ||||
| ); | ); | ||||
| if let Some(parent_copies) = parent_copies { | if let Some(parent_copies) = parent_copies { | ||||
| // combine it with data for that revision | // combine it with data for that revision | ||||
| let vertex_copies = add_from_changes( | let vertex_copies = add_from_changes( | ||||
| &mut path_map, | &mut path_map, | ||||
| &mut oracle, | |||||
| &parent_copies, | &parent_copies, | ||||
| &changes, | &changes, | ||||
| Parent::SecondParent, | Parent::SecondParent, | ||||
| rev, | rev, | ||||
| ); | ); | ||||
| copies = match copies { | copies = match copies { | ||||
| None => Some(vertex_copies), | None => Some(vertex_copies), | ||||
| // Merge has two parents needs to combines their copy | // Merge has two parents needs to combines their copy | ||||
| // information. | // information. | ||||
| // | // | ||||
| // If we got data from both parents, We need to combine | // If we got data from both parents, We need to combine | ||||
| // them. | // them. | ||||
| Some(copies) => Some(merge_copies_dict( | Some(copies) => Some(merge_copies_dict( | ||||
| &path_map, | &path_map, | ||||
| rev, | rev, | ||||
| vertex_copies, | vertex_copies, | ||||
| copies, | copies, | ||||
| &changes, | &changes, | ||||
| &mut oracle, | |||||
| )), | )), | ||||
| }; | }; | ||||
| } | } | ||||
| } | } | ||||
| match copies { | match copies { | ||||
| Some(copies) => { | Some(copies) => { | ||||
| all_copies.insert(rev, copies); | all_copies.insert(rev, copies); | ||||
| } | } | ||||
| Some(c) => Some(c.clone()), | Some(c) => Some(c.clone()), | ||||
| None => Some(InternalPathCopies::default()), | None => Some(InternalPathCopies::default()), | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /// Combine ChangedFiles with some existing PathCopies information and return | /// Combine ChangedFiles with some existing PathCopies information and return | ||||
| /// the result | /// the result | ||||
| fn add_from_changes<A: Fn(Revision, Revision) -> bool>( | fn add_from_changes( | ||||
| path_map: &mut TwoWayPathMap, | path_map: &mut TwoWayPathMap, | ||||
| oracle: &mut AncestorOracle<A>, | |||||
| base_copies: &InternalPathCopies, | base_copies: &InternalPathCopies, | ||||
| changes: &ChangedFiles, | changes: &ChangedFiles, | ||||
| parent: Parent, | parent: Parent, | ||||
| current_rev: Revision, | current_rev: Revision, | ||||
| ) -> InternalPathCopies { | ) -> InternalPathCopies { | ||||
| let mut copies = base_copies.clone(); | let mut copies = base_copies.clone(); | ||||
| for action in changes.iter_actions(parent) { | for action in changes.iter_actions(parent) { | ||||
| match action { | match action { | ||||
| // information. See merge_copies_dict for details. | // information. See merge_copies_dict for details. | ||||
| match copies.entry(dest) { | match copies.entry(dest) { | ||||
| Entry::Vacant(slot) => { | Entry::Vacant(slot) => { | ||||
| let ttpc = CopySource::new(current_rev, entry); | let ttpc = CopySource::new(current_rev, entry); | ||||
| slot.insert(ttpc); | slot.insert(ttpc); | ||||
| } | } | ||||
| Entry::Occupied(mut slot) => { | Entry::Occupied(mut slot) => { | ||||
| let ttpc = slot.get_mut(); | let ttpc = slot.get_mut(); | ||||
| oracle.record_overwrite(ttpc.rev, current_rev); | |||||
| ttpc.overwrite(current_rev, entry); | ttpc.overwrite(current_rev, entry); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| Action::Removed(deleted_path) => { | Action::Removed(deleted_path) => { | ||||
| // We must drop copy information for removed file. | // We must drop copy information for removed file. | ||||
| // | // | ||||
| // We need to explicitly record them as dropped to | // We need to explicitly record them as dropped to | ||||
| // propagate this information when merging two | // propagate this information when merging two | ||||
| // InternalPathCopies object. | // InternalPathCopies object. | ||||
| let deleted = path_map.tokenize(deleted_path); | let deleted = path_map.tokenize(deleted_path); | ||||
| copies.entry(deleted).and_modify(|old| { | copies.entry(deleted).and_modify(|old| { | ||||
| oracle.record_overwrite(old.rev, current_rev); | |||||
| old.mark_delete(current_rev); | old.mark_delete(current_rev); | ||||
| }); | }); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| copies | copies | ||||
| } | } | ||||
| /// merge two copies-mapping together, minor and major | /// merge two copies-mapping together, minor and major | ||||
| /// | /// | ||||
| /// In case of conflict, value from "major" will be picked, unless in some | /// In case of conflict, value from "major" will be picked, unless in some | ||||
| /// cases. See inline documentation for details. | /// cases. See inline documentation for details. | ||||
| fn merge_copies_dict<A: Fn(Revision, Revision) -> bool>( | fn merge_copies_dict( | ||||
| path_map: &TwoWayPathMap, | path_map: &TwoWayPathMap, | ||||
| current_merge: Revision, | current_merge: Revision, | ||||
| mut minor: InternalPathCopies, | mut minor: InternalPathCopies, | ||||
| mut major: InternalPathCopies, | mut major: InternalPathCopies, | ||||
| changes: &ChangedFiles, | changes: &ChangedFiles, | ||||
| oracle: &mut AncestorOracle<A>, | |||||
| ) -> InternalPathCopies { | ) -> InternalPathCopies { | ||||
| // This closure exist as temporary help while multiple developper are | // This closure exist as temporary help while multiple developper are | ||||
| // actively working on this code. Feel free to re-inline it once this | // actively working on this code. Feel free to re-inline it once this | ||||
| // code is more settled. | // code is more settled. | ||||
| let cmp_value = |oracle: &mut AncestorOracle<A>, | let cmp_value = | ||||
| dest: &PathToken, | |dest: &PathToken, src_minor: &CopySource, src_major: &CopySource| { | ||||
| src_minor: &CopySource, | |||||
| src_major: &CopySource| { | |||||
| compare_value( | compare_value( | ||||
| path_map, | path_map, | ||||
| current_merge, | current_merge, | ||||
| changes, | changes, | ||||
| oracle, | |||||
| dest, | dest, | ||||
| src_minor, | src_minor, | ||||
| src_major, | src_major, | ||||
| ) | ) | ||||
| }; | }; | ||||
| if minor.is_empty() { | if minor.is_empty() { | ||||
| major | major | ||||
| } else if major.is_empty() { | } else if major.is_empty() { | ||||
| minor | minor | ||||
| } else if minor.len() * 2 < major.len() { | } else if minor.len() * 2 < major.len() { | ||||
| // Lets says we are merging two InternalPathCopies instance A and B. | // Lets says we are merging two InternalPathCopies instance A and B. | ||||
| // | // | ||||
| // If A contains N items, the merge result will never contains more | // If A contains N items, the merge result will never contains more | ||||
| for (dest, src_minor) in minor { | for (dest, src_minor) in minor { | ||||
| let src_major = major.get(&dest); | let src_major = major.get(&dest); | ||||
| match src_major { | match src_major { | ||||
| None => { | None => { | ||||
| major.insert(dest, src_minor); | major.insert(dest, src_minor); | ||||
| } | } | ||||
| Some(src_major) => { | Some(src_major) => { | ||||
| let (pick, overwrite) = | let (pick, overwrite) = | ||||
| cmp_value(oracle, &dest, &src_minor, src_major); | cmp_value(&dest, &src_minor, src_major); | ||||
| if overwrite { | if overwrite { | ||||
| oracle.record_overwrite(src_minor.rev, current_merge); | |||||
| oracle.record_overwrite(src_major.rev, current_merge); | |||||
| let src = match pick { | let src = match pick { | ||||
| MergePick::Major => CopySource::new_from_merge( | MergePick::Major => CopySource::new_from_merge( | ||||
| current_merge, | current_merge, | ||||
| src_major, | src_major, | ||||
| &src_minor, | &src_minor, | ||||
| ), | ), | ||||
| MergePick::Minor => CopySource::new_from_merge( | MergePick::Minor => CopySource::new_from_merge( | ||||
| current_merge, | current_merge, | ||||
| for (dest, src_major) in major { | for (dest, src_major) in major { | ||||
| let src_minor = minor.get(&dest); | let src_minor = minor.get(&dest); | ||||
| match src_minor { | match src_minor { | ||||
| None => { | None => { | ||||
| minor.insert(dest, src_major); | minor.insert(dest, src_major); | ||||
| } | } | ||||
| Some(src_minor) => { | Some(src_minor) => { | ||||
| let (pick, overwrite) = | let (pick, overwrite) = | ||||
| cmp_value(oracle, &dest, src_minor, &src_major); | cmp_value(&dest, src_minor, &src_major); | ||||
| if overwrite { | if overwrite { | ||||
| oracle.record_overwrite(src_minor.rev, current_merge); | |||||
| oracle.record_overwrite(src_major.rev, current_merge); | |||||
| let src = match pick { | let src = match pick { | ||||
| MergePick::Major => CopySource::new_from_merge( | MergePick::Major => CopySource::new_from_merge( | ||||
| current_merge, | current_merge, | ||||
| &src_major, | &src_major, | ||||
| src_minor, | src_minor, | ||||
| ), | ), | ||||
| MergePick::Minor => CopySource::new_from_merge( | MergePick::Minor => CopySource::new_from_merge( | ||||
| current_merge, | current_merge, | ||||
| for d in minor.diff(&major) { | for d in minor.diff(&major) { | ||||
| match d { | match d { | ||||
| DiffItem::Add(k, v) => to_minor(k, v), | DiffItem::Add(k, v) => to_minor(k, v), | ||||
| DiffItem::Remove(k, v) => to_major(k, v), | DiffItem::Remove(k, v) => to_major(k, v), | ||||
| DiffItem::Update { old, new } => { | DiffItem::Update { old, new } => { | ||||
| let (dest, src_major) = new; | let (dest, src_major) = new; | ||||
| let (_, src_minor) = old; | let (_, src_minor) = old; | ||||
| let (pick, overwrite) = | let (pick, overwrite) = | ||||
| cmp_value(oracle, dest, src_minor, src_major); | cmp_value(dest, src_minor, src_major); | ||||
| if overwrite { | if overwrite { | ||||
| oracle.record_overwrite(src_minor.rev, current_merge); | |||||
| oracle.record_overwrite(src_major.rev, current_merge); | |||||
| let src = match pick { | let src = match pick { | ||||
| MergePick::Major => CopySource::new_from_merge( | MergePick::Major => CopySource::new_from_merge( | ||||
| current_merge, | current_merge, | ||||
| src_major, | src_major, | ||||
| src_minor, | src_minor, | ||||
| ), | ), | ||||
| MergePick::Minor => CopySource::new_from_merge( | MergePick::Minor => CopySource::new_from_merge( | ||||
| current_merge, | current_merge, | ||||
| /// The "minor" (p2) side prevails | /// The "minor" (p2) side prevails | ||||
| Minor, | Minor, | ||||
| /// Any side could be used (because they are the same) | /// Any side could be used (because they are the same) | ||||
| Any, | Any, | ||||
| } | } | ||||
| /// decide which side prevails in case of conflicting values | /// decide which side prevails in case of conflicting values | ||||
| #[allow(clippy::if_same_then_else)] | #[allow(clippy::if_same_then_else)] | ||||
| fn compare_value<A: Fn(Revision, Revision) -> bool>( | fn compare_value( | ||||
| path_map: &TwoWayPathMap, | path_map: &TwoWayPathMap, | ||||
| current_merge: Revision, | current_merge: Revision, | ||||
| changes: &ChangedFiles, | changes: &ChangedFiles, | ||||
| oracle: &mut AncestorOracle<A>, | |||||
| dest: &PathToken, | dest: &PathToken, | ||||
| src_minor: &CopySource, | src_minor: &CopySource, | ||||
| src_major: &CopySource, | src_major: &CopySource, | ||||
| ) -> (MergePick, bool) { | ) -> (MergePick, bool) { | ||||
| if src_major.rev == current_merge { | if src_major.rev == current_merge { | ||||
| if src_minor.rev == current_merge { | if src_minor.rev == current_merge { | ||||
| if src_major.path.is_none() { | if src_major.path.is_none() { | ||||
| // We cannot get different copy information for both p1 and p2 | // We cannot get different copy information for both p1 and p2 | ||||
| use cpython::ObjectProtocol; | use cpython::ObjectProtocol; | ||||
| use cpython::PyBool; | |||||
| use cpython::PyBytes; | use cpython::PyBytes; | ||||
| use cpython::PyDict; | use cpython::PyDict; | ||||
| use cpython::PyList; | use cpython::PyList; | ||||
| use cpython::PyModule; | use cpython::PyModule; | ||||
| use cpython::PyObject; | use cpython::PyObject; | ||||
| use cpython::PyResult; | use cpython::PyResult; | ||||
| use cpython::PyTuple; | use cpython::PyTuple; | ||||
| use cpython::Python; | use cpython::Python; | ||||
| /// | /// | ||||
| /// See mercurial/copies.py for details | /// See mercurial/copies.py for details | ||||
| pub fn combine_changeset_copies_wrapper( | pub fn combine_changeset_copies_wrapper( | ||||
| py: Python, | py: Python, | ||||
| revs: PyList, | revs: PyList, | ||||
| children_count: PyDict, | children_count: PyDict, | ||||
| target_rev: Revision, | target_rev: Revision, | ||||
| rev_info: PyObject, | rev_info: PyObject, | ||||
| is_ancestor: PyObject, | |||||
| ) -> PyResult<PyDict> { | ) -> PyResult<PyDict> { | ||||
| let revs: PyResult<_> = | let revs: PyResult<_> = | ||||
| revs.iter(py).map(|r| Ok(r.extract(py)?)).collect(); | revs.iter(py).map(|r| Ok(r.extract(py)?)).collect(); | ||||
| // Wrap the `is_ancestor` python callback as a Rust closure | |||||
| // | |||||
| // No errors are expected from the Python side, and they will should only | |||||
| // happens in case of programing error or severe data corruption. Such | |||||
| // errors will raise panic and the rust-cpython harness will turn them into | |||||
| // Python exception. | |||||
| let is_ancestor_wrap = |anc: Revision, desc: Revision| -> bool { | |||||
| is_ancestor | |||||
| .call(py, (anc, desc), None) | |||||
| .expect( | |||||
| "rust-copy-tracing: python call to `is_ancestor` \ | |||||
| failed", | |||||
| ) | |||||
| .cast_into::<PyBool>(py) | |||||
| .expect( | |||||
| "rust-copy-tracing: python call to `is_ancestor` \ | |||||
| returned unexpected non-Bool value", | |||||
| ) | |||||
| .is_true() | |||||
| }; | |||||
| // Wrap the `rev_info_maker` python callback as a Rust closure | // Wrap the `rev_info_maker` python callback as a Rust closure | ||||
| // | // | ||||
| // No errors are expected from the Python side, and they will should only | // No errors are expected from the Python side, and they will should only | ||||
| // happens in case of programing error or severe data corruption. Such | // happens in case of programing error or severe data corruption. Such | ||||
| // errors will raise panic and the rust-cpython harness will turn them into | // errors will raise panic and the rust-cpython harness will turn them into | ||||
| // Python exception. | // Python exception. | ||||
| let rev_info_maker: RevInfoMaker<PyBytes> = | let rev_info_maker: RevInfoMaker<PyBytes> = | ||||
| Box::new(|rev: Revision, d: &mut DataHolder<PyBytes>| -> RevInfo { | Box::new(|rev: Revision, d: &mut DataHolder<PyBytes>| -> RevInfo { | ||||
| .map(|(k, v)| Ok((k.extract(py)?, v.extract(py)?))) | .map(|(k, v)| Ok((k.extract(py)?, v.extract(py)?))) | ||||
| .collect(); | .collect(); | ||||
| let res = combine_changeset_copies( | let res = combine_changeset_copies( | ||||
| revs?, | revs?, | ||||
| children_count?, | children_count?, | ||||
| target_rev, | target_rev, | ||||
| rev_info_maker, | rev_info_maker, | ||||
| &is_ancestor_wrap, | |||||
| ); | ); | ||||
| let out = PyDict::new(py); | let out = PyDict::new(py); | ||||
| for (dest, source) in res.into_iter() { | for (dest, source) in res.into_iter() { | ||||
| out.set_item( | out.set_item( | ||||
| py, | py, | ||||
| PyBytes::new(py, &dest.into_vec()), | PyBytes::new(py, &dest.into_vec()), | ||||
| PyBytes::new(py, &source.into_vec()), | PyBytes::new(py, &source.into_vec()), | ||||
| )?; | )?; | ||||
| py, | py, | ||||
| "combine_changeset_copies", | "combine_changeset_copies", | ||||
| py_fn!( | py_fn!( | ||||
| py, | py, | ||||
| combine_changeset_copies_wrapper( | combine_changeset_copies_wrapper( | ||||
| revs: PyList, | revs: PyList, | ||||
| children: PyDict, | children: PyDict, | ||||
| target_rev: Revision, | target_rev: Revision, | ||||
| rev_info: PyObject, | rev_info: PyObject | ||||
| is_ancestor: PyObject | |||||
| ) | ) | ||||
| ), | ), | ||||
| )?; | )?; | ||||
| 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) | ||||
| } | } | ||||