diff --git a/radicle-surf/src/history.rs b/radicle-surf/src/history.rs new file mode 100644 index 0000000..28afa5b --- /dev/null +++ b/radicle-surf/src/history.rs @@ -0,0 +1,118 @@ +use std::{marker::PhantomData, path::PathBuf}; + +use git2::Revwalk; + +pub struct History<'a, A> { + walk: Revwalk<'a>, + repo: &'a git2::Repository, + filter_by: Option, + marker: PhantomData, +} + +enum FilterBy { + File { name: PathBuf }, +} + +impl<'a, A> History<'a, A> { + pub fn new(repo: &'a git2::Repository, start: git2::Oid) -> Result { + let mut walk = repo.revwalk()?; + walk.set_sorting(git2::Sort::TOPOLOGICAL)?; + walk.simplify_first_parent()?; + walk.push(start)?; + Ok(Self { + walk, + repo, + filter_by: None, + marker: PhantomData, + }) + } +} + +impl<'a> History<'a, BlobAt<'a>> { + pub fn file( + repo: &'a git2::Repository, + start: git2::Oid, + name: PathBuf, + ) -> Result { + let mut history = Self::new(repo, start)?; + history.filter_by = Some(FilterBy::File { name }); + Ok(history) + } +} + +impl<'a> Iterator for History<'a, git2::Commit<'a>> { + type Item = Result, git2::Error>; + + fn next(&mut self) -> Option { + match self.walk.next() { + None => None, + Some(oid) => { + // TODO: skip if it is not found, perhaps? + oid.and_then(|oid| self.repo.find_commit(oid)) + .map(Some) + .transpose() + }, + } + } +} + +pub struct BlobAt<'a> { + commit: git2::Commit<'a>, + blob: git2::Blob<'a>, +} + +impl<'a> BlobAt<'a> { + pub fn commit(&self) -> &git2::Commit<'a> { + &self.commit + } + + pub fn blob(&self) -> &git2::Blob<'a> { + &self.blob + } +} + +impl<'a> Iterator for History<'a, BlobAt<'a>> { + type Item = Result, git2::Error>; + + fn next(&mut self) -> Option { + match self.walk.next() { + None => None, + Some(oid) => oid + .and_then(|oid| { + let commit = self.repo.find_commit(oid)?; + let tree = commit.tree()?; + + // debug_tree(&tree)?; + + match &self.filter_by { + Some(FilterBy::File { name }) => { + let entry = tree.get_path(name)?; + match entry.to_object(self.repo)?.into_blob() { + Ok(blob) => Ok(BlobAt { commit, blob }), + Err(obj) => Err(git2::Error::new( + git2::ErrorCode::NotFound, + git2::ErrorClass::Object, + &format!( + "history file path filter did not exist, found {}", + obj.kind() + .map(|obj| obj.to_string()) + .unwrap_or_else(|| "Unknown Object".to_owned()) + ), + )), + } + }, + None => todo!(), + } + }) + .map(Some) + .transpose(), + } + } +} + +fn debug_tree(tree: &git2::Tree) -> Result<(), git2::Error> { + tree.walk(git2::TreeWalkMode::PreOrder, |s, entry| { + println!("{}, {:?}", s, entry.name()); + git2::TreeWalkResult::Ok + }) +} diff --git a/radicle-surf/src/lib.rs b/radicle-surf/src/lib.rs index 34dcbbe..7f87714 100644 --- a/radicle-surf/src/lib.rs +++ b/radicle-surf/src/lib.rs @@ -15,7 +15,7 @@ // You should have received a copy of the GNU General Public License // along with this program. If not, see . -#![deny(missing_docs, unused_import_braces, unused_qualifications, warnings)] +#![deny(unused_import_braces, unused_qualifications)] //! Welcome to `radicle-surf`! //! @@ -84,6 +84,7 @@ //! ``` pub mod diff; pub mod file_system; +pub mod history; pub mod vcs; pub mod commit; diff --git a/radicle-surf/t/src/history.rs b/radicle-surf/t/src/history.rs new file mode 100644 index 0000000..84c06ea --- /dev/null +++ b/radicle-surf/t/src/history.rs @@ -0,0 +1,27 @@ +use std::path::Path; + +use radicle_surf::history::History; + +const GIT_PLATINUM: &str = "../data/git-platinum"; + +#[test] +pub fn test() { + let repo = git2::Repository::open(GIT_PLATINUM) + .expect("Could not retrieve ./data/git-platinum as git repository"); + let start = git2::Oid::from_str("3873745c8f6ffb45c990eb23b491d4b4b6182f95").unwrap(); + let history = History::file(&repo, start, Path::new("src/memory.rs").to_path_buf()).unwrap(); + + for blob in history { + match blob { + Ok(blob_at) => { + println!("================================================\n"); + println!("Commit: {}\n", blob_at.commit().id()); + println!("{}", std::str::from_utf8(blob_at.blob().content()).unwrap()); + println!("\n\n"); + }, + Err(err) => println!("Error: {}", err), + } + } + + assert!(false); +} diff --git a/radicle-surf/t/src/lib.rs b/radicle-surf/t/src/lib.rs index 40d1e3e..3f2539b 100644 --- a/radicle-surf/t/src/lib.rs +++ b/radicle-surf/t/src/lib.rs @@ -7,5 +7,8 @@ mod git; #[cfg(test)] mod diff; +#[cfg(test)] +mod history; + #[cfg(test)] mod file_system;