summary refs log tree commit diff
path: root/ripple/fossil
diff options
context:
space:
mode:
Diffstat (limited to 'ripple/fossil')
-rw-r--r--ripple/fossil/Cargo.toml1
-rw-r--r--ripple/fossil/src/lib.rs57
2 files changed, 58 insertions, 0 deletions
diff --git a/ripple/fossil/Cargo.toml b/ripple/fossil/Cargo.toml
index 2ff2288..663919f 100644
--- a/ripple/fossil/Cargo.toml
+++ b/ripple/fossil/Cargo.toml
@@ -15,6 +15,7 @@ bytes = "1.0.1"
 clap = { version = "3.1.15", features = ["derive"] }
 env_logger = "0.9.0"
 fuser = "0.11.0"
+git2 = "0.14.3"
 lazy_static = "1.4.0"
 libc = "0.2.112"
 log = "0.4.14"
diff --git a/ripple/fossil/src/lib.rs b/ripple/fossil/src/lib.rs
index 007a771..b3a1c3d 100644
--- a/ripple/fossil/src/lib.rs
+++ b/ripple/fossil/src/lib.rs
@@ -14,6 +14,7 @@ use {
 		io::{self, Read, Write},
 		os::unix::{fs::PermissionsExt, prelude::FileExt},
 		path::Path,
+		str,
 	},
 };
 
@@ -74,6 +75,62 @@ impl Store {
 		self.meta.flush().unwrap();
 	}
 
+	pub fn add_git_tree(&self, repo: &git2::Repository, tree: git2::Oid) -> DirectoryRef {
+		let tree = repo.find_tree(tree).unwrap();
+		let d = self.add_git_tree_inner(repo, &tree);
+		self.flush();
+		d
+	}
+
+	fn add_git_tree_inner(&self, repo: &git2::Repository, tree: &git2::Tree) -> DirectoryRef {
+		let mut d = Directory::new();
+		let mut size: u32 = 0;
+
+		for entry in tree {
+			let name = entry.name().expect("invalid UTF-8");
+			let object = entry.to_object(repo).unwrap();
+			let kind = entry.kind().unwrap();
+			let mode = entry.filemode();
+
+			let child = match kind {
+				git2::ObjectType::Tree => {
+					let tree = object.as_tree().unwrap();
+					Node::Directory(self.add_git_tree_inner(repo, tree))
+				}
+				git2::ObjectType::Blob if mode == 0o120000 => {
+					let target = object.as_blob().unwrap().content();
+					let target = str::from_utf8(target).unwrap();
+					Node::Link {
+						target: target.to_owned(),
+					}
+				}
+				git2::ObjectType::Blob => {
+					let executable = match mode {
+						0o100644 => true,
+						0o100755 => false,
+						_ => panic!("unexpected mode: {:o} for file {:?}", mode, name),
+					};
+
+					let data = object.as_blob().unwrap().content();
+					Node::File(FileRef {
+						ident: self.write_blob(data),
+						size: data.len() as u32,
+						executable,
+					})
+				}
+				kind => panic!("unexpected object kind: {:?}", kind),
+			};
+
+			size = size.checked_add(child.size()).expect("overflow");
+			d.children.insert(name.to_owned(), child);
+		}
+
+		let blob = d.into_pb().encode_to_vec();
+		let ident = self.write_blob(&blob);
+
+		DirectoryRef { ident, size }
+	}
+
 	pub fn add_directory(&self, path: impl AsRef<Path>) -> DirectoryRef {
 		let d = self.add_directory_inner(path.as_ref());
 		self.flush();