Skip to content

Commit 0652bbe

Browse files
committed
Calculate authors on the fly as much as possible; don't store commits
1 parent 954de84 commit 0652bbe

File tree

3 files changed

+80
-70
lines changed

3 files changed

+80
-70
lines changed

src/info/author.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ impl Author {
2525
contribution,
2626
}
2727
}
28+
29+
pub fn clear_email(&mut self) {
30+
self.email = None;
31+
}
2832
}
2933

3034
impl std::fmt::Display for Author {

src/info/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,7 +159,7 @@ impl Info {
159159
pub fn new(config: Config) -> Result<Self> {
160160
let git_version = cli::get_git_version();
161161
let repo = Repository::discover(&config.repo_path)?;
162-
let internal_repo = Repo::new(&repo, config.no_merges, &config.bot_regex_pattern)?;
162+
let mut internal_repo = Repo::new(&repo, config.no_merges, &config.bot_regex_pattern)?;
163163
let (repo_name, repo_url) = internal_repo.get_name_and_url()?;
164164
let head_refs = internal_repo.get_head_refs()?;
165165
let pending_changes = internal_repo.get_pending_changes()?;
@@ -170,7 +170,7 @@ impl Info {
170170
let creation_date = internal_repo.get_creation_date(config.iso_time);
171171
let number_of_commits = internal_repo.get_number_of_commits();
172172
let (authors, contributors) =
173-
internal_repo.get_authors(config.number_of_authors, config.show_email)?;
173+
internal_repo.take_authors(config.number_of_authors, config.show_email);
174174
let last_change = internal_repo.get_date_of_last_commit(config.iso_time);
175175
let (repo_size, file_count) = internal_repo.get_repo_size();
176176
let workdir = internal_repo.get_work_dir()?;

src/info/repo.rs

Lines changed: 74 additions & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@ use time_humanize::HumanTime;
1515

1616
pub struct Repo<'a> {
1717
git2_repo: &'a Repository,
18-
repo: git::Repository,
19-
commits: Vec<git::DetachedObject>,
18+
authors: Vec<Author>,
19+
total_num_authors: usize,
2020
num_commits: usize,
2121
time_of_most_recent_commit: git::actor::Time,
2222
time_of_first_commit: git::actor::Time,
@@ -41,12 +41,17 @@ impl<'a> Repo<'a> {
4141
bot_regex_pattern: &Option<Regex>,
4242
) -> Result<Self> {
4343
let mut repo = git::open(git2_repo.path())?;
44-
let (logs, num_commits, time_of_first_commit, time_of_most_recent_commit) =
45-
Self::extract_commit_infos(&mut repo, no_merges, bot_regex_pattern)?;
44+
let (
45+
authors,
46+
total_num_authors,
47+
num_commits,
48+
time_of_first_commit,
49+
time_of_most_recent_commit,
50+
) = Self::extract_commit_infos(&mut repo, no_merges, bot_regex_pattern)?;
4651
Ok(Self {
4752
git2_repo,
48-
repo,
49-
commits: logs,
53+
authors,
54+
total_num_authors,
5055
num_commits,
5156
time_of_first_commit,
5257
time_of_most_recent_commit,
@@ -60,76 +65,53 @@ impl<'a> Repo<'a> {
6065
no_merges: bool,
6166
bot_regex_pattern: &Option<Regex>,
6267
) -> Result<(
63-
Vec<git::DetachedObject>,
68+
Vec<Author>,
69+
usize,
6470
usize,
6571
git::actor::Time,
6672
git::actor::Time,
6773
)> {
6874
// assure that objects we just traversed are coming from cache
6975
// when we read the commit right after.
7076
repo.object_cache_size(128 * 1024);
71-
let mut commits = Vec::new();
7277

7378
let mut most_recent_commit_time = None;
7479
let mut first_commit_time = None;
7580
let mut ancestors = repo.head()?.peel_to_commit_in_place()?.ancestors();
7681
let mut commit_iter = ancestors.all().peekable();
7782

83+
let mailmap = repo.load_mailmap();
84+
let mut author_to_number_of_commits: HashMap<Sig, usize> = HashMap::new();
85+
let mut total_nbr_of_commits = 0;
86+
87+
let mut num_commits = 0;
7888
while let Some(commit_id) = commit_iter.next() {
7989
let commit: git::Commit = commit_id?
8090
.object()
8191
.expect("commit is still present/comes from cache")
8292
.into_commit();
83-
if no_merges && commit.parent_ids().take(2).count() > 1 {
84-
continue;
85-
}
86-
if is_bot(commit.iter().author(), bot_regex_pattern) {
87-
continue;
88-
}
89-
90-
most_recent_commit_time.get_or_insert_with(|| commit.time());
91-
if commit_iter.peek().is_none() {
92-
first_commit_time = commit.time()?.into();
93-
}
94-
commits.push(commit.detach().into());
95-
}
96-
97-
let num_commits = commits.len();
98-
Ok((
99-
commits,
100-
num_commits,
101-
first_commit_time.expect("at least one commit"),
102-
most_recent_commit_time.expect("at least one commit")?,
103-
))
104-
}
93+
num_commits += 1;
94+
{
95+
let commit = commit.decode()?;
96+
if no_merges && commit.parents().take(2).count() > 1 {
97+
continue;
98+
}
10599

106-
pub fn get_creation_date(&self, iso_time: bool) -> String {
107-
gitoxide_time_to_formatted_time(self.time_of_first_commit, iso_time)
108-
}
100+
if is_bot(commit.author, bot_regex_pattern) {
101+
continue;
102+
}
109103

110-
pub fn get_number_of_commits(&self) -> String {
111-
self.num_commits.to_string()
112-
}
104+
let author_nbr_of_commits = author_to_number_of_commits
105+
.entry(Sig::from(mailmap.resolve(commit.author)))
106+
.or_insert(0);
107+
*author_nbr_of_commits += 1;
108+
total_nbr_of_commits += 1;
113109

114-
pub fn get_authors(
115-
&self,
116-
number_of_authors_to_display: usize,
117-
show_email: bool,
118-
) -> Result<(Vec<Author>, usize)> {
119-
let mut author_to_number_of_commits: HashMap<Sig, usize> = HashMap::new();
120-
let mut total_nbr_of_commits = 0;
121-
let mailmap = self.repo.load_mailmap();
122-
for commit in &self.commits {
123-
let mut commit = git::objs::CommitRefIter::from_bytes(&commit.data);
124-
let author = match commit.author() {
125-
Some(author) => mailmap.resolve(&author),
126-
None => continue,
127-
};
128-
let author_nbr_of_commits = author_to_number_of_commits
129-
.entry(Sig::from(author))
130-
.or_insert(0);
131-
*author_nbr_of_commits += 1;
132-
total_nbr_of_commits += 1;
110+
most_recent_commit_time.get_or_insert_with(|| commit.committer.time);
111+
if commit_iter.peek().is_none() {
112+
first_commit_time = commit.committer.time.into();
113+
}
114+
}
133115
}
134116

135117
let mut authors_by_number_of_commits: Vec<(Sig, usize)> =
@@ -139,24 +121,51 @@ impl<'a> Repo<'a> {
139121

140122
authors_by_number_of_commits.sort_by(|(_, a_count), (_, b_count)| b_count.cmp(a_count));
141123

142-
if number_of_authors > number_of_authors_to_display {
143-
authors_by_number_of_commits.truncate(number_of_authors_to_display);
144-
}
145-
146124
let authors: Vec<Author> = authors_by_number_of_commits
147125
.into_iter()
148126
.map(|(author, author_nbr_of_commits)| {
149127
let email = author.email;
150128
Author::new(
151129
author.name,
152-
show_email.then(|| email),
130+
email.into(),
153131
author_nbr_of_commits,
154132
total_nbr_of_commits,
155133
)
156134
})
157135
.collect();
158136

159-
Ok((authors, number_of_authors))
137+
Ok((
138+
authors,
139+
number_of_authors,
140+
num_commits,
141+
first_commit_time.expect("at least one commit"),
142+
most_recent_commit_time.expect("at least one commit"),
143+
))
144+
}
145+
146+
pub fn get_creation_date(&self, iso_time: bool) -> String {
147+
gitoxide_time_to_formatted_time(self.time_of_first_commit, iso_time)
148+
}
149+
150+
pub fn get_number_of_commits(&self) -> String {
151+
self.num_commits.to_string()
152+
}
153+
154+
pub fn take_authors(
155+
&mut self,
156+
number_of_authors_to_display: usize,
157+
show_email: bool,
158+
) -> (Vec<Author>, usize) {
159+
if self.total_num_authors > number_of_authors_to_display {
160+
self.authors.truncate(number_of_authors_to_display);
161+
}
162+
163+
if !show_email {
164+
for author in &mut self.authors {
165+
author.clear_email();
166+
}
167+
}
168+
(std::mem::take(&mut self.authors), self.total_num_authors)
160169
}
161170

162171
pub fn get_date_of_last_commit(&self, iso_time: bool) -> String {
@@ -351,13 +360,10 @@ impl<'a> Repo<'a> {
351360
}
352361
}
353362

354-
fn is_bot(author: Option<git::actor::SignatureRef<'_>>, bot_regex_pattern: &Option<Regex>) -> bool {
355-
author
356-
.and_then(|author| {
357-
bot_regex_pattern
358-
.as_ref()
359-
.map(|regex| regex.is_match(author.name.to_str_lossy().as_ref()))
360-
})
363+
fn is_bot(author: git::actor::SignatureRef<'_>, bot_regex_pattern: &Option<Regex>) -> bool {
364+
bot_regex_pattern
365+
.as_ref()
366+
.map(|regex| regex.is_match(author.name.to_str_lossy().as_ref()))
361367
.unwrap_or(false)
362368
}
363369

0 commit comments

Comments
 (0)