parent
fcc89b1b5b
commit
c8f3364c74
@ -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<_>>() |
||||
); |
||||
} |
||||
|
@ -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") |
||||
); |
||||
} |
Loading…
Reference in new issue