octopus: supergit: add interface to query specific store paths

wip/yesman
Katharina Fey 3 years ago
parent fcc89b1b5b
commit c8f3364c74
  1. 57
      apps/servers/octopus/supergit/src/bin/test.rs
  2. 6
      apps/servers/octopus/supergit/src/branch.rs
  3. 6
      apps/servers/octopus/supergit/src/commit.rs
  4. 9
      apps/servers/octopus/supergit/src/files/explorer.rs
  5. 15
      apps/servers/octopus/supergit/src/files/mod.rs
  6. 104
      apps/servers/octopus/supergit/src/files/tree.rs
  7. 61
      apps/servers/octopus/supergit/src/files/tree_utils.rs
  8. 14
      apps/servers/octopus/supergit/src/lib.rs
  9. 36
      apps/servers/octopus/supergit/src/old-files.rs
  10. 4
      apps/servers/octopus/supergit/src/repo.rs

@ -1,33 +1,38 @@
//! A test binary to use during development
use std::sync::mpsc::channel;
use supergit::{BranchCommit, Repository};
// use std::sync::mpsc::channel;
// use supergit::{BranchCommit, Repository};
fn main() {
let path = match std::env::args().nth(1) {
Some(p) => p,
None => {
eprintln!("USAGE: supergit-test <path>");
std::process::exit(2);
}
};
// fn main() {
// let path = match std::env::args().nth(1) {
// Some(p) => p,
// None => {
// eprintln!("USAGE: supergit-test <path>");
// std::process::exit(2);
// }
// };
// let repo = Repository::open(path.as_str()).unwrap();
// let branches = repo.branches().unwrap();
// let main = branches
// .into_iter()
// .filter(|b| b.name() == Some("master".to_string()))
// .nth(0)
// .unwrap();
let repo = Repository::open(path.as_str()).unwrap();
let branches = repo.branches().unwrap();
let main = branches
.into_iter()
.filter(|b| b.name() == Some("master".to_string()))
.nth(0)
.unwrap();
// let head = main.get_head();
// let tree = head.get_tree();
// println!(
// "{:#?}",
// tree.history(main.get_all(), "Cargo.toml")
// .into_iter()
// .map(|c| c.summary())
// .collect::<Vec<_>>()
// );
// }
let head = main.get_head();
let tree = head.get_tree();
fn main() {
println!(
"{:#?}",
tree.history(main.get_all(), "Cargo.toml")
.into_iter()
.map(|c| c.summary())
.collect::<Vec<_>>()
);
}

@ -1,3 +1,5 @@
//! Type system for working with git branches
use crate::{Commit, HashId};
use atomptr::AtomPtr;
use git2::Repository;
@ -114,7 +116,7 @@ impl Branch {
}
/// Get the current HEAD commit
pub fn get_head(&self) -> Commit {
pub fn head(&self) -> Commit {
Commit::new(&self.repo, self.head.clone()).unwrap()
}
@ -326,7 +328,7 @@ pub enum IterMode {
BreadthFirst,
}
/// the limit applied to a branch segment
/// Limiter applied to a branch segment
pub enum SegLimit {
/// No limit, enumerating all children
None,

@ -1,4 +1,4 @@
use crate::{Diff, FileTree, HashId};
use crate::{files::FileTree, Diff, HashId};
use git2::Repository;
use std::sync::Arc;
@ -67,8 +67,8 @@ impl Commit {
}
/// Get the file tree for this commit
pub fn get_tree(&self) -> Arc<FileTree> {
FileTree::new(&self.repo, self.id.clone())
pub fn tree(&self) -> FileTree {
FileTree::new(Arc::clone(&self.repo), self.id.clone())
}
/// Get the list of paths in the repository touched by this commit

@ -0,0 +1,9 @@
///
pub struct Explorer {}
///
pub struct Yield {}
///
pub enum YieldType {}

@ -0,0 +1,15 @@
//! File tree abstractions
//!
//! The supergit files API is split into two parts: `FileTree`, which
//! is mostly used internally and only exposed to allow external tools
//! to rely on the same indexing mechanism as supergit, and
//! `Explorer`, which is a high-level API for loading trees for
//! specific commits.
//!
pub(self) mod tree_utils;
mod tree;
pub use tree::{FileTree, TreeEntry, EntryType};
mod explorer;
pub use explorer::{Explorer, Yield, YieldType};

@ -0,0 +1,104 @@
//! Low-level abstraction over finding refs inside a commit tree
use super::tree_utils as utils;
use crate::HashId;
use git2::{ObjectType, Repository, TreeWalkMode, TreeWalkResult};
use std::sync::Arc;
/// A git directory tree walker abstraction
///
/// This type is meant to be used ephemerally, and internally uses the
/// libgit2 `Tree` abstraction to walk directory trees lazily to
/// resolve paths to [`TreeEntry`](self::TreeEntry)'s.
///
/// Note: this type _may_ be removed in the future. For a more
/// high-level (and stable) API, check
/// [`Explorer`](crate::files::Explorer)
pub struct FileTree {
repo: Arc<Repository>,
c: HashId,
}
impl FileTree {
/// Construct a new FileTree with a repository
pub(crate) fn new(repo: Arc<Repository>, c: HashId) -> Self {
Self { repo, c }
}
/// Resolve a path inside this file tree
///
/// Will return `None` if there is no tree for the selected
/// commit, or the file inside the tree does not exist.
pub fn resolve(&self, path: &str) -> Option<TreeEntry> {
let tree = utils::open_tree(&self.repo, &self.c)?;
let target = utils::path_split(path);
// Initialise entry to None as a fallback
let mut entry = None;
// Walk over tree and swallor errors (which we use to
// terminace traversal to speed up indexing time)
let _ = tree.walk(TreeWalkMode::PreOrder, |p, e| {
if utils::path_cmp(&target, p, e.name().unwrap()) {
entry = Some(TreeEntry::new(p, &e));
TreeWalkResult::Ok
} else {
TreeWalkResult::Skip
}
});
// Return whatever the entry is now
entry
}
}
/// An entry in a commit tree
///
/// This type is lazily loaded, and can represent either a Blob or a
/// Directory. You can resolve its value by calling
/// [`resolve()`](Self::resolve)
pub struct TreeEntry {
tt: EntryType,
id: HashId,
path: String,
}
impl TreeEntry {
fn new(path: &str, entry: &git2::TreeEntry) -> Self {
let tt = match entry.kind() {
Some(ObjectType::Blob) => EntryType::File,
Some(ObjectType::Tree) => EntryType::Dir,
_ => unimplemented!(),
};
let id = entry.id().into();
let path = path.into();
Self { tt, id, path }
}
/// Resolve this type to a [`Yield`]()
pub fn resolve(&self) {}
}
/// Type of a TreeEntry
pub enum EntryType {
/// A file that can be loaded
File,
/// A directory that can be indexed
Dir,
}
#[test]
fn index_tree() {
let path = env!("CARGO_MANIFEST_DIR").to_owned() + "/test-repo";
use crate::Repository as Repo;
eprintln!("Path: `{}`", path);
let r = Repo::open(&path).unwrap();
let b = r.branch("master".into()).unwrap();
let h = b.head();
let t = h.tree();
t.resolve("README".into()).unwrap();
}

@ -0,0 +1,61 @@
//! A set of tree utilities used internally in the tree.rs module
use crate::HashId;
use git2::{Repository, Tree};
use std::{path::PathBuf, sync::Arc};
/// 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"
pub(super) fn path_segs_join(segments: Vec<&str>) -> String {
segments
.into_iter()
.fold(PathBuf::new(), |buf, seg| buf.join(seg))
.as_path()
.to_str()
.unwrap()
.to_owned()
}
/// Take an offset path inside the repository and split it into a vec
pub(super) fn path_split(path: &str) -> Vec<&str> {
path.split("/").filter(|s| s != &"").collect()
}
/// Compare a path and entry to a target buffer
pub(super) fn path_cmp(target: &Vec<&str>, path: &str, entry: &str) -> bool {
let mut buf = path_split(path);
buf.push(entry);
eprintln!("{:?}", buf);
target == &buf
}
/// Open a tree for a particular commit
pub(super) fn open_tree<'r>(repo: &'r Arc<Repository>, c: &HashId) -> Option<Tree<'r>> {
repo.find_commit(c.to_oid()).unwrap().tree().ok()
}
#[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")
);
}

@ -15,8 +15,7 @@
//! are very computationally intensive, and will be marked with their
//! runtime cost.
mod branch;
pub use branch::{Branch, BranchIter, BranchCommit};
pub mod branch;
mod commit;
pub use commit::Commit;
@ -28,8 +27,11 @@ mod repo;
pub(crate) use repo::HashId;
pub use repo::Repository;
mod files;
pub use files::{Yield, FileTree};
pub mod files;
use async_std::sync::{Arc, RwLock};
use std::sync::atomic::{AtomicUsize, Ordering};
/// Contains all core functions and types in supergit
pub mod prelude {
pub use crate::branch::{Branch, BranchIter};
pub use crate::files::{EntryType, Explorer, TreeEntry};
pub use crate::{Commit, Diff, Repository};
}

@ -230,39 +230,3 @@ impl Directory {
}
////////////////////////////////
/// 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<&str>) -> 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")
);
}

@ -1,6 +1,6 @@
//! Raw representation wrappers for libgit2
use crate::{Branch, BranchCommit};
use crate::branch::{Branch, BranchCommit};
use git2::{self, Oid};
use std::{fmt, sync::Arc};
@ -104,7 +104,7 @@ impl Repository {
/// desired one. If you want to make repeated queries onto the
/// branch set, it's recommended you call `branches()`, and cache
/// the data yourself.
pub fn get_branch(&self, name: String) -> Option<Branch> {
pub fn branch(&self, name: String) -> Option<Branch> {
self.branches().ok().and_then(|ok| {
ok.into_iter()
.filter(|b| b.name().is_some())

Loading…
Cancel
Save