@@ -10,7 +10,10 @@ use git2::{
10
10
DescribeOptions ,
11
11
Oid ,
12
12
Repository as GitRepository ,
13
+ Revwalk ,
13
14
Sort ,
15
+ Submodule ,
16
+ Tree ,
14
17
TreeWalkMode ,
15
18
Worktree ,
16
19
} ;
@@ -22,7 +25,10 @@ use lazy_regex::{
22
25
Regex ,
23
26
} ;
24
27
use std:: io;
25
- use std:: path:: PathBuf ;
28
+ use std:: path:: {
29
+ Path ,
30
+ PathBuf ,
31
+ } ;
26
32
use std:: result:: Result as StdResult ;
27
33
use url:: Url ;
28
34
@@ -149,6 +155,57 @@ impl Repository {
149
155
Ok ( commits)
150
156
}
151
157
158
+ /// Returns submodule repositories for a given commit range.
159
+ ///
160
+ /// For a two given commits in this repository, a list of changed submodules
161
+ /// is calculated. For each submodule a [`Repository`] object is created
162
+ /// along with commit range string
163
+ /// "first_submodule_commit..last_submodule_commit". This can then be used
164
+ /// with [`Repository::commits`] again.
165
+ pub fn submodules_range (
166
+ & self ,
167
+ first_commit : & Commit ,
168
+ last_commit : & Commit ,
169
+ ) -> Result < Vec < ( Repository , String ) > > {
170
+ let diff = self . inner . diff_tree_to_tree (
171
+ first_commit. tree ( ) . ok ( ) . as_ref ( ) ,
172
+ last_commit. tree ( ) . ok ( ) . as_ref ( ) ,
173
+ None ,
174
+ ) ?;
175
+ // (path, commit_range)
176
+ let before_and_after_deltas = diff. deltas ( ) . filter_map ( |delta| {
177
+ let old_file_id = delta. old_file ( ) . id ( ) ;
178
+ let new_file_id = delta. new_file ( ) . id ( ) ;
179
+ // no changes or element added/removed
180
+ if old_file_id == new_file_id ||
181
+ new_file_id. is_zero ( ) ||
182
+ old_file_id. is_zero ( )
183
+ {
184
+ None
185
+ } else {
186
+ let range = format ! (
187
+ "{}..{}" ,
188
+ old_file_id. to_string( ) ,
189
+ new_file_id. to_string( )
190
+ ) ;
191
+ delta
192
+ . new_file ( )
193
+ . path ( )
194
+ . and_then ( Path :: to_str)
195
+ . zip ( Some ( range) )
196
+ }
197
+ } ) ;
198
+ // (repository, commit_range)
199
+ let submodule_range = before_and_after_deltas. filter_map ( |( path, range) | {
200
+ self . inner
201
+ . find_submodule ( path)
202
+ . ok ( )
203
+ . and_then ( |submodule| Self :: init ( submodule. path ( ) . into ( ) ) . ok ( ) )
204
+ . zip ( Some ( range) )
205
+ } ) ;
206
+ Ok ( submodule_range. collect ( ) )
207
+ }
208
+
152
209
/// Normalizes the glob pattern to match the git diff paths.
153
210
///
154
211
/// It removes the leading `./` and adds `**` to the end if the pattern is a
0 commit comments