Radish alpha
r
rad:z6cFWeWpnZNHh9rUW8phgA3b5yGt
Git libraries for Radicle
Radicle
Git
consolidate object::commit.rs with commit.rs
Han Xu committed 3 years ago
commit 6bb9670abcc057d39fac4f19d5eb071bbcb73c33
parent 2bb35ed
6 files changed +73 -249
modified radicle-surf/src/object.rs
@@ -23,5 +23,3 @@ pub use blob::{Blob, BlobContent};

pub mod tree;
pub use tree::{Tree, TreeEntry};
-

-
pub mod commit;
modified radicle-surf/src/object/blob.rs
@@ -27,18 +27,18 @@ use serde::{
    Serialize,
};

-
use crate::object::commit;
+
use crate::Commit;

/// Represents a git blob object.
pub struct Blob {
    id: Oid,
    content: BlobContent,
-
    commit: commit::Header,
+
    commit: Commit,
}

impl Blob {
    /// Returns the [`Blob`] for a file at `revision` under `path`.
-
    pub(crate) fn new(id: Oid, content: &[u8], commit: commit::Header) -> Self {
+
    pub(crate) fn new(id: Oid, content: &[u8], commit: Commit) -> Self {
        let content = BlobContent::from(content);
        Self {
            id,
@@ -62,7 +62,7 @@ impl Blob {
    }

    /// Returns the commit that created this blob.
-
    pub fn commit(&self) -> &commit::Header {
+
    pub fn commit(&self) -> &Commit {
        &self.commit
    }
}
deleted radicle-surf/src/object/commit.rs
@@ -1,203 +0,0 @@
-
// This file is part of radicle-surf
-
// <https://github.com/radicle-dev/radicle-surf>
-
//
-
// Copyright (C) 2019-2020 The Radicle Team <dev@radicle.xyz>
-
//
-
// This program is free software: you can redistribute it and/or modify
-
// it under the terms of the GNU General Public License version 3 or
-
// later as published by the Free Software Foundation.
-
//
-
// This program is distributed in the hope that it will be useful,
-
// but WITHOUT ANY WARRANTY; without even the implied warranty of
-
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-
// GNU General Public License for more details.
-
//
-
// You should have received a copy of the GNU General Public License
-
// along with this program. If not, see <https://www.gnu.org/licenses/>.
-

-
//! Represents a commit.
-

-
use std::path::PathBuf;
-

-
use git_ref_format::RefString;
-
#[cfg(feature = "serde")]
-
use serde::{
-
    ser::{SerializeStruct as _, Serializer},
-
    Serialize,
-
};
-

-
use crate::{diff, glob, Glob, Repository, Revision};
-

-
use radicle_git_ext::Oid;
-

-
/// Representation of a changeset between two revs.
-
#[cfg_attr(feature = "serde", derive(Serialize))]
-
#[derive(Clone)]
-
pub struct Commit {
-
    /// The commit header.
-
    pub header: Header,
-
    /// The changeset introduced by this commit.
-
    pub diff: diff::Diff,
-
    /// The list of branches this commit belongs to.
-
    pub branches: Vec<RefString>,
-
}
-

-
/// Representation of a code commit.
-
#[derive(Clone, Debug)]
-
pub struct Header {
-
    /// Identifier of the commit in the form of a sha1 hash. Often referred to
-
    /// as oid or object id.
-
    pub sha1: Oid,
-
    /// The author of the commit.
-
    pub author: Person,
-
    /// The summary of the commit message body.
-
    pub summary: String,
-
    /// The entire commit message body.
-
    pub message: String,
-
    /// The committer of the commit.
-
    pub committer: Person,
-
    /// The recorded time of the committer signature. This is a convenience
-
    /// alias until we expose the actual author and commiter signatures.
-
    pub committer_time: git2::Time,
-
}
-

-
impl Header {
-
    /// Returns the commit description text. This is the text after the one-line
-
    /// summary.
-
    #[must_use]
-
    pub fn description(&self) -> &str {
-
        self.message
-
            .strip_prefix(&self.summary)
-
            .unwrap_or(&self.message)
-
            .trim()
-
    }
-
}
-

-
impl From<&crate::Commit> for Header {
-
    fn from(commit: &crate::Commit) -> Self {
-
        Self {
-
            sha1: commit.id,
-
            author: Person {
-
                name: commit.author.name.clone(),
-
                email: commit.author.email.clone(),
-
            },
-
            summary: commit.summary.clone(),
-
            message: commit.message.clone(),
-
            committer: Person {
-
                name: commit.committer.name.clone(),
-
                email: commit.committer.email.clone(),
-
            },
-
            committer_time: commit.committer.time,
-
        }
-
    }
-
}
-

-
impl From<crate::Commit> for Header {
-
    fn from(commit: crate::Commit) -> Self {
-
        Self::from(&commit)
-
    }
-
}
-

-
#[cfg(feature = "serde")]
-
impl Serialize for Header {
-
    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
-
    where
-
        S: Serializer,
-
    {
-
        let mut state = serializer.serialize_struct("Header", 6)?;
-
        state.serialize_field("sha1", &self.sha1.to_string())?;
-
        state.serialize_field("author", &self.author)?;
-
        state.serialize_field("summary", &self.summary)?;
-
        state.serialize_field("description", &self.description())?;
-
        state.serialize_field("committer", &self.committer)?;
-
        state.serialize_field("committerTime", &self.committer_time.seconds())?;
-
        state.end()
-
    }
-
}
-

-
/// A selection of commit headers and their statistics.
-
#[cfg_attr(feature = "serde", derive(Serialize))]
-
pub struct Commits {
-
    /// The commit headers
-
    pub headers: Vec<Header>,
-
    /// The statistics for the commit headers
-
    pub stats: crate::Stats,
-
}
-

-
/// Retrieves a [`Commit`].
-
///
-
/// # Errors
-
///
-
/// Will return [`Error`] if the project doesn't exist or the surf interaction
-
/// fails.
-
pub fn commit<R: Revision>(repo: &Repository, rev: R) -> Result<Commit, Error> {
-
    let commit = repo.commit(rev)?;
-
    let sha1 = commit.id;
-
    let header = Header::from(&commit);
-
    let diff = repo.diff_commit(commit)?;
-

-
    let branches = repo
-
        .revision_branches(&sha1, Glob::all_heads().branches().and(Glob::all_remotes()))?
-
        .into_iter()
-
        .map(|b| b.refname().into())
-
        .collect();
-

-
    Ok(Commit {
-
        header,
-
        diff,
-
        branches,
-
    })
-
}
-

-
/// Retrieves the [`Header`] for the given `sha1`.
-
///
-
/// # Errors
-
///
-
/// Will return [`Error`] if the project doesn't exist or the surf interaction
-
/// fails.
-
pub fn header(repo: &Repository, sha1: Oid) -> Result<Header, Error> {
-
    let commit = repo.commit(sha1)?;
-
    Ok(Header::from(&commit))
-
}
-

-
/// Retrieves the [`Commit`] history for the given `revision`.
-
///
-
/// # Errors
-
///
-
/// Will return [`Error`] if the project doesn't exist or the surf interaction
-
/// fails.
-
pub fn commits<R>(repo: &Repository, revision: &R) -> Result<Commits, Error>
-
where
-
    R: Revision,
-
{
-
    let stats = repo.stats_from(revision)?;
-
    let commits = repo.history(revision)?.collect::<Result<Vec<_>, _>>()?;
-
    let headers = commits.into_iter().map(Header::from).collect();
-
    Ok(Commits { headers, stats })
-
}
-

-
/// An error reported by commit API.
-
#[derive(Debug, thiserror::Error)]
-
pub enum Error {
-
    /// An error occurred during a git operation.
-
    #[error(transparent)]
-
    Git(#[from] crate::Error),
-

-
    #[error(transparent)]
-
    Glob(#[from] glob::Error),
-

-
    /// Trying to find a file path which could not be found.
-
    #[error("the path '{0}' was not found")]
-
    PathNotFound(PathBuf),
-
}
-

-
/// Representation of a person (e.g. committer, author, signer) from a
-
/// repository. Usually extracted from a signature.
-
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
-
#[derive(Clone, Debug)]
-
pub struct Person {
-
    /// Name part of the commit signature.
-
    pub name: String,
-
    /// Email part of the commit signature.
-
    pub email: String,
-
}
modified radicle-surf/src/object/tree.rs
@@ -27,7 +27,7 @@ use serde::{
    Serialize,
};

-
use crate::{fs, object::commit};
+
use crate::{fs, Commit};

/// Represents a tree object as in git. It is essentially the content of
/// one directory. Note that multiple directories can have the same content,
@@ -39,12 +39,12 @@ pub struct Tree {
    /// The first descendant entries for this tree.
    entries: Vec<TreeEntry>,
    /// The commit object that created this tree object.
-
    commit: commit::Header,
+
    commit: Commit,
}

impl Tree {
    /// Creates a new tree, ensuring the `entries` are sorted.
-
    pub(crate) fn new(id: Oid, mut entries: Vec<TreeEntry>, commit: commit::Header) -> Self {
+
    pub(crate) fn new(id: Oid, mut entries: Vec<TreeEntry>, commit: Commit) -> Self {
        entries.sort();
        Self {
            id,
@@ -58,7 +58,7 @@ impl Tree {
    }

    /// Returns the commit that created this tree.
-
    pub fn commit(&self) -> &commit::Header {
+
    pub fn commit(&self) -> &Commit {
        &self.commit
    }

@@ -148,11 +148,11 @@ pub struct TreeEntry {
    entry: Entry,

    /// The commit object that created this entry object.
-
    commit: commit::Header,
+
    commit: Commit,
}

impl TreeEntry {
-
    pub(crate) fn new(name: String, entry: Entry, commit: commit::Header) -> Self {
+
    pub(crate) fn new(name: String, entry: Entry, commit: Commit) -> Self {
        Self {
            name,
            entry,
@@ -172,7 +172,7 @@ impl TreeEntry {
        matches!(self.entry, Entry::Tree(_))
    }

-
    pub fn commit(&self) -> &commit::Header {
+
    pub fn commit(&self) -> &Commit {
        &self.commit
    }

modified radicle-surf/src/repo.rs
@@ -32,7 +32,7 @@ use crate::{
    fs::{self, Directory, File, FileContent},
    glob,
    namespace,
-
    object::{commit::Header, Blob, Tree, TreeEntry},
+
    object::{Blob, Tree, TreeEntry},
    refs::{self, BranchNames, Branches, Categories, Namespaces, TagNames, Tags},
    Branch,
    Commit,
@@ -283,8 +283,7 @@ impl Repository {
                let commit = self
                    .last_commit(&path, commit.id)?
                    .ok_or(Error::PathNotFound(path))?;
-
                let commit_header = Header::from(commit);
-
                Ok(TreeEntry::new(name, en.into(), commit_header))
+
                Ok(TreeEntry::new(name, en.into(), commit))
            })
            .collect::<Result<Vec<TreeEntry>, Error>>()?;
        entries.sort();
@@ -292,8 +291,7 @@ impl Repository {
        let last_commit = self
            .last_commit(path, commit)?
            .ok_or_else(|| Error::PathNotFound(path.as_ref().to_path_buf()))?;
-
        let header = Header::from(last_commit);
-
        Ok(Tree::new(dir.id(), entries, header))
+
        Ok(Tree::new(dir.id(), entries, last_commit))
    }

    /// Returns a [`Blob`] for `path` in `commit`.
@@ -305,10 +303,8 @@ impl Repository {
        let last_commit = self
            .last_commit(path, commit)?
            .ok_or_else(|| Error::PathNotFound(path.as_ref().to_path_buf()))?;
-
        let header = Header::from(last_commit);
-

        let content = file.content(self)?;
-
        Ok(Blob::new(file.id(), content.as_bytes(), header))
+
        Ok(Blob::new(file.id(), content.as_bytes(), last_commit))
    }

    /// Returns the last commit, if exists, for a `path` in the history of
@@ -415,31 +411,32 @@ impl Repository {
    pub fn history<C: ToCommit>(&self, head: C) -> Result<History, Error> {
        History::new(self, head)
    }
-
}

-
////////////////////////////////////////////////////////////
-
// Private API, ONLY add `pub(crate) fn` or `fn` in here. //
-
////////////////////////////////////////////////////////////
-
impl Repository {
-
    /// Lists branches that are reachable from `oid`.
-
    pub(crate) fn revision_branches(
+
    /// Lists branches that are reachable from `rev`.
+
    pub fn revision_branches(
        &self,
-
        oid: &Oid,
+
        rev: impl Revision,
        glob: Glob<Branch>,
    ) -> Result<Vec<Branch>, Error> {
+
        let oid = self.object_id(&rev)?;
        let mut contained_branches = vec![];
        for branch in self.branches(glob)? {
            let branch = branch?;
            let namespaced = self.namespaced_refname(&branch.refname())?;
            let reference = self.inner.find_reference(namespaced.as_str())?;
-
            if self.reachable_from(&reference, oid)? {
+
            if self.reachable_from(&reference, &oid)? {
                contained_branches.push(branch);
            }
        }

        Ok(contained_branches)
    }
+
}

+
////////////////////////////////////////////////////////////
+
// Private API, ONLY add `pub(crate) fn` or `fn` in here. //
+
////////////////////////////////////////////////////////////
+
impl Repository {
    pub(crate) fn find_blob(&self, oid: Oid) -> Result<git2::Blob<'_>, git2::Error> {
        self.inner.find_blob(oid.into())
    }
modified radicle-surf/t/src/source.rs
@@ -1,7 +1,7 @@
use std::path::PathBuf;

use git_ref_format::refname;
-
use radicle_surf::Repository;
+
use radicle_surf::{Glob, Repository};
use serde_json::json;

const GIT_PLATINUM: &str = "../data/git-platinum";
@@ -18,15 +18,18 @@ fn tree_serialization() {
          "lastCommit": {
            "author": {
              "email": "fintan.halpenny@gmail.com",
-
              "name": "Fintan Halpenny"
+
              "name": "Fintan Halpenny",
+
              "time": 1578309972
            },
            "committer": {
              "email": "noreply@github.com",
-
              "name": "GitHub"
+
              "name": "GitHub",
+
              "time": 1578309972
            },
-
            "committerTime": 1578309972,
            "description": "I want to have files under src that have separate commits.\r\nThat way src's latest commit isn't the same as all its files, instead it's the file that was touched last.",
-
            "sha1": "3873745c8f6ffb45c990eb23b491d4b4b6182f95",
+
            "id": "3873745c8f6ffb45c990eb23b491d4b4b6182f95",
+
            "message": "Extend the docs (#2)\n\nI want to have files under src that have separate commits.\r\nThat way src's latest commit isn't the same as all its files, instead it's the file that was touched last.",
+
            "parents": ["d6880352fc7fda8f521ae9b7357668b17bb5bad5"],
            "summary": "Extend the docs (#2)"
          },
          "name": "Eval.hs",
@@ -37,15 +40,18 @@ fn tree_serialization() {
          "lastCommit": {
            "author": {
              "email": "rudolfs@osins.org",
-
              "name": "Rūdolfs Ošiņš"
+
              "name": "Rūdolfs Ošiņš",
+
              "time": 1575283266
            },
            "committer": {
              "email": "rudolfs@osins.org",
-
              "name": "Rūdolfs Ošiņš"
+
              "name": "Rūdolfs Ošiņš",
+
              "time": 1575283266
            },
-
            "committerTime": 1575283266,
            "description": "",
-
            "sha1": "e24124b7538658220b5aaf3b6ef53758f0a106dc",
+
            "id": "e24124b7538658220b5aaf3b6ef53758f0a106dc",
+
            "message": "Move examples to \"src\"\n",
+
            "parents": ["19bec071db6474af89c866a1bd0e4b1ff76e2b97"],
            "summary": "Move examples to \"src\""
          },
          "name": "memory.rs",
@@ -55,15 +61,18 @@ fn tree_serialization() {
      "lastCommit": {
        "author": {
          "email": "rudolfs@osins.org",
-
          "name": "Rūdolfs Ošiņš"
+
          "name": "Rūdolfs Ošiņš",
+
          "time": 1582198877
        },
        "committer": {
          "email": "noreply@github.com",
-
          "name": "GitHub"
+
          "name": "GitHub",
+
          "time": 1582198877
        },
-
        "committerTime": 1582198877,
        "description": "It was a bad idea to have an actual source file which is used by\r\nradicle-upstream in the fixtures repository. It gets in the way of\r\nlinting and editors pick it up as a regular source file by accident.",
-
        "sha1": "a57846bbc8ced6587bf8329fc4bce970eb7b757e",
+
        "id": "a57846bbc8ced6587bf8329fc4bce970eb7b757e",
+
        "message": "Remove src/Folder.svelte (#3)\n\nIt was a bad idea to have an actual source file which is used by\r\nradicle-upstream in the fixtures repository. It gets in the way of\r\nlinting and editors pick it up as a regular source file by accident.",
+
        "parents": ["3873745c8f6ffb45c990eb23b491d4b4b6182f95"],
        "summary": "Remove src/Folder.svelte (#3)"
      },
      "oid": "ed52e9f8dfe1d8b374b2a118c25235349a743dd2"
@@ -81,7 +90,7 @@ fn repo_tree() {

    let commit_header = tree.commit();
    assert_eq!(
-
        commit_header.sha1.to_string(),
+
        commit_header.id.to_string(),
        "e24124b7538658220b5aaf3b6ef53758f0a106dc"
    );

@@ -102,7 +111,7 @@ fn repo_tree() {
    );
    let commit = entry.commit();
    assert_eq!(
-
        commit.sha1.to_string(),
+
        commit.id.to_string(),
        "e24124b7538658220b5aaf3b6ef53758f0a106dc"
    );

@@ -128,7 +137,7 @@ fn repo_blob() {

    let commit_header = blob.commit();
    assert_eq!(
-
        commit_header.sha1.to_string(),
+
        commit_header.id.to_string(),
        "e24124b7538658220b5aaf3b6ef53758f0a106dc"
    );

@@ -165,3 +174,26 @@ fn tree_ordering() {
        ]
    );
}
+

+
#[test]
+
fn commit_branches() {
+
    let repo = Repository::open(GIT_PLATINUM).unwrap();
+
    let init_commit = "d3464e33d75c75c99bfb90fa2e9d16efc0b7d0e3";
+
    let glob = Glob::all_heads().branches().and(Glob::all_remotes());
+
    let branches = repo.revision_branches(init_commit, glob).unwrap();
+

+
    assert_eq!(branches.len(), 7);
+
    assert_eq!(branches[0].refname().as_str(), "refs/heads/dev");
+
    assert_eq!(branches[1].refname().as_str(), "refs/heads/master");
+
    assert_eq!(
+
        branches[2].refname().as_str(),
+
        "refs/remotes/banana/orange/pineapple"
+
    );
+
    assert_eq!(
+
        branches[3].refname().as_str(),
+
        "refs/remotes/banana/pineapple"
+
    );
+
    assert_eq!(branches[4].refname().as_str(), "refs/remotes/origin/HEAD");
+
    assert_eq!(branches[5].refname().as_str(), "refs/remotes/origin/dev");
+
    assert_eq!(branches[6].refname().as_str(), "refs/remotes/origin/master");
+
}