Radish alpha
r
rad:z6cFWeWpnZNHh9rUW8phgA3b5yGt
Git libraries for Radicle
Radicle
Git
surf: avoid last commit computation on Tree
Fintan Halpenny committed 9 months ago
commit 30d8275e5f63212b10793e6a08aa2fd5eac92be0
parent 603a188
4 files changed +95 -69
added radicle-git-ext/t/src/fintohaps@haptop.2550552:1709642522
modified radicle-surf/src/repo.rs
@@ -260,18 +260,17 @@ impl Repository {
            .map(|en| {
                let name = en.name().to_string();
                let path = en.path();
-
                let commit = self
-
                    .last_commit(&path, commit.id)?
-
                    .ok_or(error::Repo::PathNotFound(path))?;
-
                Ok(Entry::new(name, en.into(), commit))
+
                Ok(Entry::new(name, path, en.into(), commit.clone()))
            })
            .collect::<Result<Vec<Entry>, Error>>()?;
        entries.sort();

-
        let last_commit = self
-
            .last_commit(path, commit)?
-
            .ok_or_else(|| error::Repo::PathNotFound(path.as_ref().to_path_buf()))?;
-
        Ok(Tree::new(dir.id(), entries, last_commit))
+
        Ok(Tree::new(
+
            dir.id(),
+
            entries,
+
            commit,
+
            path.as_ref().to_path_buf(),
+
        ))
    }

    /// Returns a [`Blob`] for `path` in `commit`.
modified radicle-surf/src/tree.rs
@@ -19,6 +19,7 @@
//! See git [doc](https://git-scm.com/book/en/v2/Git-Internals-Git-Objects) for more details.

use std::cmp::Ordering;
+
use std::path::PathBuf;

use radicle_git_ext::Oid;
#[cfg(feature = "serde")]
@@ -28,7 +29,7 @@ use serde::{
};
use url::Url;

-
use crate::{fs, Commit};
+
use crate::{fs, Commit, Error, Repository};

/// Represents a tree object as in git. It is essentially the content of
/// one directory. Note that multiple directories can have the same content,
@@ -41,16 +42,27 @@ pub struct Tree {
    entries: Vec<Entry>,
    /// The commit object that created this tree object.
    commit: Commit,
+
    /// The root path this tree was constructed from.
+
    root: PathBuf,
+
}
+

+
#[derive(Debug, thiserror::Error)]
+
pub enum LastCommitError {
+
    #[error(transparent)]
+
    Repo(#[from] Error),
+
    #[error("could not get the last commit for this entry")]
+
    Missing,
}

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

@@ -58,11 +70,17 @@ impl Tree {
        self.id
    }

-
    /// Returns the commit that created this tree.
+
    /// Returns the commit for which this [`Tree`] was constructed from.
    pub fn commit(&self) -> &Commit {
        &self.commit
    }

+
    /// Returns the commit that last touched this [`Tree`].
+
    pub fn last_commit(&self, repo: &Repository) -> Result<Commit, LastCommitError> {
+
        repo.last_commit(&self.root, self.commit().clone())?
+
            .ok_or(LastCommitError::Missing)
+
    }
+

    /// Returns the entries of the tree.
    pub fn entries(&self) -> &Vec<Entry> {
        &self.entries
@@ -79,7 +97,8 @@ impl Serialize for Tree {
    ///     { <entry_1> },
    ///     { <entry_2> },
    ///   ],
-
    ///   "lastCommit": {
+
    ///   "root": "src/foo",
+
    ///   "commit": {
    ///     "author": {
    ///       "email": "foobar@gmail.com",
    ///       "name": "Foo Bar"
@@ -104,7 +123,8 @@ impl Serialize for Tree {
        let mut state = serializer.serialize_struct("Tree", FIELDS)?;
        state.serialize_field("oid", &self.id)?;
        state.serialize_field("entries", &self.entries)?;
-
        state.serialize_field("lastCommit", &self.commit)?;
+
        state.serialize_field("commit", &self.commit)?;
+
        state.serialize_field("root", &self.root)?;
        state.end()
    }
}
@@ -150,16 +170,17 @@ impl Ord for EntryKind {
pub struct Entry {
    name: String,
    entry: EntryKind,
-

-
    /// The commit object that created this entry object.
+
    path: PathBuf,
+
    /// The commit from which this entry was constructed from.
    commit: Commit,
}

impl Entry {
-
    pub(crate) fn new(name: String, entry: EntryKind, commit: Commit) -> Self {
+
    pub(crate) fn new(name: String, path: PathBuf, entry: EntryKind, commit: Commit) -> Self {
        Self {
            name,
            entry,
+
            path,
            commit,
        }
    }
@@ -168,6 +189,11 @@ impl Entry {
        &self.name
    }

+
    /// The full path to this entry from the root of the Git repository
+
    pub fn path(&self) -> &PathBuf {
+
        &self.path
+
    }
+

    pub fn entry(&self) -> &EntryKind {
        &self.entry
    }
@@ -187,6 +213,12 @@ impl Entry {
            EntryKind::Submodule { id, .. } => id,
        }
    }
+

+
    /// Returns the commit that last touched this [`Entry`].
+
    pub fn last_commit(&self, repo: &Repository) -> Result<Commit, LastCommitError> {
+
        repo.last_commit(&self.path, self.commit.clone())?
+
            .ok_or(LastCommitError::Missing)
+
    }
}

// To support `sort`.
@@ -231,7 +263,7 @@ impl Serialize for Entry {
    /// ```json
    ///  {
    ///     "kind": "blob",
-
    ///     "lastCommit": {
+
    ///     "commit": {
    ///       "author": {
    ///         "email": "foobar@gmail.com",
    ///         "name": "Foo Bar"
@@ -245,6 +277,7 @@ impl Serialize for Entry {
    ///       "sha1": "2873745c8f6ffb45c990eb23b491d4b4b6182f95",
    ///       "summary": "Add a new sample"
    ///     },
+
    ///     "path": "src/foo/Sample.rs",
    ///     "name": "Sample.rs",
    ///     "oid": "6d6240123a8d8ea8a8376610168a0a4bcb96afd0"
    ///   },
@@ -268,7 +301,7 @@ impl Serialize for Entry {
            state.serialize_field("url", url)?;
        };
        state.serialize_field("oid", &self.object_id())?;
-
        state.serialize_field("lastCommit", &self.commit)?;
+
        state.serialize_field("commit", &self.path)?;
        state.end()
    }
}
modified radicle-surf/t/src/source.rs
@@ -12,72 +12,61 @@ fn tree_serialization() {
    let tree = repo.tree(refname!("refs/heads/master"), &"src").unwrap();

    let expected = json!({
+
      "oid": "ed52e9f8dfe1d8b374b2a118c25235349a743dd2",
      "entries": [
        {
-
          "kind": "blob",
-
          "lastCommit": {
-
            "author": {
-
              "email": "fintan.halpenny@gmail.com",
-
              "name": "Fintan Halpenny",
-
              "time": 1578309972
-
            },
-
            "committer": {
-
              "email": "noreply@github.com",
-
              "name": "GitHub",
-
              "time": 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.",
-
            "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",
-
          "oid": "7d6240123a8d8ea8a8376610168a0a4bcb96afd0"
+
          "kind": "blob",
+
          "oid": "7d6240123a8d8ea8a8376610168a0a4bcb96afd0",
+
          "commit": "src/Eval.hs"
        },
        {
-
          "kind": "blob",
-
          "lastCommit": {
-
            "author": {
-
              "email": "rudolfs@osins.org",
-
              "name": "Rūdolfs Ošiņš",
-
              "time": 1575283266
-
            },
-
            "committer": {
-
              "email": "rudolfs@osins.org",
-
              "name": "Rūdolfs Ošiņš",
-
              "time": 1575283266
-
            },
-
            "description": "",
-
            "id": "e24124b7538658220b5aaf3b6ef53758f0a106dc",
-
            "message": "Move examples to \"src\"\n",
-
            "parents": ["19bec071db6474af89c866a1bd0e4b1ff76e2b97"],
-
            "summary": "Move examples to \"src\""
-
          },
          "name": "memory.rs",
-
          "oid": "b84992d24be67536837f5ab45a943f1b3f501878"
+
          "kind": "blob",
+
          "oid": "b84992d24be67536837f5ab45a943f1b3f501878",
+
          "commit": "src/memory.rs"
        }
      ],
-
      "lastCommit": {
+
      "commit": {
+
        "id": "a0dd9122d33dff2a35f564d564db127152c88e02",
        "author": {
-
          "email": "rudolfs@osins.org",
          "name": "Rūdolfs Ošiņš",
-
          "time": 1582198877
+
          "email": "rudolfs@osins.org",
+
          "time": 1602778504
        },
        "committer": {
-
          "email": "noreply@github.com",
          "name": "GitHub",
-
          "time": 1582198877
+
          "email": "noreply@github.com",
+
          "time": 1602778504
        },
-
        "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.",
-
        "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)"
+
        "summary": "Add files with special characters in their filenames (#5)",
+
        "message": "Add files with special characters in their filenames (#5)\n\n",
+
        "description": "",
+
        "parents": [
+
          "223aaf87d6ea62eef0014857640fd7c8dd0f80b5"
+
        ]
      },
-
      "oid": "ed52e9f8dfe1d8b374b2a118c25235349a743dd2"
+
      "root": "src"
    });
-
    assert_eq!(serde_json::to_value(tree).unwrap(), expected)
+

+
    assert_eq!(
+
        serde_json::to_value(&tree).unwrap(),
+
        expected,
+
        "Got:\n{}",
+
        serde_json::to_string_pretty(&tree).unwrap()
+
    )
+
}
+

+
#[test]
+
fn test_tree_last_commit() {
+
    let repo = Repository::open(GIT_PLATINUM).unwrap();
+
    let tree = repo.tree(refname!("refs/heads/master"), &"src").unwrap();
+
    let last_commit = tree.last_commit(&repo).unwrap();
+
    assert_ne!(*tree.commit(), last_commit);
+
    assert_eq!(
+
        last_commit.id.to_string(),
+
        "a57846bbc8ced6587bf8329fc4bce970eb7b757e"
+
    )
}

#[test]
@@ -105,7 +94,7 @@ fn repo_tree() {
    let commit_header = tree.commit();
    assert_eq!(
        commit_header.id.to_string(),
-
        "e24124b7538658220b5aaf3b6ef53758f0a106dc"
+
        "27acd68c7504755aa11023300890bb85bbd69d45"
    );

    let tree_oid = tree.object_id();
@@ -126,6 +115,11 @@ fn repo_tree() {
    let commit = entry.commit();
    assert_eq!(
        commit.id.to_string(),
+
        "27acd68c7504755aa11023300890bb85bbd69d45"
+
    );
+
    let last_commit = entry.last_commit(&repo).unwrap();
+
    assert_eq!(
+
        last_commit.id.to_string(),
        "e24124b7538658220b5aaf3b6ef53758f0a106dc"
    );