diff --git a/rust/hg-core/src/dagops.rs b/rust/hg-core/src/dagops.rs --- a/rust/hg-core/src/dagops.rs +++ b/rust/hg-core/src/dagops.rs @@ -81,6 +81,27 @@ pub fn retain_heads( Ok(()) } +/// Roots of `revs`, passed as a `HashSet` +/// +/// They are returned in arbitrary order +pub fn roots( + graph: &G, + revs: &HashSet, +) -> Result, GraphError> { + let mut roots: Vec = Vec::new(); + for rev in revs { + if graph + .parents(*rev)? + .iter() + .filter(|p| **p != NULL_REVISION) + .all(|p| !revs.contains(p)) + { + roots.push(*rev); + } + } + Ok(roots) +} + /// Compute the topological range between two collections of revisions /// /// This is equivalent to the revset `::`. @@ -203,6 +224,30 @@ mod tests { Ok(()) } + /// Apply `roots()` and sort the result for easier comparison + fn roots_sorted( + graph: &impl Graph, + revs: &[Revision], + ) -> Result, GraphError> { + let mut as_vec = roots(graph, &revs.iter().cloned().collect())?; + as_vec.sort(); + Ok(as_vec) + } + + #[test] + fn test_roots() -> Result<(), GraphError> { + assert_eq!(roots_sorted(&SampleGraph, &[4, 5, 6])?, vec![4]); + assert_eq!( + roots_sorted(&SampleGraph, &[4, 1, 6, 12, 0])?, + vec![0, 4, 12] + ); + assert_eq!( + roots_sorted(&SampleGraph, &[1, 2, 3, 4, 5, 6, 7, 8, 9])?, + vec![1, 8] + ); + Ok(()) + } + /// Apply `range()` and convert the result into a Vec for easier comparison fn range_vec( graph: impl Graph + Clone,