aboutsummaryrefslogtreecommitdiff
path: root/apps
diff options
context:
space:
mode:
authorMx Kookie <kookie@spacekookie.de>2020-11-08 20:05:34 +0100
committerMx Kookie <kookie@spacekookie.de>2020-12-21 05:19:44 +0100
commiteac42979146e10ed740f62c2ba6d09bf410f1187 (patch)
tree660287979f3ac419717afbb9c49b45ad48774099 /apps
parent4c4d16d58c3d594938aa3ded26aa7e2fcebfc829 (diff)
octopus: implementing supergit basic tree abstraction
Diffstat (limited to 'apps')
-rw-r--r--apps/servers/octopus/Cargo.lock7
-rw-r--r--apps/servers/octopus/supergit/Cargo.toml3
-rw-r--r--apps/servers/octopus/supergit/src/bin/test.rs31
-rw-r--r--apps/servers/octopus/supergit/src/branch.rs14
-rw-r--r--apps/servers/octopus/supergit/src/commit.rs13
-rw-r--r--apps/servers/octopus/supergit/src/files.rs143
-rw-r--r--apps/servers/octopus/supergit/src/lib.rs24
7 files changed, 196 insertions, 39 deletions
diff --git a/apps/servers/octopus/Cargo.lock b/apps/servers/octopus/Cargo.lock
index 42f0f4d1df04..24f6c0c539cf 100644
--- a/apps/servers/octopus/Cargo.lock
+++ b/apps/servers/octopus/Cargo.lock
@@ -508,6 +508,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "065374052e7df7ee4047b1160cca5e1467a12351a40b3da123c870ba0b8eda2a"
[[package]]
+name = "atomptr"
+version = "1.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a29d2348bcb8ee04660963dbe624a1bbbd1ea49ca6ab51138aecb057135e3cb"
+
+[[package]]
name = "atty"
version = "0.2.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
@@ -1826,6 +1832,7 @@ name = "supergit"
version = "0.1.0"
dependencies = [
"async-std",
+ "atomptr",
"git2",
]
diff --git a/apps/servers/octopus/supergit/Cargo.toml b/apps/servers/octopus/supergit/Cargo.toml
index 029503e87c44..6de86dd640d2 100644
--- a/apps/servers/octopus/supergit/Cargo.toml
+++ b/apps/servers/octopus/supergit/Cargo.toml
@@ -7,4 +7,5 @@ edition = "2018"
[dependencies]
git2 = "0.11"
-async-std = { version = "1.0", features = ["unstable"] } \ No newline at end of file
+async-std = { version = "1.0", features = ["unstable"] }
+atomptr = "1.0" \ No newline at end of file
diff --git a/apps/servers/octopus/supergit/src/bin/test.rs b/apps/servers/octopus/supergit/src/bin/test.rs
index 25652ed0bd34..c4fcc2fbb3a8 100644
--- a/apps/servers/octopus/supergit/src/bin/test.rs
+++ b/apps/servers/octopus/supergit/src/bin/test.rs
@@ -13,31 +13,14 @@ fn main() {
};
let repo = Repository::open(path.as_str()).unwrap();
-
- let (tx, rx) = channel();
let branches = repo.branches().unwrap();
-
- branches
+ let main = branches
.into_iter()
- .filter(|b| b.name == Some("main".to_string()))
- .for_each(|b| tx.send(b.get_all()).unwrap());
-
- // Iterate over all branch iterators we get
- while let Some(biter) = rx.recv().ok() {
- use BranchCommit::*;
+ .filter(|b| b.name() == Some("master".to_string()))
+ .nth(0)
+ .unwrap();
- biter.for_each(|bc| match bc {
- Commit(c) => println!("{}: {}", c.id_str(), c.summary()),
- Merge(c, _b) => {
- println!("{}: {}", c.id_str(), c.summary());
- tx.send(_b.get_all()).unwrap();
- }
- Octopus(c, branches) => {
- println!("{}: {}", c.id_str(), c.summary());
- for _b in branches {
- tx.send(_b.get_all()).unwrap();
- }
- }
- });
- }
+ let head = main.get_head();
+ let tree = head.get_tree();
+
}
diff --git a/apps/servers/octopus/supergit/src/branch.rs b/apps/servers/octopus/supergit/src/branch.rs
index fe555d24292f..dd92aea93cba 100644
--- a/apps/servers/octopus/supergit/src/branch.rs
+++ b/apps/servers/octopus/supergit/src/branch.rs
@@ -8,8 +8,8 @@ use std::{mem, sync::Arc};
#[derive(Clone)]
pub struct Branch {
repo: Arc<Repository>,
- pub name: Option<String>,
- pub head: HashId,
+ name: Option<String>,
+ head: HashId,
}
impl Branch {
@@ -77,6 +77,16 @@ impl Branch {
SegLimit::Length(0, num),
)
}
+
+ /// Get the commit pointed at by HEAD
+ pub fn get_head(&self) -> Commit {
+ Commit::new(&self.repo, self.head.clone()).unwrap()
+ }
+
+ /// Get the branch name, if it exists
+ pub fn name(&self) -> Option<String> {
+ self.name.clone()
+ }
}
/// A branch segment iterator
diff --git a/apps/servers/octopus/supergit/src/commit.rs b/apps/servers/octopus/supergit/src/commit.rs
index 41c827f64d82..14f2d9bafdc1 100644
--- a/apps/servers/octopus/supergit/src/commit.rs
+++ b/apps/servers/octopus/supergit/src/commit.rs
@@ -1,4 +1,4 @@
-use crate::HashId;
+use crate::{FileTree, HashId};
use git2::Repository;
use std::sync::Arc;
@@ -14,21 +14,24 @@ pub struct Commit {
impl Commit {
/// Create a commit object and check if it exists in the repo
- pub fn new(r: &Arc<Repository>, id: HashId) -> Option<Self> {
+ pub(crate) fn new(r: &Arc<Repository>, id: HashId) -> Option<Self> {
r.find_commit(id.to_oid()).ok().map(|_| Self {
id,
repo: Arc::clone(r),
})
}
+ /// Get a utf-8 string representation of the commit ID
pub fn id_str(&self) -> String {
self.id.to_string()
}
+ /// Get the summary line as a utf-7 string
pub fn summary(&self) -> String {
self.find().summary().unwrap().into()
}
+ /// Get the number of parents
pub fn parent_count(&self) -> usize {
self.repo
.find_commit(self.id.to_oid())
@@ -44,6 +47,7 @@ impl Commit {
.and_then(|c| Self::new(&self.repo, c.id().into()))
}
+ /// Get a specific parent, if it exists
pub fn parent(&self, num: usize) -> Option<Self> {
self.find()
.parent(num)
@@ -58,6 +62,11 @@ impl Commit {
.collect()
}
+ /// Get the file tree for this commit
+ pub fn get_tree(&self) -> Arc<FileTree> {
+ FileTree::new(&self.repo, self.id.clone())
+ }
+
fn find(&self) -> git2::Commit {
self.repo.find_commit(self.id.to_oid()).unwrap()
}
diff --git a/apps/servers/octopus/supergit/src/files.rs b/apps/servers/octopus/supergit/src/files.rs
index 681b8877256f..2a1b69a34562 100644
--- a/apps/servers/octopus/supergit/src/files.rs
+++ b/apps/servers/octopus/supergit/src/files.rs
@@ -1,7 +1,146 @@
+use crate::{Branch, BranchIter, Commit, HashId};
+use git2::{ObjectType, TreeWalkMode, TreeWalkResult};
+use atomptr::AtomPtr;
+use std::collections::BTreeMap;
+use std::{path::PathBuf, sync::Arc};
-pub type FileId = usize;
+/// A tree of files
+pub struct FileTree {
+ repo: Arc<git2::Repository>,
+ tree: AtomPtr<BTreeMap<String, 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, |what, entry| {
+ let path_segs: Vec<_> = what.split("/").filter(|s| s != &"").collect();
+ let path = if path_segs.len() == 0 {
+ None
+ } else {
+ Some(path_segs)
+ };
+
+ println!("{:?} {}", path, entry.name().unwrap());
+ TreeWalkResult::Ok
+ })
+ .unwrap();
+ drop(tree);
+
+ // Atomicly swap new tree into place
+ self.tree.swap(new_tree);
+
+ self
+ }
+}
+
+/// An entry in a file tree
+///
+/// It's variants can either be a file (leaf), or a subtree, with it's
+/// own path handles, and children.
+pub enum TreeEntry {
+ /// A single file
+ File(File),
+ /// A sub-tree
+ Dir(Directory),
+}
+
+impl TreeEntry {
+ /// Create a tree entry from a path and `git2::TreeEntry`
+ fn generate(root: PathBuf, path_segments: Option<Vec<String>>, entry: git2::TreeEntry) -> Self {
+ let path = path_segments.map_or("".into(), |p| path_segs_join(p));
+
+ match entry.kind() {
+ Some(ObjectType::Blob) => Self::File(File::new(root, path)),
+ Some(ObjectType::Tree) => Self::Dir(Directory::new(root, path)),
+ _ => unimplemented!(),
+ }
+ }
+}
/// A file to have ever existed in a git repo
pub struct File {
- id: FileId,
+ root: PathBuf,
+ path: String,
+}
+
+impl File {
+ pub(crate) fn new(root: PathBuf, path: String) -> Self {
+ Self { root, path }
+ }
+
+ /// Get the history of a file from a branch iterator
+ pub fn get_history(&self, branch: BranchIter) -> Vec<Commit> {
+ todo!()
+ }
+}
+
+/// A subdirectory in a file tree
+///
+/// A directory has a set of children, which can either be Files, or
+/// other directories. Many of the functions to retrieve metadata
+/// (such as the last commit, count, etc) will be deferred to the
+/// children of this directory.
+pub struct Directory {
+ root: PathBuf,
+ path: String,
+}
+
+impl Directory {
+ pub(crate) fn new(root: PathBuf, path: String) -> Self {
+ Self { root, path }
+ }
+}
+
+/// Take a vector of path segments, and turn it into a valid offset path
+///
+/// There are tests to make sure this function works properly.
+/// Following are some example transformations.
+///
+/// * vec![] -> ""
+/// * vec!["foo"] -> "foo"
+/// * vec!["foo", "bar", "baz"] -> "foo/bar/baz"
+fn path_segs_join(segments: Vec<String>) -> String {
+ segments
+ .into_iter()
+ .fold(PathBuf::new(), |buf, seg| buf.join(seg))
+ .as_path()
+ .to_str()
+ .unwrap()
+ .to_owned()
+}
+
+#[test]
+fn empty_path() {
+ assert_eq!(path_segs_join(vec![]), String::from(""));
+}
+
+#[test]
+fn one_path() {
+ assert_eq!(path_segs_join(vec!["foo".into()]), String::from("foo"));
+}
+
+#[test]
+fn nested_path() {
+ assert_eq!(
+ path_segs_join(vec!["foo".into(), "bar".into(), "baz".into()]),
+ String::from("foo/bar/baz")
+ );
}
diff --git a/apps/servers/octopus/supergit/src/lib.rs b/apps/servers/octopus/supergit/src/lib.rs
index e839ab635dd1..df30d996ebed 100644
--- a/apps/servers/octopus/supergit/src/lib.rs
+++ b/apps/servers/octopus/supergit/src/lib.rs
@@ -1,15 +1,20 @@
-//! Strongly typed git repository explorer library
+//! Read-only git repository explorer library.
//!
-//! This library exposes a read-only view into a git repository. To
-//! get started, open an existing bare repo, and then call `sync()` to
-//! build a cache of it. Every time you want your view of the repo to
-//! update, call `sync()` again. If you want the sync operation to be
-//! blocking, call `sync_blocking()` instead.
+//! This library provides a more Rustic interface for libgit2, built
+//! on the `git2` bindings. If you want more low-level access to your
+//! repository, consider using that library instead.
//!
+//! supergit aims to make queries into a git repo as typed and easy as
+//! possible. Start by creating a [`Repository`](), and enumerating
+//! or fetching [`Branch`]()es that you are interested in.
//!
+//! Unlike `libgit2`, this library can resolve reverse dependencies
+//! between files, and their commit history. Some of these functions
+//! are very computationally intensive, and will be marked with their
+//! runtime cost.
mod branch;
-pub use branch::{Branch, BranchCommit};
+pub use branch::{Branch, BranchIter, BranchCommit};
mod commit;
pub use commit::Commit;
@@ -18,8 +23,11 @@ mod diff;
pub use diff::Diff;
mod repo;
-pub use repo::Repository;
pub(crate) use repo::HashId;
+pub use repo::Repository;
+
+mod files;
+pub use files::{File, FileTree};
use async_std::sync::{Arc, RwLock};
use std::sync::atomic::{AtomicUsize, Ordering};