gix_revision/spec/parse/
delegate.rs

1use bstr::BStr;
2
3/// Usually the first methods to call when parsing a rev-spec to set an anchoring revision (which is typically a `Commit` object).
4/// Methods can be called multiple time to either try input or to parse another rev-spec that is part of a range.
5///
6/// In one case they will not be called at all, e.g. `@{[-]n}` indicates the current branch (what `HEAD` dereferences to),
7/// without ever naming it, and so does `@{upstream}` or `@{<date>}`.
8///
9/// Note that when dereferencing `HEAD` implicitly, a revision must be set for later navigation.
10pub trait Revision {
11    /// Resolve `name` as reference which might not be a valid reference name. The name may be partial like `main` or full like
12    /// `refs/heads/main` solely depending on the users input.
13    /// Symbolic referenced should be followed till their object, but objects **must not yet** be peeled.
14    fn find_ref(&mut self, name: &BStr) -> Option<()>;
15
16    /// An object prefix to disambiguate, returning `None` if it is ambiguous or wasn't found at all.
17    ///
18    /// If `hint` is set, it should be used to disambiguate multiple objects with the same prefix.
19    fn disambiguate_prefix(&mut self, prefix: gix_hash::Prefix, hint: Option<PrefixHint<'_>>) -> Option<()>;
20
21    /// Lookup the reflog of the previously set reference, or dereference `HEAD` to its reference
22    /// to obtain the ref name (as opposed to `HEAD` itself).
23    /// If there is no such reflog entry, return `None`.
24    fn reflog(&mut self, query: ReflogLookup) -> Option<()>;
25
26    /// When looking at `HEAD`, `branch_no` is the non-null checkout in the path, e.g. `1` means the last branch checked out,
27    /// `2` is the one before that.
28    /// Return `None` if there is no branch as the checkout history (via the reflog) isn't long enough.
29    fn nth_checked_out_branch(&mut self, branch_no: usize) -> Option<()>;
30
31    /// Lookup the previously set branch or dereference `HEAD` to its reference to use its name to lookup the sibling branch of `kind`
32    /// in the configuration (typically in `refs/remotes/…`). The sibling branches are always local tracking branches.
33    /// Return `None` of no such configuration exists and no sibling could be found, which is also the case for all reference outside
34    /// of `refs/heads/`.
35    /// Note that the caller isn't aware if the previously set reference is a branch or not and might call this method even though no reference
36    /// is known.
37    fn sibling_branch(&mut self, kind: SiblingBranch) -> Option<()>;
38}
39
40/// Combine one or more specs into a range of multiple.
41pub trait Kind {
42    /// Set the kind of the spec, which happens only once if it happens at all.
43    /// In case this method isn't called, assume `Single`.
44    /// Reject a kind by returning `None` to stop the parsing.
45    ///
46    /// Note that ranges don't necessarily assure that a second specification will be parsed.
47    /// If `^rev` is given, this method is called with [`spec::Kind::RangeBetween`][crate::spec::Kind::RangeBetween]
48    /// and no second specification is provided.
49    ///
50    /// Note that the method can be called even if other invariants are not fulfilled, treat these as errors.
51    fn kind(&mut self, kind: crate::spec::Kind) -> Option<()>;
52}
53
54/// Once an anchor is set one can adjust it using traversal methods.
55pub trait Navigate {
56    /// Adjust the current revision to traverse the graph according to `kind`.
57    fn traverse(&mut self, kind: Traversal) -> Option<()>;
58
59    /// Peel the current object until it reached `kind` or `None` if the chain does not contain such object.
60    fn peel_until(&mut self, kind: PeelTo<'_>) -> Option<()>;
61
62    /// Find the first revision/commit whose message matches the given `regex` (which is never empty).
63    /// to see how it should be matched.
64    /// If `negated` is `true`, the first non-match will be a match.
65    ///
66    /// If no revision is known yet, find the _youngest_ matching commit from _any_ reference, including `HEAD`.
67    /// Otherwise, only find commits reachable from the currently set revision.
68    fn find(&mut self, regex: &BStr, negated: bool) -> Option<()>;
69
70    /// Look up the given `path` at the given `stage` in the index returning its blob id,
71    /// or return `None` if it doesn't exist at this `stage`.
72    /// Note that this implies no revision is needed and no anchor is set yet.
73    ///
74    /// * `stage` ranges from 0 to 2, with 0 being the base, 1 being ours, 2 being theirs.
75    /// * `path` without prefix is relative to the root of the repository, while prefixes like `./` and `../` make it
76    ///    relative to the current working directory.
77    fn index_lookup(&mut self, path: &BStr, stage: u8) -> Option<()>;
78}
79
80/// A hint to make disambiguation when looking up prefixes possible.
81#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
82pub enum PrefixHint<'a> {
83    /// The prefix must be a commit.
84    MustBeCommit,
85    /// The prefix refers to a commit, anchored to a ref and a revision generation in its future.
86    DescribeAnchor {
87        /// The name of the reference, like `v1.2.3` or `main`.
88        ref_name: &'a BStr,
89        /// The future generation of the commit we look for, with 0 meaning the commit is referenced by
90        /// `ref_name` directly.
91        generation: usize,
92    },
93}
94
95/// A lookup into the reflog of a reference.
96#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
97pub enum ReflogLookup {
98    /// Lookup by entry, where `0` is the most recent entry, and `1` is the older one behind `0`.
99    Entry(usize),
100    /// Lookup the reflog at the given time and find the closest matching entry.
101    Date(gix_date::Time),
102}
103
104/// Define how to traverse the commit graph.
105#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
106pub enum Traversal {
107    /// Select the given parent commit of the currently selected commit, start at `1` for the first parent.
108    /// The value will never be `0`.
109    NthParent(usize),
110    /// Select the given ancestor of the currently selected commit, start at `1` for the first ancestor.
111    /// The value will never be `0`.
112    NthAncestor(usize),
113}
114
115/// Define where a tag object should be peeled to.
116#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
117pub enum PeelTo<'a> {
118    /// An object of the given kind.
119    ObjectKind(gix_object::Kind),
120    /// Ensure the object at hand exists and is valid (actually without peeling it),
121    /// without imposing any restrictions to its type.
122    /// The object needs to be looked up to assure that it is valid, but it doesn't need to be decoded.
123    ValidObject,
124    /// Follow an annotated tag object recursively until an object is found.
125    RecursiveTagObject,
126    /// The path to drill into as seen relative to the current tree-ish.
127    ///
128    /// Note that the path can be relative, and `./` and `../` prefixes are seen as relative to the current
129    /// working directory.
130    ///
131    /// The path may be empty, which makes it refer to the tree at the current revision, similar to `^{tree}`.
132    /// Note that paths like `../` are valid and refer to a tree as seen relative to the current working directory.
133    Path(&'a BStr),
134}
135
136/// The kind of sibling branch to obtain.
137#[derive(PartialEq, Eq, Debug, Hash, Ord, PartialOrd, Clone, Copy)]
138pub enum SiblingBranch {
139    /// The upstream branch as configured in `branch.<name>.remote` or `branch.<name>.merge`.
140    Upstream,
141    /// The upstream branch to which we would push.
142    Push,
143}
144
145impl SiblingBranch {
146    /// Parse `input` as branch representation, if possible.
147    pub fn parse(input: &BStr) -> Option<Self> {
148        if input.eq_ignore_ascii_case(b"u") || input.eq_ignore_ascii_case(b"upstream") {
149            SiblingBranch::Upstream.into()
150        } else if input.eq_ignore_ascii_case(b"push") {
151            SiblingBranch::Push.into()
152        } else {
153            None
154        }
155    }
156}