aboutsummaryrefslogtreecommitdiff
path: root/commitquery/query_is_ancestor.go
diff options
context:
space:
mode:
Diffstat (limited to 'commitquery/query_is_ancestor.go')
-rw-r--r--commitquery/query_is_ancestor.go49
1 files changed, 49 insertions, 0 deletions
diff --git a/commitquery/query_is_ancestor.go b/commitquery/query_is_ancestor.go
new file mode 100644
index 00000000..c21892c8
--- /dev/null
+++ b/commitquery/query_is_ancestor.go
@@ -0,0 +1,49 @@
+package commitquery
+
+import objectid "codeberg.org/lindenii/furgit/object/id"
+
+// IsAncestor reports whether ancestor is reachable from descendant through
+// commit parent edges.
+//
+// Both inputs are peeled through annotated tags before commit traversal.
+func (query *query) IsAncestor(ancestor, descendant objectid.ObjectID) (bool, error) {
+ ancestorIdx, err := query.resolveCommitish(ancestor)
+ if err != nil {
+ return false, err
+ }
+
+ descendantIdx, err := query.resolveCommitish(descendant)
+ if err != nil {
+ return false, err
+ }
+
+ return query.isAncestor(ancestorIdx, descendantIdx)
+}
+
+// isAncestor answers one ancestry query between two resolved internal nodes.
+func (query *query) isAncestor(ancestor, descendant nodeIndex) (bool, error) {
+ if ancestor == descendant {
+ return true, nil
+ }
+
+ ancestorGeneration := query.effectiveGeneration(ancestor)
+ descendantGeneration := query.effectiveGeneration(descendant)
+
+ if ancestorGeneration != generationInfinity &&
+ descendantGeneration != generationInfinity &&
+ ancestorGeneration > descendantGeneration {
+ return false, nil
+ }
+
+ minGeneration := uint64(0)
+ if ancestorGeneration != generationInfinity {
+ minGeneration = ancestorGeneration
+ }
+
+ err := query.paintDownToCommon(ancestor, []nodeIndex{descendant}, minGeneration)
+ if err != nil {
+ return false, err
+ }
+
+ return query.hasAnyMarks(ancestor, markRight), nil
+}