From a0dca8186bdef76e09e9c388d7f85839e85ce8db Mon Sep 17 00:00:00 2001 From: Katharina Fey Date: Mon, 22 Jun 2020 16:33:28 +0200 Subject: git: refactoring git module to have a unified actions API --- src/git/log.rs | 57 ++++++++++++++++++++++++++++++++++----------------------- src/git/mod.rs | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++- src/git/repo.rs | 19 ------------------- src/git/tree.rs | 32 ++++++++++++++++++++++++++++++++ src/main.rs | 6 ------ 5 files changed, 120 insertions(+), 49 deletions(-) delete mode 100644 src/git/repo.rs (limited to 'src') diff --git a/src/git/log.rs b/src/git/log.rs index 97101cf..c8f4aa3 100644 --- a/src/git/log.rs +++ b/src/git/log.rs @@ -1,7 +1,7 @@ //! libgit2 log parsing -use crate::git::{self, tree::FileNode}; -use git2::{Oid, Repository}; +use crate::git::{tree::FileNode, Repo}; +use git2::Oid; use std::collections::{BTreeMap, BTreeSet}; /// A file-commit referenced graph thing @@ -17,8 +17,11 @@ use std::collections::{BTreeMap, BTreeSet}; /// change" and it will tell you (sort of). #[derive(Debug, Default)] pub(crate) struct CommitGraph { + /// The correct order of commits in the log order: Vec, - file_refs: BTreeMap>, + /// List of all files, and the commits in which they were touched + file_refs: BTreeMap>, + /// Map of commit IDs to metadata commit_refs: BTreeMap, } @@ -31,19 +34,19 @@ pub(crate) struct CommitNode { time: i64, } -fn build_diff_log(repo: &Repository, log: Vec<(String, Vec)>) -> Vec { +fn build_diff_log(repo: &Repo, log: Vec<(String, Vec)>) -> Vec { todo!() } /// Walk through all commits from a given ref and build a commit graph -pub(crate) fn create_commit_log(id: String, repo: &Repository) -> CommitGraph { - let mut walker = repo.revwalk().unwrap(); - walker.push(Oid::from_str(id.as_str()).unwrap()).unwrap(); +pub(crate) fn create_commit_log(rev: String, repo: &Repo) -> CommitGraph { + let mut walker = repo.get_inner().revwalk().unwrap(); + walker.push(Oid::from_str(rev.as_str()).unwrap()).unwrap(); let mut commits = walker .into_iter() .map(|oid| { let oid = oid.unwrap(); - repo.find_commit(oid).unwrap() + repo.get_inner().find_commit(oid).unwrap() }) .collect::>(); commits.reverse(); @@ -52,7 +55,7 @@ pub(crate) fn create_commit_log(id: String, repo: &Repository) -> CommitGraph { .into_iter() .map(|commit| { let id = format!("{}", commit.id()); - (id.clone(), git::repo::get_tree(&repo, id.as_str())) + (id.clone(), repo.get_tree(id.as_str())) }) .collect(); @@ -64,43 +67,51 @@ pub(crate) fn create_commit_log(id: String, repo: &Repository) -> CommitGraph { let (commit_refs, file_refs) = log.into_iter().fold( (BTreeMap::new(), BTreeMap::new()), |(mut cm, mut fm), (cid, current)| { + let commit_id = format!("{}", cid); + let d = repo + .get_inner() .diff_tree_to_tree(Some(&previous), Some(¤t), None) .unwrap(); - let files = d.deltas().fold(BTreeSet::new(), |mut set, delta| { - set.insert(format!("{}", delta.new_file().id())); + + // Store the commit to preserve order + order.push(commit_id.clone()); + + // For each file, store this commit as one that touched it + let touches = d.deltas().fold(BTreeSet::new(), |mut set, delta| { + let file_id = format!("{}", delta.new_file().id()); + fm.entry(file_id.clone()) + .or_insert(vec![]) + .push(commit_id.clone()); + set.insert(file_id); set }); - order.push(cid.clone()); - fm.insert(cid.clone(), files.clone()); - + // From the commit, build a metadata object let commit_u = repo + .get_inner() .find_commit(Oid::from_str(cid.as_str()).unwrap()) .unwrap(); let author_u = commit_u.author(); let commit = CommitNode { - id: cid.clone(), + id: commit_id, message: commit_u.message().unwrap().to_owned(), author: format!("{} {}", author_u.name().unwrap(), author_u.email().unwrap()), - touches: files, + touches, time: author_u.when().seconds(), }; + // Insert the metadata object cm.insert(cid.clone(), commit); + // We pass both the modified maps into the next commit (cm, fm) }, ); - let graph = CommitGraph { + CommitGraph { order, file_refs, commit_refs, - }; - - dbg!(graph); - - // let diffs = build_diff_log(&repo, log); - todo!() + } } diff --git a/src/git/mod.rs b/src/git/mod.rs index 1cd9057..244e2f4 100644 --- a/src/git/mod.rs +++ b/src/git/mod.rs @@ -1,5 +1,58 @@ //! Wrappers for libgit2 pub mod log; -pub mod repo; pub mod tree; + +use git2::{self, Repository}; +use log::CommitGraph; +use tree::Tree; + +/// A top-level wrapper API for all libgit2 functions +pub struct Repo { + inner: Repository, + commits: Option, + rev: Option, +} + +impl Repo { + pub(crate) fn new(path: &str) -> Self { + Self { + inner: Repository::open(path).expect(&format!("`{}` is not a valid git repo", path)), + commits: None, + rev: None, + } + } + + pub(self) fn get_inner(&self) -> &Repository { + &self.inner + } + + pub(self) fn get_tree<'r>(&'r self, rev: &str) -> git2::Tree<'r> { + self.inner + .revparse_single(rev) + .unwrap() + .peel_to_tree() + .unwrap() + } + + pub(crate) fn clear_cache(&mut self) { + self.rev = None; + self.commits = None; + } + + /// Load and cache commits for a specific rev + pub(crate) fn load_commits(&mut self, rev: String) { + self.rev = Some(rev.clone()); + self.commits = Some(log::create_commit_log(rev, &self)); + } + + /// Load the tree of files for the current rev + /// + /// Will fail if no rev was previously cached + pub(crate) fn get_file_tree(&self) -> Tree { + tree::parse_tree( + self.get_tree(self.rev.as_ref().unwrap().as_str()), + self.get_inner(), + ) + } +} diff --git a/src/git/repo.rs b/src/git/repo.rs deleted file mode 100644 index 0d04b0d..0000000 --- a/src/git/repo.rs +++ /dev/null @@ -1,19 +0,0 @@ -use crate::templ_data::repo::RepoData; -use git2::{Oid, Repository, Tree}; - -/// Represents a repo in libgit2 -pub(crate) struct Repo { - pub(crate) inner: Repository, -} - -impl Repo { - pub(crate) fn new(path: &str) -> Self { - Self { - inner: Repository::open(path).expect(&format!("`{}` is not a valid git repo", path)), - } - } -} - -pub(crate) fn get_tree<'r>(repo: &'r Repository, rev: &str) -> Tree<'r> { - repo.revparse_single(rev).unwrap().peel_to_tree().unwrap() -} diff --git a/src/git/tree.rs b/src/git/tree.rs index 457eb40..5343a57 100644 --- a/src/git/tree.rs +++ b/src/git/tree.rs @@ -57,6 +57,38 @@ impl Tree { vec }) } + + /// Get all the commits that touch a file + pub(crate) fn grab_path_history(&self, mut path: String) -> String { + let mut path: Vec = 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)] diff --git a/src/main.rs b/src/main.rs index 7e589ec..a0d6dce 100644 --- a/src/main.rs +++ b/src/main.rs @@ -14,12 +14,6 @@ async fn main() -> io::Result<()> { env_logger::init(); let root = PathBuf::new(); - let repo = git::repo::Repo::new("."); - let commits = git::log::create_commit_log( - "84a9a0ccee713e26a28ff5e54ea3776085d93b5f".into(), - &repo.inner, - ); - HttpServer::new(move || { App::new() .service(fs::Files::new("/static", root.join("static"))) -- cgit v1.2.3