Skip to main content

git_semantic/git/
parser.rs

1use git2::Repository;
2use std::path::Path;
3use tracing::debug;
4
5use super::diff::DiffExtractor;
6use super::{CommitInfo, GitError};
7
8pub struct RepositoryParser {
9    repo: Repository,
10}
11
12impl RepositoryParser {
13    pub fn new(path: &Path) -> Result<Self, GitError> {
14        let repo = Repository::discover(path).map_err(GitError::RepositoryNotFound)?;
15
16        Ok(Self { repo })
17    }
18
19    pub fn parse_commits(&self, include_diffs: bool) -> Result<Vec<CommitInfo>, GitError> {
20        let mut revwalk = self.repo.revwalk()?;
21        revwalk.push_head()?;
22        revwalk.set_sorting(git2::Sort::TOPOLOGICAL | git2::Sort::TIME)?;
23
24        let mut commits = Vec::new();
25
26        for oid in revwalk {
27            let oid = oid?;
28            let commit = self.repo.find_commit(oid)?;
29
30            let hash = oid.to_string();
31            let author = commit.author().name().unwrap_or("Unknown").to_string();
32            let date =
33                chrono::DateTime::from_timestamp(commit.time().seconds(), 0).unwrap_or_default();
34            let message = commit.message().unwrap_or("").to_string();
35
36            let diff_summary = if include_diffs {
37                DiffExtractor::extract_diff(&self.repo, &commit)?
38            } else {
39                String::new()
40            };
41
42            debug!("Parsed commit: {} by {} at {}", &hash[..7], author, date);
43
44            commits.push(CommitInfo {
45                hash,
46                author,
47                date,
48                message,
49                diff_summary,
50            });
51        }
52
53        Ok(commits)
54    }
55
56    pub fn parse_commits_since(
57        &self,
58        since_hash: &str,
59        include_diffs: bool,
60    ) -> Result<Vec<CommitInfo>, GitError> {
61        let mut revwalk = self.repo.revwalk()?;
62        revwalk.push_head()?;
63        revwalk.set_sorting(git2::Sort::TOPOLOGICAL | git2::Sort::TIME)?;
64
65        let since_oid = git2::Oid::from_str(since_hash)?;
66        let mut commits = Vec::new();
67        let mut found_since = false;
68
69        for oid in revwalk {
70            let oid = oid?;
71
72            if oid == since_oid {
73                found_since = true;
74                break;
75            }
76
77            let commit = self.repo.find_commit(oid)?;
78
79            let hash = oid.to_string();
80            let author = commit.author().name().unwrap_or("Unknown").to_string();
81            let date =
82                chrono::DateTime::from_timestamp(commit.time().seconds(), 0).unwrap_or_default();
83            let message = commit.message().unwrap_or("").to_string();
84
85            let diff_summary = if include_diffs {
86                DiffExtractor::extract_diff(&self.repo, &commit)?
87            } else {
88                String::new()
89            };
90
91            commits.push(CommitInfo {
92                hash,
93                author,
94                date,
95                message,
96                diff_summary,
97            });
98        }
99
100        if !found_since {
101            return Err(GitError::CommitNotFound(since_hash.to_string()));
102        }
103
104        Ok(commits)
105    }
106}