|
|
|
@ -1,7 +1,7 @@ |
|
|
|
|
//! Low-level abstraction over finding refs inside a commit tree
|
|
|
|
|
|
|
|
|
|
use super::tree_utils as utils; |
|
|
|
|
use crate::HashId; |
|
|
|
|
use super::tree_utils::{self as utils, PairIter}; |
|
|
|
|
use crate::{branch::BranchIter, files::Yield, Commit, HashId}; |
|
|
|
|
use git2::{ObjectType, Repository, TreeWalkMode, TreeWalkResult}; |
|
|
|
|
use std::sync::Arc; |
|
|
|
|
|
|
|
|
@ -25,6 +25,80 @@ impl FileTree { |
|
|
|
|
Self { repo, c } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Get a FileTree for a new commit
|
|
|
|
|
fn checkout(&self, c: HashId) -> Self { |
|
|
|
|
Self::new(Arc::clone(&self.repo), c) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Get the history of a path with a branch iterator
|
|
|
|
|
pub fn base_history(&self, iter: BranchIter, path: &str) -> Option<Vec<Commit>> { |
|
|
|
|
let mut iter = iter.peekable(); |
|
|
|
|
|
|
|
|
|
let entry = self.resolve(path)?; |
|
|
|
|
let trgid = entry.id(); |
|
|
|
|
|
|
|
|
|
let mut commits = vec![]; |
|
|
|
|
|
|
|
|
|
// Iterate over the branch in commit pairs
|
|
|
|
|
while let (Some(a), b) = iter.next_pair() { |
|
|
|
|
dbg!(&a.commit()); |
|
|
|
|
let ta = self.checkout(a.commit().id.clone()); |
|
|
|
|
let te_a = dbg!(ta.resolve(dbg!(path))); |
|
|
|
|
|
|
|
|
|
let b = match b { |
|
|
|
|
Some(b) => b, |
|
|
|
|
None if te_a.is_some() => { |
|
|
|
|
// If b doesn't exist, but the path exists in a,
|
|
|
|
|
// then it is safe to assume that a introduces the
|
|
|
|
|
// path
|
|
|
|
|
commits.push(a.commit().clone()); |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
None => break, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let tb = self.checkout(b.commit().id.clone()); |
|
|
|
|
let te_b = match ta.resolve(path) { |
|
|
|
|
Some(b) => b, |
|
|
|
|
None => continue, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
let te_a = match te_a { |
|
|
|
|
Some(a) => a, |
|
|
|
|
None => continue, |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
// If the two tree nodes are not equal, add the commit to
|
|
|
|
|
// the list. This means that the `a` commit changed
|
|
|
|
|
// something in the path.
|
|
|
|
|
if dbg!(te_a != te_b) { |
|
|
|
|
commits.push(a.commit().clone()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Some(commits) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Enumerate a non-leaf tree entry
|
|
|
|
|
pub fn enumerate(&self, path: &str) -> Option<Vec<TreeEntry>> { |
|
|
|
|
let tree = utils::open_tree(&self.repo, &self.c)?; |
|
|
|
|
let target = utils::path_split(path); |
|
|
|
|
|
|
|
|
|
let mut entries = vec![]; |
|
|
|
|
tree.walk(TreeWalkMode::PreOrder, |p, e| { |
|
|
|
|
let path = utils::path_split(p); |
|
|
|
|
if path == target { |
|
|
|
|
entries.push(TreeEntry::new(p, &e)); |
|
|
|
|
TreeWalkResult::Ok |
|
|
|
|
} else { |
|
|
|
|
TreeWalkResult::Skip |
|
|
|
|
} |
|
|
|
|
}) |
|
|
|
|
.ok()?; |
|
|
|
|
|
|
|
|
|
Some(entries) |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Resolve a path inside this file tree
|
|
|
|
|
///
|
|
|
|
|
/// Will return `None` if there is no tree for the selected
|
|
|
|
@ -38,14 +112,15 @@ impl FileTree { |
|
|
|
|
|
|
|
|
|
// Walk over tree and swallor errors (which we use to
|
|
|
|
|
// terminace traversal to speed up indexing time)
|
|
|
|
|
let _ = tree.walk(TreeWalkMode::PreOrder, |p, e| { |
|
|
|
|
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 |
|
|
|
|
} |
|
|
|
|
}); |
|
|
|
|
}) |
|
|
|
|
.ok()?; |
|
|
|
|
|
|
|
|
|
// Return whatever the entry is now
|
|
|
|
|
entry |
|
|
|
@ -57,10 +132,12 @@ impl FileTree { |
|
|
|
|
/// This type is lazily loaded, and can represent either a Blob or a
|
|
|
|
|
/// Directory. You can resolve its value by calling
|
|
|
|
|
/// [`resolve()`](Self::resolve)
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)] |
|
|
|
|
pub struct TreeEntry { |
|
|
|
|
tt: EntryType, |
|
|
|
|
id: HashId, |
|
|
|
|
path: String, |
|
|
|
|
name: Option<String>, |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
impl TreeEntry { |
|
|
|
@ -72,15 +149,28 @@ impl TreeEntry { |
|
|
|
|
}; |
|
|
|
|
let id = entry.id().into(); |
|
|
|
|
let path = path.into(); |
|
|
|
|
let name = entry.name().map(|s| s.into()); |
|
|
|
|
|
|
|
|
|
Self { tt, id, path, name } |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
Self { tt, id, path } |
|
|
|
|
pub fn id(&self) -> HashId { |
|
|
|
|
self.id.clone() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Get a reference to the name of this TreeEntry
|
|
|
|
|
pub fn name(&self) -> Option<&String> { |
|
|
|
|
self.name.as_ref() |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Resolve this type to a [`Yield`]()
|
|
|
|
|
pub fn resolve(&self) {} |
|
|
|
|
pub fn resolve(&self) -> Yield { |
|
|
|
|
todo!() |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// Type of a TreeEntry
|
|
|
|
|
#[derive(Clone, Debug, PartialEq, Eq)] |
|
|
|
|
pub enum EntryType { |
|
|
|
|
/// A file that can be loaded
|
|
|
|
|
File, |
|
|
|
@ -89,7 +179,7 @@ pub enum EntryType { |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn index_tree() { |
|
|
|
|
fn s_resolve() { |
|
|
|
|
let path = env!("CARGO_MANIFEST_DIR").to_owned() + "/test-repo"; |
|
|
|
|
use crate::Repository as Repo; |
|
|
|
|
|
|
|
|
@ -100,5 +190,48 @@ fn index_tree() { |
|
|
|
|
let h = b.head(); |
|
|
|
|
|
|
|
|
|
let t = h.tree(); |
|
|
|
|
t.resolve("README".into()).unwrap(); |
|
|
|
|
t.resolve("README").unwrap(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn s_enumerate() { |
|
|
|
|
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(); |
|
|
|
|
let entries = t.enumerate("").unwrap(); |
|
|
|
|
|
|
|
|
|
assert_eq!( |
|
|
|
|
entries |
|
|
|
|
.iter() |
|
|
|
|
.filter_map(|e| e.name().map(|s| s.as_str())) |
|
|
|
|
.collect::<Vec<_>>(), |
|
|
|
|
vec!["README", "test.rs"] |
|
|
|
|
); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#[test] |
|
|
|
|
fn s_history() { |
|
|
|
|
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 head = b.head(); |
|
|
|
|
let iter = b.get_all(); |
|
|
|
|
|
|
|
|
|
let tree = head.tree(); |
|
|
|
|
let history = tree.base_history(iter, "test.rs").unwrap(); |
|
|
|
|
|
|
|
|
|
dbg!(&history); |
|
|
|
|
assert_eq!(history.len(), 1); |
|
|
|
|
} |
|
|
|
|