Skip to content

Commit

Permalink
radicle-surf: Repository.blob_at() to retrieve a blob using its oid.
Browse files Browse the repository at this point in the history
Signed-off-by: Han Xu <keepsimple@gmail.com>
  • Loading branch information
keepsimple1 committed Mar 24, 2023
1 parent 165f25f commit 25add2b
Show file tree
Hide file tree
Showing 2 changed files with 91 additions and 1 deletion.
64 changes: 64 additions & 0 deletions radicle-surf/src/repo.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ use crate::{

/// Enumeration of errors that can occur in repo operations.
pub mod error {
use crate::Oid;
use std::path::PathBuf;
use thiserror::Error;

Expand All @@ -56,6 +57,8 @@ pub mod error {
pub enum Repo {
#[error("path not found for: {0}")]
PathNotFound(PathBuf),
#[error("blob not found for: {0}")]
BlobNotFound(Oid),
}
}

Expand Down Expand Up @@ -301,6 +304,20 @@ impl Repository {
Ok(Blob::<BlobRef<'a>>::new(file.id(), git2_blob, last_commit))
}

/// Retrieves the blob with `oid` in `commit`.
pub fn blob_at<'a, C: ToCommit>(
&'a self,
commit: C,
oid: Oid,
) -> Result<Blob<BlobRef<'a>>, Error> {
let commit = commit
.to_commit(self)
.map_err(|e| Error::ToCommit(e.into()))?;
let git2_blob = self.find_blob(oid)?;
let last_commit = self.find_commit_of_blob(oid, &commit)?;
Ok(Blob::<BlobRef<'a>>::new(oid, git2_blob, last_commit))
}

/// Returns the last commit, if exists, for a `path` in the history of
/// `rev`.
pub fn last_commit<P, C>(&self, path: &P, rev: C) -> Result<Option<Commit>, Error>
Expand Down Expand Up @@ -524,6 +541,53 @@ impl Repository {
Ok(diff)
}

/// Returns true if the diff between `from` and `to` creates `oid`.
fn diff_commits_has_oid(
&self,
from: Option<&git2::Commit>,
to: &git2::Commit,
oid: &git2::Oid,
) -> Result<bool, Error> {
let diff = self.diff_commits(None, from, to)?;
for delta in diff.deltas() {
if &delta.new_file().id() == oid {
return Ok(true);
}
}
Ok(false)
}

/// Returns whether `oid` was created in `commit` or not.
fn is_oid_in_commit(&self, oid: Oid, commit: &git2::Commit) -> Result<bool, Error> {
if commit.parent_count() == 0 {
return self.diff_commits_has_oid(None, commit, oid.as_ref());
}

for p in commit.parents() {
if self.diff_commits_has_oid(Some(&p), commit, oid.as_ref())? {
return Ok(true);
}
}

Ok(false)
}

/// Returns the commit that created the blob with `oid`.
///
/// It is assumed that `oid` exists in `head`.
fn find_commit_of_blob(&self, oid: Oid, head: &Commit) -> Result<Commit, Error> {
let mut revwalk = self.revwalk()?;
revwalk.push(head.id.into())?;
for commit_id in revwalk {
let commit_id = commit_id?;
let git2_commit = self.inner.find_commit(commit_id)?;
if self.is_oid_in_commit(oid, &git2_commit)? {
return Ok(Commit::try_from(git2_commit)?);
}
}
Err(Error::Repo(error::Repo::BlobNotFound(oid)))
}

/// Returns a full reference name with namespace(s) included.
pub(crate) fn namespaced_refname<'a>(
&'a self,
Expand Down
28 changes: 27 additions & 1 deletion radicle-surf/t/src/source.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use std::path::PathBuf;

use radicle_git_ext::ref_format::refname;
use radicle_surf::{Branch, Glob, Repository};
use radicle_surf::{Branch, Glob, Oid, Repository};
use serde_json::json;

const GIT_PLATINUM: &str = "../data/git-platinum";
Expand Down Expand Up @@ -175,6 +175,32 @@ fn repo_blob() {
assert_eq!(json_ref, json_owned);
}

#[test]
fn repo_blob_at() {
let repo = Repository::open(GIT_PLATINUM).unwrap();
let oid = Oid::try_from("b84992d24be67536837f5ab45a943f1b3f501878").unwrap();

// Retrieve the blob using its oid.
let blob = repo
.blob_at("27acd68c7504755aa11023300890bb85bbd69d45", oid)
.unwrap();

// Verify the blob oid.
let blob_oid = blob.object_id();
assert_eq!(blob_oid, oid);

// Verify the commit that created the blob.
let blob_commit = blob.commit();
assert_eq!(
blob_commit.id.to_string(),
"e24124b7538658220b5aaf3b6ef53758f0a106dc"
);

// Verify the blob content ("memory.rs").
assert!(!blob.is_binary());
assert_eq!(blob.size(), 6253);
}

#[test]
fn tree_ordering() {
let repo = Repository::open(GIT_PLATINUM).unwrap();
Expand Down

0 comments on commit 25add2b

Please sign in to comment.