@@ -19,6 +19,9 @@ pub struct Repo<'a> {
19
19
git2_repo : & ' a Repository ,
20
20
repo : git:: Repository ,
21
21
commits : Vec < git:: DetachedObject > ,
22
+ num_commits : usize ,
23
+ time_of_most_recent_commit : git:: actor:: Time ,
24
+ time_of_first_commit : git:: actor:: Time ,
22
25
}
23
26
24
27
#[ derive( Hash , PartialEq , Eq ) ]
@@ -51,64 +54,74 @@ impl<'a> Repo<'a> {
51
54
bot_regex_pattern : & Option < Regex > ,
52
55
) -> Result < Self > {
53
56
let mut repo = git:: open ( git2_repo. path ( ) ) ?;
54
- let logs = Self :: get_logs ( & mut repo, no_merges, bot_regex_pattern) ?;
57
+ let ( logs, num_commits, time_of_first_commit, time_of_most_recent_commit) =
58
+ Self :: extract_commit_infos ( & mut repo, no_merges, bot_regex_pattern) ?;
55
59
Ok ( Self {
56
60
git2_repo,
57
61
repo,
58
62
commits : logs,
63
+ num_commits,
64
+ time_of_first_commit,
65
+ time_of_most_recent_commit,
59
66
} )
60
67
}
61
68
62
69
// TODO: avoid allocating/copying buffers. Instead gather the desired information
63
70
// during traversal and keep only author names for processing.
64
- fn get_logs (
71
+ fn extract_commit_infos (
65
72
repo : & mut git:: Repository ,
66
73
no_merges : bool ,
67
74
bot_regex_pattern : & Option < Regex > ,
68
- ) -> Result < Vec < git:: DetachedObject > > {
75
+ ) -> Result < (
76
+ Vec < git:: DetachedObject > ,
77
+ usize ,
78
+ git:: actor:: Time ,
79
+ git:: actor:: Time ,
80
+ ) > {
69
81
// assure that objects we just traversed are coming from cache
70
82
// when we read the commit right after.
71
83
repo. object_cache_size ( 128 * 1024 ) ;
72
- let commits = repo
73
- . head ( ) ?
74
- . peel_to_commit_in_place ( ) ?
75
- . ancestors ( )
76
- . all ( )
77
- . filter_map ( |commit_id| {
78
- let commit: git:: Commit = commit_id
79
- . ok ( ) ?
80
- . object ( )
81
- . expect ( "commit is still present/comes from cache" )
82
- . into_commit ( ) ;
83
- if no_merges && commit. parent_ids ( ) . take ( 2 ) . count ( ) > 1 {
84
- return None ;
85
- }
86
- if is_bot ( commit. iter ( ) . author ( ) , bot_regex_pattern) {
87
- return None ;
88
- }
89
- commit. detach ( ) . into ( )
90
- } )
91
- . collect ( ) ;
92
- Ok ( commits)
93
- }
84
+ let mut commits = Vec :: new ( ) ;
85
+
86
+ let mut most_recent_commit_time = None ;
87
+ let mut first_commit_time = None ;
88
+ let mut ancestors = repo. head ( ) ?. peel_to_commit_in_place ( ) ?. ancestors ( ) ;
89
+ let mut commit_iter = ancestors. all ( ) . peekable ( ) ;
90
+
91
+ while let Some ( commit_id) = commit_iter. next ( ) {
92
+ let commit: git:: Commit = commit_id?
93
+ . object ( )
94
+ . expect ( "commit is still present/comes from cache" )
95
+ . into_commit ( ) ;
96
+ if no_merges && commit. parent_ids ( ) . take ( 2 ) . count ( ) > 1 {
97
+ continue ;
98
+ }
99
+ if is_bot ( commit. iter ( ) . author ( ) , bot_regex_pattern) {
100
+ continue ;
101
+ }
94
102
95
- pub fn get_creation_date ( & self , iso_time : bool ) -> Result < String > {
96
- let first_commit = self . commits . last ( ) ;
97
- let output = match first_commit {
98
- Some ( commit) => {
99
- // TODO: no clone
100
- let commit: git:: Commit = commit. clone ( ) . attach ( & self . repo ) . into_commit ( ) ;
101
- gitoxide_time_to_formatted_time ( commit. time ( ) ?, iso_time)
103
+ most_recent_commit_time. get_or_insert_with ( || commit. time ( ) ) ;
104
+ if commit_iter. peek ( ) . is_none ( ) {
105
+ first_commit_time = commit. time ( ) ?. into ( ) ;
102
106
}
103
- None => "" . into ( ) ,
104
- } ;
107
+ commits. push ( commit. detach ( ) . into ( ) ) ;
108
+ }
109
+
110
+ let num_commits = commits. len ( ) ;
111
+ Ok ( (
112
+ commits,
113
+ num_commits,
114
+ first_commit_time. expect ( "at least one commit" ) ,
115
+ most_recent_commit_time. expect ( "at least one commit" ) ?,
116
+ ) )
117
+ }
105
118
106
- Ok ( output)
119
+ pub fn get_creation_date ( & self , iso_time : bool ) -> String {
120
+ gitoxide_time_to_formatted_time ( self . time_of_first_commit , iso_time)
107
121
}
108
122
109
123
pub fn get_number_of_commits ( & self ) -> String {
110
- let number_of_commits = self . commits . len ( ) ;
111
- number_of_commits. to_string ( )
124
+ self . num_commits . to_string ( )
112
125
}
113
126
114
127
pub fn get_authors (
@@ -158,17 +171,8 @@ impl<'a> Repo<'a> {
158
171
Ok ( ( authors, number_of_authors) )
159
172
}
160
173
161
- pub fn get_date_of_last_commit ( & self , iso_time : bool ) -> Result < String > {
162
- let last_commit = self . commits . first ( ) ;
163
-
164
- Ok ( match last_commit {
165
- Some ( commit) => {
166
- // TODO: no clone
167
- let commit = commit. clone ( ) . attach ( & self . repo ) . into_commit ( ) ;
168
- gitoxide_time_to_formatted_time ( commit. time ( ) ?, iso_time)
169
- }
170
- None => "" . into ( ) ,
171
- } )
174
+ pub fn get_date_of_last_commit ( & self , iso_time : bool ) -> String {
175
+ gitoxide_time_to_formatted_time ( self . time_of_most_recent_commit , iso_time)
172
176
}
173
177
174
178
// This collects the repo size excluding .git
0 commit comments