apps/servers/octopus: implement initial branch parsing

wip/yesman
Katharina Fey 4 years ago
parent eaf889ee80
commit eb1b781fab
  1. 11
      apps/servers/octopus/supergit/src/bin/test.rs
  2. 51
      apps/servers/octopus/supergit/src/raw/branch.rs
  3. 65
      apps/servers/octopus/supergit/src/raw/mod.rs
  4. 167
      apps/servers/octopus/supergit/src/raw/tree_walk.rs

@ -10,7 +10,14 @@ fn main() {
std::process::exit(2);
}
};
let rr = RawRepository::open(path.as_str()).unwrap();
let branches = rr.parse_branches().unwrap();
for branch in branches {
if branch.name.as_str() != "main" && continue {}
println!("Branch: {}", branch.name);
branch.enumerate(" ".into(), &rr.inner);
}
}

@ -0,0 +1,51 @@
use super::{HashId, RawRepository};
use crate::Branch;
use git2::{Commit, Repository};
/// Represent some raw branch metadata
pub struct RawBranch {
pub name: String,
pub head: HashId,
}
fn print_commit(i: &String, c: &Commit) {
println!(
"{}{}: {}",
i,
c.id().to_string(),
c.message().unwrap().trim().split("\n").nth(0).unwrap()
);
}
fn print_parent_tree(c: &Commit, indent: String) {
c.parents().for_each(|c| {
println!(
"{}{}: {}",
indent,
c.id().to_string(),
c.message().unwrap().trim().split("\n").nth(0).unwrap()
);
print_parent_tree(&c, indent.clone());
});
}
impl RawBranch {
/// Consume branch reference and enumerate real branch history
pub fn into_branch(self, repo: &mut RawRepository) -> Branch {
todo!()
}
/// **REMOVE ME** A test function to do some test things
pub fn enumerate(&self, indent: String, repo: &Repository) {
let c = repo.find_commit((&self.head).into()).unwrap();
println!(
"{}{}: {}",
indent,
c.id().to_string(),
c.message().unwrap().trim().split("\n").nth(0).unwrap()
);
print_parent_tree(&c, indent);
}
}

@ -1,13 +1,45 @@
//! Raw representation wrappers for libgit2
mod branch;
pub use branch::RawBranch;
mod branch_walk;
mod tree_walk;
use crate::{Branch, BranchCommit};
use git2::{self, Repository};
use git2::{self, Oid, Repository};
pub type RawResult<T> = Result<T, RawError>;
/// The hex ID of a commit
#[derive(Debug)]
pub struct HashId(String);
impl From<Oid> for HashId {
fn from(o: Oid) -> Self {
Self(o.to_string())
}
}
impl From<HashId> for Oid {
fn from(hid: HashId) -> Self {
Oid::from_str(hid.0.as_str()).expect(&format!(
"Tried turning an invalid HashId variant into an Oid: {:?}",
hid
))
}
}
impl<'any> From<&'any HashId> for Oid {
fn from(hid: &'any HashId) -> Self {
Oid::from_str(hid.0.as_str()).expect(&format!(
"Tried turning an invalid HashId variant into an Oid: {:?}",
hid
))
}
}
/// An error abstraction for raw git operations
#[derive(Debug)]
pub enum RawError {
@ -20,15 +52,9 @@ impl From<git2::Error> for RawError {
}
}
/// Represent a raw branch
pub struct RawBranch {
name: String,
head: String,
}
/// Wrap a libgit2 repository to provide an API fascade
pub struct RawRepository {
inner: Repository,
pub inner: Repository,
}
impl RawRepository {
@ -39,7 +65,30 @@ impl RawRepository {
}
/// Parse branch data from repository
///
/// ## Panics
///
/// If there is an error around getting the name, or head commit.
pub fn parse_branches(&self) -> RawResult<Vec<RawBranch>> {
Ok(self
.inner
.branches(None)?
.into_iter()
.filter_map(|e| e.ok())
.map(|(branch, _)| {
let name = branch.name().unwrap().unwrap().into();
let head = branch.get().peel_to_commit().unwrap().id().into();
RawBranch { name, head }
})
.collect())
}
/// Get the files touched by a commit
pub fn get_files_for(&self, id: HashId) -> RawResult<Vec<()>> {
let c = self.inner.find_commit(id.into())?;
let tree = c.tree()?;
todo!()
}
}

@ -1 +1,168 @@
//! Walk the file tree for a particular commit
use git2::{self, ObjectType, TreeWalkMode};
use std::collections::BTreeMap;
/// A cache of a repository tree
#[derive(Default, Debug, Clone)]
pub(crate) struct Tree {
inner: BTreeMap<String, TreeNode>,
}
impl Tree {
/// Insert a node into a subtree with it's full path
fn insert_to_subtree(&mut self, mut path: Vec<String>, name: String, node: TreeNode) {
// If we are given a path, resolve it first
let curr = if path.len() > 0 {
let rest = path.split_off(1);
let mut curr = self.inner.get_mut(&path[0]).unwrap();
for dir in rest {
match curr {
TreeNode::Dir(ref mut d) => {
curr = d.children.inner.get_mut(&dir).unwrap();
}
_ => panic!("Not a tree!"),
}
}
match curr {
TreeNode::Dir(ref mut d) => &mut d.children,
TreeNode::File(_) => panic!("Not a tree!"),
}
} else {
// If no path was given, we assume the root is meant
self
};
curr.inner.insert(name, node);
}
/// Walk through the tree and only return filenode objects
pub(crate) fn flatten(&self) -> Vec<FileNode> {
self.inner.values().fold(vec![], |mut vec, node| {
match node {
TreeNode::File(f) => vec.push(f.clone()),
TreeNode::Dir(d) => vec.append(&mut d.children.flatten()),
}
vec
})
}
/// Get all the commits that touch a file
pub(crate) fn grab_path_history(&self, path: String) -> String {
let mut path: Vec<String> = path
.split("/")
.filter_map(|seg| match seg {
"" => None,
val => Some(val.into()),
})
.collect();
let leaf = if path.len() > 0 {
let rest = path.split_off(1);
let mut curr = self.inner.get(&path[0]).unwrap();
for dir in rest {
match curr {
TreeNode::Dir(d) => curr = d.children.inner.get(&dir).unwrap(),
TreeNode::File(_) => break, // we reached the leaf
}
}
curr
} else {
panic!("No valid path!");
};
match leaf {
TreeNode::File(f) => f.id.clone(),
_ => panic!("Not a leaf!"),
}
}
}
#[derive(Clone, Debug)]
pub(crate) enum TreeNode {
File(FileNode),
Dir(DirNode),
}
impl TreeNode {
fn name(&self) -> String {
match self {
Self::File(f) => f.name.clone(),
Self::Dir(d) => d.name.clone(),
}
}
}
#[derive(Clone, Debug)]
pub(crate) struct FileNode {
pub id: String,
pub path: Vec<String>,
pub name: String,
}
#[derive(Clone, Debug)]
pub(crate) struct DirNode {
pub path: Vec<String>,
pub name: String,
pub children: Tree,
}
impl DirNode {
fn append(&mut self, node: TreeNode) {
self.children.inner.insert(node.name(), node);
}
}
/// Take a series of path-segments and render a tree at that location
pub(crate) fn parse_tree(tree: git2::Tree) -> Tree {
let mut root = Tree::default();
tree.walk(TreeWalkMode::PreOrder, |path, entry| {
let path: Vec<String> = path
.split("/")
.filter_map(|seg| match seg {
"" => None,
val => Some(val.into()),
})
.collect();
let name = entry.name().unwrap().to_string();
match entry.kind() {
// For every tree in the tree we create a new TreeNode with the path we know about
Some(ObjectType::Tree) => {
root.insert_to_subtree(
path.clone(),
name.clone(),
TreeNode::Dir(DirNode {
path,
name,
children: Tree::default(),
}),
);
}
// If we encounter a blob, this is a file that we can simply insert into the tree
Some(ObjectType::Blob) => {
root.insert_to_subtree(
path.clone(),
name.clone(),
TreeNode::File(FileNode {
id: format!("{}", entry.id()),
path,
name,
}),
);
}
_ => {}
}
0
})
.unwrap();
root
}

Loading…
Cancel
Save