use crate::graph::RelationGraph;
use git2::{Commit, Repository};
use regex::Regex;
pub fn walk(conf: Config) -> RelationGraph {
let repo_path = conf.repo_path;
let repo = Repository::open(repo_path).expect("Failed to open repository");
let head = repo
.head()
.expect("Failed to get HEAD ref")
.peel_to_commit()
.expect("Failed to peel HEAD to commit");
let mut revwalk = repo.revwalk().expect("Failed to create revwalk");
revwalk.push(head.id()).expect("Failed to push commit");
let _ = revwalk.set_sorting(git2::Sort::TIME | git2::Sort::REVERSE);
let mut counter = 0;
let mut graph = RelationGraph::new();
let issue_regex: Regex = Regex::new(&*conf.issue_regex).unwrap();
for id in revwalk {
if let Ok(commit_id) = id {
if let Ok(commit) = repo.find_commit(commit_id) {
let commit_result = process_commit(&repo, &commit, &issue_regex);
for file in &commit_result.files {
graph.add_file_node(file);
}
graph.add_commit_node(&commit_id.to_string());
for file in &commit_result.files {
graph.add_edge_file2commit(file, &commit_id.to_string(), &String::new());
}
for issue in &commit_result.issues {
graph.add_issue_node(issue);
for file in &commit_result.files {
graph.add_edge_file2issue(file, issue, &String::new());
}
}
counter += 1;
if counter > conf.depth {
break;
}
} else {
eprintln!("Failed to find commit {}", commit_id);
}
} else {
eprintln!("Failed to get commit id");
}
}
return graph;
}
fn process_commit(repo: &Repository, commit: &Commit, re: &Regex) -> CommitResult {
if let Some(parent) = commit.parent(0).ok() {
let parent_tree = parent.tree().expect("Failed to get parent tree");
let current_tree = commit.tree().expect("Failed to get commit tree");
let changes = repo
.diff_tree_to_tree(Some(&parent_tree), Some(¤t_tree), None)
.expect("Failed to get diff");
let changed_files: Vec<String> = changes
.deltas()
.filter_map(|delta| {
delta
.new_file()
.path()
.map(|path| path.to_string_lossy().into_owned())
})
.collect();
let issues = re
.find_iter(commit.message().unwrap_or_default())
.map(|mat| mat.as_str().to_string())
.collect();
return CommitResult {
files: changed_files,
issues,
};
}
return CommitResult::default();
}
struct CommitResult {
files: Vec<String>,
issues: Vec<String>,
}
impl CommitResult {
pub fn default() -> CommitResult {
return CommitResult {
files: Vec::new(),
issues: Vec::new(),
};
}
}
pub struct Config {
pub repo_path: String,
pub depth: i32,
pub issue_regex: String,
}
impl Config {
pub fn default() -> Config {
return Config {
repo_path: String::from("."),
depth: 10240,
issue_regex: String::from(r"(#\d+)"),
};
}
}