diff options
| -rw-r--r-- | server/common/src/git.rs | 68 | ||||
| -rw-r--r-- | server/common/src/tests.rs | 53 |
2 files changed, 121 insertions, 0 deletions
diff --git a/server/common/src/git.rs b/server/common/src/git.rs index c6a44e3..053ff3f 100644 --- a/server/common/src/git.rs +++ b/server/common/src/git.rs @@ -64,6 +64,20 @@ pub struct User { pub username: String, } +#[derive(Debug, PartialEq)] +pub enum ObjectType { + BLOB, + COMMIT, + TREE, +} + +#[derive(Debug, PartialEq)] +pub struct TreeEntry { + pub object_type: ObjectType, + pub object_name: String, + pub path: String, +} + fn io_err(action: &str, e: std::io::Error) -> Error { Error::new(format!("{action}: {e}")) } @@ -80,6 +94,34 @@ fn parse_user(output: String) -> User { } } +fn parse_tree_entries(output: String) -> Vec<TreeEntry> { + let mut ret = Vec::new(); + for line in output.split_terminator('\0') { + let mut parts = line.split_terminator('\t'); + let mut parts2 = parts.next().unwrap_or("").split_terminator(' '); + parts2.next(); // object mode + let object_type = match parts2.next() { + Some(value) => match value { + "blob" => ObjectType::BLOB, + "commit" => ObjectType::COMMIT, + "tree" => ObjectType::TREE, + _ => continue, + }, + None => continue, + }; + if let Some(object_name) = parts2.next() { + if let Some(path) = parts.next() { + ret.push(TreeEntry { + object_type, + object_name: object_name.to_string(), + path: path.to_string(), + }); + } + } + } + ret +} + impl RepoData { fn new() -> Self { Self { @@ -342,6 +384,21 @@ impl RepoData { self.run(&mut cmd).await } + async fn ls_tree( + &self, + repo: &Repository, + commit: &str, + recursive: bool, + ) -> Result<Vec<TreeEntry>, Error> { + let mut cmd = self.git_cmd(repo); + cmd.arg("ls-tree").arg("-z"); + if recursive { + cmd.arg("-r"); + } + cmd.arg(commit); + self.output(&mut cmd).map_ok(parse_tree_entries).await + } + async fn get_log_format( &self, repo: &Repository, @@ -594,4 +651,15 @@ impl Repository { data.delete_branch(self, branch.as_str()).await } + + pub async fn ls_tree( + &self, + commit: impl Into<String>, + recursive: bool, + ) -> Result<Vec<TreeEntry>, Error> { + let commit = commit.into(); + let data = self.lock.read().await; + + data.ls_tree(self, commit.as_str(), recursive).await + } } diff --git a/server/common/src/tests.rs b/server/common/src/tests.rs index 09e780b..34cc315 100644 --- a/server/common/src/tests.rs +++ b/server/common/src/tests.rs @@ -205,6 +205,53 @@ async fn git_get_author_commiter(repo: &git::Repository) { assert!(repo.get_author("<invalid>").await.is_err()); } +async fn git_ls_tree(repo: &git::Repository) { + let t1 = repo + .ls_tree("d7c502b9c6b833060576a0c4da0287933d603011", false) + .await + .unwrap(); + assert_eq!( + t1, + vec![ + git::TreeEntry { + object_type: git::ObjectType::BLOB, + object_name: "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391".to_string(), + path: "empty".to_string(), + }, + git::TreeEntry { + object_type: git::ObjectType::BLOB, + object_name: "d393216eb885c4b4ec91b691dd59a88c846d881d".to_string(), + path: "phrase".to_string(), + }, + ] + ); + + let t2 = repo + .ls_tree("2cecdec660a30bf3964cee645d9cee03640ef8dc", false) + .await + .unwrap(); + assert_eq!( + t2, + vec![ + git::TreeEntry { + object_type: git::ObjectType::BLOB, + object_name: "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391".to_string(), + path: "empty".to_string(), + }, + git::TreeEntry { + object_type: git::ObjectType::BLOB, + object_name: "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391".to_string(), + path: "foo".to_string(), + }, + git::TreeEntry { + object_type: git::ObjectType::BLOB, + object_name: "d393216eb885c4b4ec91b691dd59a88c846d881d".to_string(), + path: "phrase".to_string(), + }, + ] + ); +} + async fn git_fetch(bare: bool) -> git::Repository { let path = testdir!().join("repo"); let remote_path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("src/testdata/bare"); @@ -289,6 +336,12 @@ async fn test_git_bare_delete_branch() { } #[tokio::test] +async fn test_git_ls_tree() { + let repo = BARE.get_or_init(|| git_setup(true)).await; + git_ls_tree(repo).await; +} + +#[tokio::test] async fn test_grit_parse_base() { let path = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("src/testdata/grit/base.grd"); let grit = grit::parse_grit(path).await.unwrap(); |
