use anyhow::Result;
use git2::Repository;
use std::collections::HashSet;
use std::path::Path;
pub fn get_changed_rust_files(repo_path: &Path, since_revision: &str) -> Result<HashSet<String>> {
let repo = Repository::open(repo_path)
.map_err(|e| anyhow::anyhow!("Failed to open git repository: {}", e))?;
let rev = repo
.revparse_single(since_revision)
.map_err(|e| anyhow::anyhow!("Failed to parse revision '{}': {}", since_revision, e))?;
let parent = rev
.as_commit()
.and_then(|c| c.parent(0).ok())
.ok_or_else(|| anyhow::anyhow!("Commit has no parent"))?;
let parent_tree = parent.tree()?;
let head_tree = repo
.head()
.map_err(|e| anyhow::anyhow!("Failed to get HEAD: {}", e))?
.peel_to_tree()?;
let diff = repo.diff_tree_to_tree(Some(&parent_tree), Some(&head_tree), None)?;
let mut changed_files = HashSet::new();
diff.foreach(
&mut |delta, _progress| {
if let Some(new_file) = delta.new_file().path() {
if new_file.extension().map_or(false, |e| e == "rs") {
if let Some(path_str) = new_file.to_str() {
changed_files.insert(path_str.to_string());
}
}
}
true
},
None,
None,
None,
)?;
Ok(changed_files)
}
pub fn get_changed_functions(
backend: &crate::storage::MirageDb,
repo_path: &Path,
since_revision: &str,
) -> Result<Vec<i64>> {
use sqlitegraph::SnapshotId;
let changed_files = get_changed_rust_files(repo_path, since_revision)?;
let graph_backend = backend.backend();
let snapshot = SnapshotId::current();
let mut function_ids = Vec::new();
let all_entities = graph_backend.entity_ids()?;
for entity_id in all_entities {
if let Ok(entity) = graph_backend.get_node(snapshot, entity_id) {
let is_function = entity.kind == "Symbol"
&& entity
.data
.get("kind")
.and_then(|v| v.as_str())
.map_or(false, |k| k == "Function");
if is_function {
if let Some(entity_file_value) = entity.data.get("file") {
if let Some(entity_file_str) = entity_file_value.as_str() {
for changed_file in &changed_files {
if entity_file_str.contains(changed_file)
|| changed_file.contains(entity_file_str)
{
function_ids.push(entity_id);
break; }
}
}
}
}
}
}
function_ids.sort();
function_ids.dedup();
Ok(function_ids)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_get_changed_rust_files() {
let repo_path = Path::new(".");
if let Ok(changed) = get_changed_rust_files(repo_path, "HEAD~1") {
println!("Changed files: {:?}", changed);
}
}
}