aboutsummaryrefslogtreecommitdiff
path: root/apps/servers/octopus/supergit/src/old-files.rs
diff options
context:
space:
mode:
Diffstat (limited to 'apps/servers/octopus/supergit/src/old-files.rs')
-rw-r--r--apps/servers/octopus/supergit/src/old-files.rs232
1 files changed, 232 insertions, 0 deletions
diff --git a/apps/servers/octopus/supergit/src/old-files.rs b/apps/servers/octopus/supergit/src/old-files.rs
new file mode 100644
index 000000000000..a4b10513c7fb
--- /dev/null
+++ b/apps/servers/octopus/supergit/src/old-files.rs
@@ -0,0 +1,232 @@
+use crate::{Branch, BranchIter, Commit, HashId};
+use atomptr::AtomPtr;
+use git2::{ObjectType, TreeWalkMode, TreeWalkResult};
+use std::collections::{BTreeMap, BTreeSet};
+use std::{path::PathBuf, sync::Arc};
+
+/// A tree of files
+pub struct FileTree {
+ repo: Arc<git2::Repository>,
+ tree: AtomPtr<BTreeMap<String, Arc<TreeEntry>>>,
+}
+
+impl FileTree {
+ /// Utility function to create a tree, and then parse it too
+ pub(crate) fn new(repo: &Arc<git2::Repository>, commit: HashId) -> Arc<Self> {
+ Arc::new(Self {
+ repo: Arc::clone(repo),
+ tree: AtomPtr::new(BTreeMap::new()),
+ })
+ .parse(commit)
+ }
+
+ /// Parse a tree from a specific commit
+ pub(crate) fn parse(self: Arc<Self>, commit: HashId) -> Arc<Self> {
+ let mut new_tree = BTreeMap::new();
+
+ let tree = (&self.repo)
+ .find_commit(commit.to_oid())
+ .unwrap()
+ .tree()
+ .unwrap();
+
+ tree.walk(TreeWalkMode::PreOrder, |p, entry| {
+ let path_segs: Vec<_> = p.split("/").filter(|s| s != &"").collect();
+ let path = if path_segs.len() == 0 {
+ None
+ } else {
+ Some(path_segs)
+ };
+
+ let te = TreeEntry::generate(path, entry);
+ new_tree.insert(te.path(), Arc::new(te));
+ TreeWalkResult::Ok
+ })
+ .unwrap();
+
+ // Add a special entry for the root of the repo
+ new_tree.insert(
+ "".into(),
+ Arc::new(TreeEntry::Dir(Directory {
+ id: tree.id().into(),
+ path: "".into(),
+ name: "".into(),
+ })),
+ );
+
+ // This is needed to make borrowchk shut up
+ drop(tree);
+
+ // Atomicly swap new tree into place
+ self.tree.swap(new_tree);
+
+ self
+ }
+
+ fn get_entry(&self, path: &str) -> Option<Arc<TreeEntry>> {
+ self.tree.get_ref().get(path).map(|e| Arc::clone(&e))
+ }
+
+ /// Load a file entry in this `FileTree` from disk
+ ///
+ /// When calling this function on a directory, nothing will happen
+ /// (returns `None`), because directories can't be loaded. If you
+ /// want to get a list of children for a directory, use
+ /// [`FileTree::enumerate()`]() instead!
+ pub fn load(&self, path: &str) -> Option<Yield> {
+ self.get_entry(path).and_then(|e| e.load(&self.repo))
+ }
+
+ /// Get the history of a path with a branch iterator
+ ///
+ /// This function is very computationally intensive, because it
+ /// will step through the entire iterator to pull commits from,
+ /// and see if they touch the respective path.
+ pub fn history(&self, iter: BranchIter, path: &str) -> Vec<Commit> {
+ iter.filter_map(|c| {
+ if c.commit()
+ .get_paths()
+ .into_iter()
+ .collect::<BTreeSet<_>>()
+ .contains(path)
+ {
+ Some(c.commit().clone())
+ } else {
+ None
+ }
+ })
+ .collect()
+ }
+}
+
+/// Data yielded from loading a part of the file tree
+///
+/// This type is returned when fetching a path via `FileTree::load()`,
+/// and can either be a single file read into memory, or an
+/// enumeration of direct children of a directory.
+///
+/// To get all children of a subtree, use `Yield::into_tree()` to
+/// create a new, recursive `FileTree` to enumerate.
+#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord)]
+pub enum Yield {
+ /// Load a single file into a buffer
+ File(Vec<u8>),
+ /// Enumerate children in a directory
+ Dir(Vec<String>),
+}
+
+enum TreeEntry {
+ /// A single file
+ File(File),
+ /// A sub-tree
+ Dir(Directory),
+}
+
+impl TreeEntry {
+ fn generate(path_segments: Option<Vec<&str>>, entry: &git2::TreeEntry) -> Self {
+ let path = path_segments.map_or("".into(), |p| path_segs_join(p));
+ let id = entry.id().into();
+ let name = entry.name().unwrap().into();
+
+ match entry.kind() {
+ Some(ObjectType::Blob) => Self::File(File::new(id, path, name)),
+ Some(ObjectType::Tree) => Self::Dir(Directory::new(id, path, name)),
+ _ => unimplemented!(),
+ }
+ }
+
+ fn load(&self, repo: &Arc<git2::Repository>) -> Option<Yield> {
+ let id = self.id();
+
+ match self {
+ Self::File(ref f) => repo
+ .find_blob(id.into())
+ .ok()
+ .map(|b| Yield::File(b.content().into())),
+ Self::Dir(ref d) => repo
+ .find_tree(id.into())
+ .ok()
+ .map(|tree| {
+ let mut children = vec![];
+
+ // Iterate the tree, but only as long as there are no
+ // additional path segments
+ tree.walk(TreeWalkMode::PreOrder, |p, entry| {
+ let path_segs: Vec<_> = p.split("/").filter(|s| s != &"").collect();
+ if path_segs.len() > 0 {
+ TreeWalkResult::Skip
+ } else {
+ // Take the current tree path, and append the
+ // name of whatever we're currently iterating
+ // over is
+ let path = PathBuf::new().join(self.path()).join(entry.name().unwrap());
+ children.push(path.as_path().to_str().unwrap().into());
+ TreeWalkResult::Ok
+ }
+ });
+
+ children
+ })
+ .map(|c| Yield::Dir(c)),
+ }
+ }
+
+ fn is_file(&self) -> bool {
+ match self {
+ Self::File(_) => true,
+ Self::Dir(_) => false,
+ }
+ }
+
+ fn id(&self) -> HashId {
+ match self {
+ Self::File(ref f) => f.id.clone(),
+ Self::Dir(ref d) => d.id.clone(),
+ }
+ }
+
+ /// Get the repo-internal path (including name)
+ ///
+ /// This is used to index files in a file tree, to allow O(1)
+ /// access to deeply nested items.
+ fn path(&self) -> String {
+ match self {
+ Self::File(ref f) => PathBuf::new().join(&f.path).join(&f.name),
+ Self::Dir(ref d) => PathBuf::new().join(&d.path).join(&d.name),
+ }
+ .as_path()
+ .to_str()
+ .unwrap()
+ .into()
+ }
+}
+
+struct File {
+ id: HashId,
+ path: String,
+ name: String,
+}
+
+impl File {
+ fn new(id: HashId, path: String, name: String) -> Self {
+ Self { id, path, name }
+ }
+}
+
+struct Directory {
+ id: HashId,
+ path: String,
+ name: String,
+}
+
+impl Directory {
+ fn new(id: HashId, path: String, name: String) -> Self {
+ Self { id, path, name }
+ }
+
+ fn enumerate(&self, repo: git2::Repository) -> Vec<String> {
+ vec![]
+ }
+}
+
+////////////////////////////////