swh_graph_stdlib/
vcs.rs

1// Copyright (C) 2024  The Software Heritage developers
2// See the AUTHORS file at the top-level directory of this distribution
3// License: GNU General Public License version 3, or any later version
4// See top-level LICENSE file for more information
5
6//! Version control system (VCS) functions.
7
8use anyhow::{ensure, Result};
9
10use swh_graph::graph::*;
11use swh_graph::labels::{EdgeLabel, LabelNameId};
12use swh_graph::properties;
13use swh_graph::NodeType;
14
15/// Names of references ("branches") that are considered to be pointing to the
16/// HEAD revision in a VCS, by [find_head_rev] below. Names are tried in order,
17/// when attempting to identify the HEAD revision.
18pub const HEAD_REF_NAMES: [&str; 2] = ["refs/heads/main", "refs/heads/master"];
19
20/// Given a graph and a snapshot node in it, return the node id of the revision
21/// pointed by the HEAD branch, it it exists.
22pub fn find_head_rev<G>(graph: &G, snp: NodeId) -> Result<Option<NodeId>>
23where
24    G: SwhLabeledForwardGraph + SwhGraphWithProperties,
25    <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
26    <G as SwhGraphWithProperties>::Maps: properties::Maps,
27{
28    let props = graph.properties();
29    let head_ref_name_ids: Vec<LabelNameId> = HEAD_REF_NAMES
30        .into_iter()
31        .filter_map(|name| props.label_name_id(name.as_bytes()).ok())
32        // Note, we ignore errors on purpose here, because some ref names that
33        // do exist on the SWH graph might be missing in user-provided graphs,
34        // and will fail [label_name_id] call.
35        .collect();
36    find_head_rev_by_refs(graph, snp, &head_ref_name_ids)
37}
38
39/// Same as [find_head_rev], but with the ability to configure which branch
40/// names correspond to the HEAD revision.
41///
42/// Note: this function is also more efficient than [find_head_rev], because
43/// branch names are pre-resolved to integers once (before calling this
44/// function) and compared as integers.  See the source code of [find_head_rev]
45/// for an example of how to translate a given set of branch names (like
46/// [HEAD_REF_NAMES]) to label-name IDs for this function.
47pub fn find_head_rev_by_refs<G>(
48    graph: &G,
49    snp: NodeId,
50    ref_name_ids: &[LabelNameId],
51) -> Result<Option<NodeId>>
52where
53    G: SwhLabeledForwardGraph + SwhGraphWithProperties,
54    <G as SwhGraphWithProperties>::Maps: properties::Maps,
55    <G as SwhGraphWithProperties>::LabelNames: properties::LabelNames,
56{
57    let props = graph.properties();
58    let node_type = props.node_type(snp);
59    ensure!(
60        node_type == NodeType::Snapshot,
61        "Type of {snp} should be snp, but is {node_type} instead"
62    );
63    for (succ, labels) in graph.labeled_successors(snp) {
64        let node_type = props.node_type(succ);
65        if node_type != NodeType::Revision && node_type != NodeType::Release {
66            continue;
67        }
68        for label in labels {
69            if let EdgeLabel::Branch(branch) = label {
70                if ref_name_ids.contains(&branch.label_name_id()) {
71                    return Ok(Some(succ));
72                }
73            }
74        }
75    }
76    Ok(None)
77}