Skip to content

Commit 5d9e8cf

Browse files
committed
radicle-surf: refactor design: part 4 (WIP)
- Update design doc. - Refactor History API. - Add more test cases. Signed-off-by: Han Xu <[email protected]>
1 parent 7fe6993 commit 5d9e8cf

File tree

7 files changed

+117
-79
lines changed

7 files changed

+117
-79
lines changed

radicle-surf/docs/refactor-design.md

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,9 @@ I think we should use `Rev` in our API as much as possible.
9898

9999
#### How to identify History
100100

101-
TBD
101+
Currently we are keeping the basic `History` type, and only change it from
102+
a TupleStruct to a regular Struct. The goal is to make it easier to refactor
103+
later.
102104

103105
### Code browsing
104106

@@ -143,17 +145,37 @@ The core API is:
143145

144146
```Rust
145147
imp RepositoryRef {
148+
/// Returns the diff between two revisions.
146149
pub fn diff(&self, from: &Rev, to: &Rev) -> Result<Diff, Error>;
150+
151+
/// Returns the diff between a revision and the initial state of the repo.
152+
pub fn initial_diff(&self, rev: &Rev) -> Result<Diff, Error> {
153+
154+
/// Returns the diff of a specific revision.
155+
pub fn diff_of_rev(&self, rev: &Rev) -> Result<Diff, Error>;
147156
}
148157
```
149158

150-
To help convert from `Oid` to `Rev`, we provide a helper method:
159+
### Retrieve the history
160+
161+
The user would be able to get the list of previous commits reachable from a
162+
particular commit or a ref.
163+
164+
The main change is to move `get_history` in `Vcs` trait to `history()` in
165+
`RepositoryRef`.
166+
151167
```Rust
152168
imp RepositoryRef {
153-
/// Returns the Oid of `rev`.
154-
pub fn rev_oid(&self, rev: &Rev) -> Result<Oid, Error>;
169+
pub fn history(&self, rev: &Rev) -> Result<History, Error>;
155170
}
156171
```
172+
TBD: how to retrieve the history of a particular file? Is it a valid use case?
173+
174+
### Retrieve a specific object: all its metadata
175+
176+
Git has four different types of objects: Blob, Tree, Commit and Tag. The `blob`
177+
and `tree` are low-level to git and so I don't think we should expose them in
178+
our API.
157179

158180
## Error handling
159181

radicle-surf/src/commit.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,7 @@ where
241241
};
242242

243243
let stats = repo.get_stats(&rev)?;
244-
let headers = repo.history(rev)?.iter().map(Header::from).collect();
244+
let headers = repo.history(&rev)?.iter().map(Header::from).collect();
245245

246246
Ok(Commits { headers, stats })
247247
}

radicle-surf/src/object/tree.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,8 @@ where
155155
entries.sort_by(|a, b| a.info.object_type.cmp(&b.info.object_type));
156156

157157
let last_commit = if path.is_root() {
158-
Some(commit::Header::from(repo.history(rev).unwrap().first()))
158+
let history = repo.history(&rev)?;
159+
Some(commit::Header::from(history.first()))
159160
} else {
160161
None
161162
};

radicle-surf/src/vcs.rs

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -21,41 +21,44 @@ use nonempty::NonEmpty;
2121

2222
pub mod git;
2323

24-
/// A non-empty bag of artifacts which are used to
25-
/// derive a [`crate::file_system::Directory`] view. Examples of artifacts
26-
/// would be commits in Git or patches in Pijul.
24+
/// A non-empty bag of artifacts. Examples of artifacts
25+
/// would be commits in Git.
2726
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
28-
pub struct History<A>(pub NonEmpty<A>);
27+
pub struct History<A> {
28+
items: NonEmpty<A>,
29+
}
2930

3031
impl<A> History<A> {
3132
/// Create a new `History` consisting of one artifact.
3233
pub fn new(a: A) -> Self {
33-
History(NonEmpty::new(a))
34+
History {
35+
items: NonEmpty::new(a),
36+
}
3437
}
3538

3639
/// Push an artifact to the end of the `History`.
3740
pub fn push(&mut self, a: A) {
38-
self.0.push(a)
41+
self.items.push(a)
3942
}
4043

4144
/// Iterator over the artifacts.
4245
pub fn iter(&self) -> impl Iterator<Item = &A> {
43-
self.0.iter()
46+
self.items.iter()
4447
}
4548

4649
/// Get the firest artifact in the `History`.
4750
pub fn first(&self) -> &A {
48-
self.0.first()
51+
self.items.first()
4952
}
5053

5154
/// Get the length of `History` (aka the artefacts count)
5255
pub fn len(&self) -> usize {
53-
self.0.len()
56+
self.items.len()
5457
}
5558

5659
/// Check if `History` is empty
5760
pub fn is_empty(&self) -> bool {
58-
self.0.is_empty()
61+
self.items.is_empty()
5962
}
6063

6164
/// Given that the `History` is topological order from most
@@ -76,15 +79,17 @@ impl<A> History<A> {
7679
.collect::<Vec<_>>(),
7780
);
7881

79-
new_history.map(History)
82+
new_history.map(|x| History { items: x })
8083
}
8184

8285
/// Apply a function from `A` to `B` over the `History`
8386
pub fn map<F, B>(self, f: F) -> History<B>
8487
where
8588
F: FnMut(A) -> B,
8689
{
87-
History(self.0.map(f))
90+
History {
91+
items: self.items.map(f),
92+
}
8893
}
8994

9095
/// Find an artifact in the `History`.

radicle-surf/src/vcs/git/reference.rs

Lines changed: 1 addition & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ use std::{fmt, str};
1919

2020
use thiserror::Error;
2121

22-
use crate::vcs::git::{repo::RepositoryRef, BranchName, Namespace, TagName};
22+
use crate::vcs::git::{BranchName, Namespace, TagName};
2323
use radicle_git_ext::Oid;
2424
pub(super) mod glob;
2525

@@ -93,15 +93,6 @@ impl Ref {
9393

9494
ref_namespace
9595
}
96-
97-
/// We try to find a [`git2::Reference`] based off of a `Ref` by turning the
98-
/// ref into a fully qualified ref (e.g. refs/remotes/**/master).
99-
pub fn find_ref<'a>(
100-
&self,
101-
repo: &RepositoryRef<'a>,
102-
) -> Result<git2::Reference<'a>, git2::Error> {
103-
repo.repo_ref.find_reference(&self.to_string())
104-
}
10596
}
10697

10798
impl fmt::Display for Ref {

radicle-surf/src/vcs/git/repo.rs

Lines changed: 32 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,6 @@ use crate::{
1919
diff::*,
2020
file_system,
2121
file_system::{directory, DirectoryContents, Label},
22-
vcs,
2322
vcs::git::{
2423
error::*,
2524
reference::{glob::RefGlob, Ref, Rev},
@@ -35,7 +34,6 @@ use crate::{
3534
},
3635
};
3736
use directory::Directory;
38-
use nonempty::NonEmpty;
3937
use radicle_git_ext::Oid;
4038
use std::{
4139
collections::{BTreeSet, HashSet},
@@ -51,7 +49,7 @@ pub(super) enum CommitHistory {
5149
}
5250

5351
/// A `History` that uses `git2::Commit` as the underlying artifact.
54-
pub type History = vcs::History<Commit>;
52+
pub type History = crate::vcs::History<Commit>;
5553

5654
/// Wrapper around the `git2`'s `git2::Repository` type.
5755
/// This is to to limit the functionality that we can do
@@ -142,18 +140,6 @@ impl<'a> RepositoryRef<'a> {
142140
Ok(namespaces?.into_iter().collect())
143141
}
144142

145-
pub(super) fn ref_history<R>(&self, reference: R) -> Result<History, Error>
146-
where
147-
R: Into<Ref>,
148-
{
149-
let reference = match self.which_namespace()? {
150-
None => reference.into(),
151-
Some(namespace) => reference.into().namespaced(namespace),
152-
}
153-
.find_ref(self)?;
154-
self.to_history(&reference)
155-
}
156-
157143
/// Get the [`Diff`] between two commits.
158144
pub fn diff(&self, from: &Rev, to: &Rev) -> Result<Diff, Error> {
159145
let from_commit = self.rev_to_commit(from)?;
@@ -169,6 +155,19 @@ impl<'a> RepositoryRef<'a> {
169155
.and_then(|diff| Diff::try_from(diff).map_err(Error::from))
170156
}
171157

158+
/// Get the diff introduced by a particlar rev.
159+
pub fn diff_of_rev(&self, rev: &Rev) -> Result<Diff, Error> {
160+
let oid = self.rev_oid(rev)?;
161+
let commit = self.get_commit(oid)?;
162+
match commit.parents.first() {
163+
Some(parent) => {
164+
let parent_rev = (*parent).into();
165+
self.diff(&parent_rev, rev)
166+
},
167+
None => self.initial_diff(rev),
168+
}
169+
}
170+
172171
/// Parse an [`Oid`] from the given string.
173172
pub fn oid(&self, oid: &str) -> Result<Oid, Error> {
174173
Ok(self.repo_ref.revparse_single(oid)?.id().into())
@@ -198,7 +197,7 @@ impl<'a> RepositoryRef<'a> {
198197
/// Gets stats of `rev`.
199198
pub fn get_stats(&self, rev: &Rev) -> Result<Stats, Error> {
200199
let branches = self.list_branches(RefScope::Local)?.len();
201-
let history = self.history(rev.clone())?;
200+
let history = self.history(rev)?;
202201
let commits = history.len();
203202

204203
let contributors = history
@@ -256,10 +255,19 @@ impl<'a> RepositoryRef<'a> {
256255
pub(super) fn rev_to_commit(&self, rev: &Rev) -> Result<git2::Commit, Error> {
257256
match rev {
258257
Rev::Oid(oid) => Ok(self.repo_ref.find_commit((*oid).into())?),
259-
Rev::Ref(reference) => Ok(reference.find_ref(self)?.peel_to_commit()?),
258+
Rev::Ref(reference) => Ok(self.find_git2_ref(reference)?.peel_to_commit()?),
260259
}
261260
}
262261

262+
fn find_git2_ref(&self, reference: &Ref) -> Result<git2::Reference<'_>, Error> {
263+
let reference = match self.which_namespace()? {
264+
None => reference.clone(),
265+
Some(namespace) => reference.clone().namespaced(namespace),
266+
};
267+
let git2_ref = self.repo_ref.find_reference(&reference.to_string())?;
268+
Ok(git2_ref)
269+
}
270+
263271
/// Switch to a `namespace`
264272
pub fn switch_namespace(&self, namespace: &str) -> Result<(), Error> {
265273
Ok(self.repo_ref.set_namespace(namespace)?)
@@ -271,18 +279,11 @@ impl<'a> RepositoryRef<'a> {
271279
Ok(commit)
272280
}
273281

274-
/// Turn a [`git2::Reference`] into a [`History`] by completing
275-
/// a revwalk over the first commit in the reference.
276-
pub(super) fn to_history(&self, history: &git2::Reference<'a>) -> Result<History, Error> {
277-
let head = history.peel_to_commit()?;
278-
self.commit_to_history(head)
279-
}
280-
281282
/// Turn a [`git2::Reference`] into a [`History`] by completing
282283
/// a revwalk over the first commit in the reference.
283284
pub(super) fn commit_to_history(&self, head: git2::Commit) -> Result<History, Error> {
284285
let head_id = head.id();
285-
let mut commits = NonEmpty::new(Commit::try_from(head)?);
286+
let mut commits = History::new(Commit::try_from(head)?);
286287
let mut revwalk = self.repo_ref.revwalk()?;
287288

288289
// Set the revwalk to the head commit
@@ -302,7 +303,7 @@ impl<'a> RepositoryRef<'a> {
302303
commits.push(commit);
303304
}
304305

305-
Ok(vcs::History(commits))
306+
Ok(commits)
306307
}
307308

308309
/// Extract the signature from a commit
@@ -363,7 +364,7 @@ impl<'a> RepositoryRef<'a> {
363364
Ok(other == git2_oid || is_descendant)
364365
}
365366

366-
/// Get the history of the file system where the head of the [`NonEmpty`] is
367+
/// Get the history of the file system where the head of the `commit` is
367368
/// the latest commit.
368369
pub(super) fn file_history(
369370
&self,
@@ -531,14 +532,9 @@ impl<'a> RepositoryRef<'a> {
531532
}
532533

533534
/// Returns the history of `rev`.
534-
pub fn history(&self, rev: Rev) -> Result<History, Error> {
535-
match rev {
536-
Rev::Ref(reference) => self.ref_history(reference),
537-
Rev::Oid(oid) => {
538-
let commit = self.get_git2_commit(oid)?;
539-
self.commit_to_history(commit)
540-
},
541-
}
535+
pub fn history(&self, rev: &Rev) -> Result<History, Error> {
536+
let commit = self.rev_to_commit(rev)?;
537+
self.commit_to_history(commit)
542538
}
543539
}
544540

@@ -562,7 +558,7 @@ impl Repository {
562558

563559
/// Since our operations are read-only when it comes to surfing a repository
564560
/// we have a separate struct called [`RepositoryRef`]. This turns an owned
565-
/// [`Repository`], the one returend by [`Repository::new`], into a
561+
/// [`Repository`], the one returned by [`Repository::new`], into a
566562
/// [`RepositoryRef`].
567563
pub fn as_ref(&'_ self) -> RepositoryRef<'_> {
568564
RepositoryRef { repo_ref: &self.0 }

0 commit comments

Comments
 (0)