Skip to content

Commit 79b473a

Browse files
committed
feat!: Add version of Graph that handles fully-parsed commits
This renames `graph::Commit` to `graph::LazyCommit` to make space for `graph::Commit` to be a fully owned. `LazyCommit::to_owned()` was added to obtain fully owned `Commit` instances. Rename `Graph::try_lookup_and_insert()` to `Graph::try_lookup_or_insert()` and `Graph::try_lookup_and_insert_default()` to `Graph::try_lookup_or_insert_default()`
1 parent ff96f58 commit 79b473a

File tree

8 files changed

+460
-249
lines changed

8 files changed

+460
-249
lines changed

gix-revision/src/graph/commit.rs

+148
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
use super::LazyCommit;
2+
use crate::graph::{Commit, CommitterTimestamp, Either, Generation};
3+
use smallvec::SmallVec;
4+
5+
impl<'graph> LazyCommit<'graph> {
6+
/// Return an iterator over the parents of this commit.
7+
pub fn iter_parents(&self) -> Parents<'graph> {
8+
let backing = match &self.backing {
9+
Either::Left(buf) => Either::Left(gix_object::CommitRefIter::from_bytes(buf)),
10+
Either::Right((cache, pos)) => Either::Right((*cache, cache.commit_at(*pos).iter_parents())),
11+
};
12+
Parents { backing }
13+
}
14+
15+
/// Returns the timestamp at which this commit was created.
16+
///
17+
/// This is the single-most important date for determining recency of commits.
18+
/// Note that this can only fail if the commit is backed by the object database *and* parsing fails.
19+
pub fn committer_timestamp(&self) -> Result<CommitterTimestamp, gix_object::decode::Error> {
20+
Ok(match &self.backing {
21+
Either::Left(buf) => {
22+
gix_object::CommitRefIter::from_bytes(buf)
23+
.committer()?
24+
.time
25+
.seconds_since_unix_epoch as CommitterTimestamp
26+
}
27+
Either::Right((cache, pos)) => cache.commit_at(*pos).committer_timestamp(),
28+
})
29+
}
30+
31+
/// Returns the generation of the commit if it is backed by a commit graph.
32+
pub fn generation(&self) -> Option<Generation> {
33+
match &self.backing {
34+
Either::Left(_) => None,
35+
Either::Right((cache, pos)) => cache.commit_at(*pos).generation().into(),
36+
}
37+
}
38+
39+
/// Convert ourselves into an owned version, which effectively detaches us from the underlying graph.
40+
/// Use `new_data()` to provide the `data` field for the owned `Commit`.
41+
pub fn to_owned<T>(&self, new_data: impl FnOnce() -> T) -> Result<Commit<T>, to_owned::Error> {
42+
let data = new_data();
43+
Ok(match &self.backing {
44+
Either::Left(buf) => {
45+
use gix_object::commit::ref_iter::Token;
46+
let iter = gix_object::CommitRefIter::from_bytes(buf);
47+
let mut parents = SmallVec::default();
48+
let mut timestamp = None;
49+
for token in iter {
50+
match token? {
51+
Token::Tree { .. } => {}
52+
Token::Parent { id } => parents.push(id),
53+
Token::Author { .. } => {}
54+
Token::Committer { signature } => {
55+
timestamp = Some(signature.time.seconds_since_unix_epoch as CommitterTimestamp);
56+
break;
57+
}
58+
_ => {
59+
unreachable!(
60+
"we break naturally after seeing the committer which is always at the same spot"
61+
)
62+
}
63+
}
64+
}
65+
Commit {
66+
parents,
67+
commit_time: timestamp.unwrap_or_default(),
68+
generation: None,
69+
data,
70+
}
71+
}
72+
Either::Right((cache, pos)) => {
73+
let mut parents = SmallVec::default();
74+
let commit = cache.commit_at(*pos);
75+
for pos in commit.iter_parents() {
76+
let pos = pos?;
77+
parents.push(cache.commit_at(pos).id().to_owned());
78+
}
79+
Commit {
80+
parents,
81+
commit_time: commit.committer_timestamp(),
82+
generation: Some(commit.generation()),
83+
data,
84+
}
85+
}
86+
})
87+
}
88+
}
89+
90+
/// An iterator over the parents of a commit.
91+
pub struct Parents<'graph> {
92+
backing: Either<
93+
gix_object::CommitRefIter<'graph>,
94+
(
95+
&'graph gix_commitgraph::Graph,
96+
gix_commitgraph::file::commit::Parents<'graph>,
97+
),
98+
>,
99+
}
100+
101+
impl<'graph> Iterator for Parents<'graph> {
102+
type Item = Result<gix_hash::ObjectId, iter_parents::Error>;
103+
104+
fn next(&mut self) -> Option<Self::Item> {
105+
match &mut self.backing {
106+
Either::Left(it) => {
107+
for token in it {
108+
match token {
109+
Ok(gix_object::commit::ref_iter::Token::Tree { .. }) => continue,
110+
Ok(gix_object::commit::ref_iter::Token::Parent { id }) => return Some(Ok(id)),
111+
Ok(_unused_token) => break,
112+
Err(err) => return Some(Err(err.into())),
113+
}
114+
}
115+
None
116+
}
117+
Either::Right((cache, it)) => it
118+
.next()
119+
.map(|r| r.map(|pos| cache.id_at(pos).to_owned()).map_err(Into::into)),
120+
}
121+
}
122+
}
123+
124+
///
125+
pub mod iter_parents {
126+
/// The error returned by the [`Parents`][super::Parents] iterator.
127+
#[derive(Debug, thiserror::Error)]
128+
#[allow(missing_docs)]
129+
pub enum Error {
130+
#[error("An error occurred when parsing commit parents")]
131+
DecodeCommit(#[from] gix_object::decode::Error),
132+
#[error("An error occurred when parsing parents from the commit graph")]
133+
DecodeCommitGraph(#[from] gix_commitgraph::file::commit::Error),
134+
}
135+
}
136+
137+
///
138+
pub mod to_owned {
139+
/// The error returned by [`to_owned()`][crate::graph::LazyCommit::to_owned()].
140+
#[derive(Debug, thiserror::Error)]
141+
#[allow(missing_docs)]
142+
pub enum Error {
143+
#[error("A commit could not be decoded during traversal")]
144+
Decode(#[from] gix_object::decode::Error),
145+
#[error("Could not find commit position in graph when traversing parents")]
146+
CommitGraphParent(#[from] gix_commitgraph::file::commit::Error),
147+
}
148+
}

gix-revision/src/graph/errors.rs

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
///
2+
pub mod lookup {
3+
/// The error returned by [`try_lookup()`][crate::Graph::try_lookup()].
4+
#[derive(Debug, thiserror::Error)]
5+
#[error("There was an error looking up a commit")]
6+
pub struct Error {
7+
#[from]
8+
source: Box<dyn std::error::Error + Send + Sync + 'static>,
9+
}
10+
11+
///
12+
pub mod commit {
13+
/// The error returned by [`try_lookup_commit()`][crate::Graph::try_lookup_commit()].
14+
#[derive(Debug, thiserror::Error)]
15+
#[allow(missing_docs)]
16+
pub enum Error {
17+
#[error("There was an error looking up a commit")]
18+
Find(#[from] crate::graph::lookup::Error),
19+
#[error(transparent)]
20+
ToOwned(#[from] crate::graph::commit::to_owned::Error),
21+
}
22+
}
23+
24+
///
25+
pub mod existing {
26+
/// The error returned by [`lookup()`][crate::Graph::lookup()].
27+
#[derive(Debug, thiserror::Error)]
28+
#[allow(missing_docs)]
29+
pub enum Error {
30+
#[error(transparent)]
31+
Find(#[from] super::Error),
32+
#[error("Commit could not be found")]
33+
Missing,
34+
}
35+
}
36+
}
37+
38+
///
39+
pub mod insert_parents {
40+
use crate::graph::commit::iter_parents;
41+
use crate::graph::lookup;
42+
43+
/// The error returned by [`insert_parents()`][crate::Graph::insert_parents()].
44+
#[derive(Debug, thiserror::Error)]
45+
#[allow(missing_docs)]
46+
pub enum Error {
47+
#[error(transparent)]
48+
Lookup(#[from] lookup::existing::Error),
49+
#[error("A commit could not be decoded during traversal")]
50+
Decode(#[from] gix_object::decode::Error),
51+
#[error(transparent)]
52+
Parent(#[from] iter_parents::Error),
53+
}
54+
}

0 commit comments

Comments
 (0)