1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
use crate::revision;
#[cfg(feature = "revision")]
use crate::{bstr::BStr, Id};
/// Methods for resolving revisions by spec or working with the commit graph.
impl crate::Repository {
/// Parse a revision specification and turn it into the object(s) it describes, similar to `git rev-parse`.
///
/// # Deviation
///
/// - `@` actually stands for `HEAD`, whereas `git` resolves it to the object pointed to by `HEAD` without making the
/// `HEAD` ref available for lookups.
#[doc(alias = "revparse", alias = "git2")]
#[cfg(feature = "revision")]
pub fn rev_parse<'a>(&self, spec: impl Into<&'a BStr>) -> Result<revision::Spec<'_>, gix_error::Error> {
revision::Spec::from_bstr(
spec,
self,
revision::spec::parse::Options {
object_kind_hint: self.config.object_kind_hint,
..Default::default()
},
)
}
/// Parse a revision specification and return single object id as represented by this instance.
///
/// # Examples
///
/// ```
/// # fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
/// # mod doctest { include!(concat!(env!("CARGO_MANIFEST_DIR"), "/tests/doctest.rs")); }
/// # let repo = doctest::open_repo(doctest::basic_repo_dir()?)?;
/// let parent = repo.rev_parse_single("HEAD^")?.object()?.into_commit();
///
/// assert_eq!(parent.message_raw()?, "c1\n");
/// assert_ne!(parent.id, repo.rev_parse_single("HEAD")?);
/// # Ok(()) }
/// ```
#[doc(alias = "revparse_single", alias = "git2")]
#[cfg(feature = "revision")]
pub fn rev_parse_single<'repo, 'a>(
&'repo self,
spec: impl Into<&'a BStr>,
) -> Result<Id<'repo>, revision::spec::parse::single::Error> {
let spec = spec.into();
self.rev_parse(spec)?
.single()
.ok_or(revision::spec::parse::single::Error::RangedRev { spec: spec.into() })
}
/// Obtain the best merge-base between commit `one` and `two`, or fail if there is none.
///
/// # Performance
/// For repeated calls, prefer [`merge_base_with_cache()`](crate::Repository::merge_base_with_graph()).
/// Also be sure to [set an object cache](crate::Repository::object_cache_size_if_unset) to accelerate repeated commit lookups.
#[cfg(feature = "revision")]
pub fn merge_base(
&self,
one: impl Into<gix_hash::ObjectId>,
two: impl Into<gix_hash::ObjectId>,
) -> Result<Id<'_>, super::merge_base::Error> {
use crate::prelude::ObjectIdExt;
let one = one.into();
let two = two.into();
let cache = self.commit_graph_if_enabled()?;
let mut graph = self.revision_graph(cache.as_ref());
let bases = gix_revision::merge_base(one, &[two], &mut graph)?.ok_or(super::merge_base::Error::NotFound {
first: one,
second: two,
})?;
Ok(bases.first().attach(self))
}
/// Obtain the best merge-base between commit `one` and `two`, or fail if there is none, providing a
/// commit-graph `graph` to potentially greatly accelerate the operation by reusing graphs from previous runs.
///
/// # Performance
/// Be sure to [set an object cache](crate::Repository::object_cache_size_if_unset) to accelerate repeated commit lookups.
#[cfg(feature = "revision")]
pub fn merge_base_with_graph(
&self,
one: impl Into<gix_hash::ObjectId>,
two: impl Into<gix_hash::ObjectId>,
graph: &mut gix_revwalk::Graph<'_, '_, gix_revwalk::graph::Commit<gix_revision::merge_base::Flags>>,
) -> Result<Id<'_>, super::merge_base_with_graph::Error> {
use crate::prelude::ObjectIdExt;
let one = one.into();
let two = two.into();
let bases =
gix_revision::merge_base(one, &[two], graph)?.ok_or(super::merge_base_with_graph::Error::NotFound {
first: one,
second: two,
})?;
Ok(bases.first().attach(self))
}
/// Get all merge-bases between commit `one` and `others`, or an empty list if there is none, providing a
/// commit-graph `graph` to potentially greatly speed up the operation.
///
/// # Performance
/// Be sure to [set an object cache](crate::Repository::object_cache_size_if_unset) to speed up repeated commit lookups.
#[doc(alias = "merge_bases_many", alias = "git2")]
#[cfg(feature = "revision")]
pub fn merge_bases_many_with_graph(
&self,
one: impl Into<gix_hash::ObjectId>,
others: &[gix_hash::ObjectId],
graph: &mut gix_revwalk::Graph<'_, '_, gix_revwalk::graph::Commit<gix_revision::merge_base::Flags>>,
) -> Result<Vec<Id<'_>>, gix_revision::merge_base::Error> {
use crate::prelude::ObjectIdExt;
let one = one.into();
Ok(match gix_revision::merge_base(one, others, graph)? {
Some(bases) => bases.into_iter().map(|id| id.attach(self)).collect(),
None => Vec::new(),
})
}
/// Like [`merge_bases_many_with_graph()`](Self::merge_bases_many_with_graph), but without the ability to speed up consecutive calls with a [graph](gix_revwalk::Graph).
///
/// # Performance
///
/// Be sure to [set an object cache](crate::Repository::object_cache_size_if_unset) to speed up repeated commit lookups, and consider
/// using [`merge_bases_many_with_graph()`](Self::merge_bases_many_with_graph) for consecutive calls.
#[doc(alias = "git2")]
#[cfg(feature = "revision")]
pub fn merge_bases_many(
&self,
one: impl Into<gix_hash::ObjectId>,
others: &[gix_hash::ObjectId],
) -> Result<Vec<Id<'_>>, crate::repository::merge_bases_many::Error> {
let cache = self.commit_graph_if_enabled()?;
let mut graph = self.revision_graph(cache.as_ref());
Ok(self.merge_bases_many_with_graph(one, others, &mut graph)?)
}
/// Return the best merge-base among all `commits`, or fail if `commits` yields no commit or no merge-base was found.
///
/// Use `graph` to speed up repeated calls.
#[cfg(feature = "revision")]
pub fn merge_base_octopus_with_graph(
&self,
commits: impl IntoIterator<Item = impl Into<gix_hash::ObjectId>>,
graph: &mut gix_revwalk::Graph<'_, '_, gix_revwalk::graph::Commit<gix_revision::merge_base::Flags>>,
) -> Result<Id<'_>, crate::repository::merge_base_octopus_with_graph::Error> {
use crate::{prelude::ObjectIdExt, repository::merge_base_octopus_with_graph};
let commits: Vec<_> = commits.into_iter().map(Into::into).collect();
let first = commits
.first()
.copied()
.ok_or(merge_base_octopus_with_graph::Error::MissingCommit)?;
gix_revision::merge_base::octopus(first, &commits[1..], graph)?
.ok_or(merge_base_octopus_with_graph::Error::NoMergeBase)
.map(|id| id.attach(self))
}
/// Return the best merge-base among all `commits`, or fail if `commits` yields no commit or no merge-base was found.
///
/// For repeated calls, prefer [`Self::merge_base_octopus_with_graph()`] for cache-reuse.
#[cfg(feature = "revision")]
pub fn merge_base_octopus(
&self,
commits: impl IntoIterator<Item = impl Into<gix_hash::ObjectId>>,
) -> Result<Id<'_>, crate::repository::merge_base_octopus::Error> {
let cache = self.commit_graph_if_enabled()?;
let mut graph = self.revision_graph(cache.as_ref());
Ok(self.merge_base_octopus_with_graph(commits, &mut graph)?)
}
/// Create the baseline for a revision walk by initializing it with the `tips` to start iterating on.
///
/// It can be configured further before starting the actual walk.
#[doc(alias = "revwalk", alias = "git2")]
pub fn rev_walk(
&self,
tips: impl IntoIterator<Item = impl Into<gix_hash::ObjectId>>,
) -> revision::walk::Platform<'_> {
revision::walk::Platform::new(tips, self)
}
}