Skip to content

Commit d13315a

Browse files
committed
feat(repo): get submodule repositories with commit ranges
1 parent 9babe06 commit d13315a

File tree

1 file changed

+58
-1
lines changed

1 file changed

+58
-1
lines changed

git-cliff-core/src/repo.rs

+58-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ use git2::{
1010
DescribeOptions,
1111
Oid,
1212
Repository as GitRepository,
13+
Revwalk,
1314
Sort,
15+
Submodule,
16+
Tree,
1417
TreeWalkMode,
1518
Worktree,
1619
};
@@ -22,7 +25,10 @@ use lazy_regex::{
2225
Regex,
2326
};
2427
use std::io;
25-
use std::path::PathBuf;
28+
use std::path::{
29+
Path,
30+
PathBuf,
31+
};
2632
use std::result::Result as StdResult;
2733
use url::Url;
2834

@@ -149,6 +155,57 @@ impl Repository {
149155
Ok(commits)
150156
}
151157

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+
152209
/// Normalizes the glob pattern to match the git diff paths.
153210
///
154211
/// It removes the leading `./` and adds `**` to the end if the pattern is a

0 commit comments

Comments
 (0)