|
2 | 2 | use super::{CommitId, SharedCommitFilterFn};
|
3 | 3 | use crate::error::Result;
|
4 | 4 | use git2::{Commit, Oid, Repository};
|
| 5 | +use gix::{revision::Walk, traverse::commit::simple::Sorting}; |
5 | 6 | use std::{
|
6 | 7 | cmp::Ordering,
|
7 | 8 | collections::{BinaryHeap, HashSet},
|
@@ -108,6 +109,78 @@ impl<'a> LogWalker<'a> {
|
108 | 109 | }
|
109 | 110 | }
|
110 | 111 |
|
| 112 | +/// This is separate from `LogWalker` because filtering currently (June 2024) works through |
| 113 | +/// `SharedCommitFilterFn`. |
| 114 | +/// |
| 115 | +/// `SharedCommitFilterFn` requires access to a `git2::repo::Repository` because, under the hood, |
| 116 | +/// it calls into functions that work with a `git2::repo::Repository`. It seems unwise to open a |
| 117 | +/// repo both through `gix::open` and `Repository::open_ext` at the same time, so there is a |
| 118 | +/// separate struct that works with `gix::Repository` only. |
| 119 | +/// |
| 120 | +/// A more long-term option is to refactor filtering to work with a `gix::Repository` and to remove |
| 121 | +/// `LogWalker` once this is done, but this is a larger effort. |
| 122 | +pub struct LogWalkerWithoutFilter<'a> { |
| 123 | + walk: Walk<'a>, |
| 124 | + limit: usize, |
| 125 | + visited: usize, |
| 126 | +} |
| 127 | + |
| 128 | +impl<'a> LogWalkerWithoutFilter<'a> { |
| 129 | + /// |
| 130 | + pub fn new( |
| 131 | + repo: &'a mut gix::Repository, |
| 132 | + limit: usize, |
| 133 | + ) -> Result<Self> { |
| 134 | + // This seems to be an object cache size that yields optimal performance. There’s no specific |
| 135 | + // reason this is 2^14, so benchmarking might reveal that there’s better values. |
| 136 | + repo.object_cache_size_if_unset(2_usize.pow(14)); |
| 137 | + |
| 138 | + let commit = repo.head()?.peel_to_commit_in_place()?; |
| 139 | + |
| 140 | + let tips = [commit.id]; |
| 141 | + |
| 142 | + let platform = repo |
| 143 | + .rev_walk(tips) |
| 144 | + .sorting(Sorting::ByCommitTimeNewestFirst) |
| 145 | + .use_commit_graph(false); |
| 146 | + |
| 147 | + let walk = platform.all()?; |
| 148 | + |
| 149 | + Ok(Self { |
| 150 | + walk, |
| 151 | + limit, |
| 152 | + visited: 0, |
| 153 | + }) |
| 154 | + } |
| 155 | + |
| 156 | + /// |
| 157 | + pub const fn visited(&self) -> usize { |
| 158 | + self.visited |
| 159 | + } |
| 160 | + |
| 161 | + /// |
| 162 | + pub fn read(&mut self, out: &mut Vec<CommitId>) -> Result<usize> { |
| 163 | + let mut count = 0_usize; |
| 164 | + |
| 165 | + while let Some(Ok(info)) = self.walk.next() { |
| 166 | + let bytes = info.id.as_bytes(); |
| 167 | + let commit_id: CommitId = Oid::from_bytes(bytes)?.into(); |
| 168 | + |
| 169 | + out.push(commit_id); |
| 170 | + |
| 171 | + count += 1; |
| 172 | + |
| 173 | + if count == self.limit { |
| 174 | + break; |
| 175 | + } |
| 176 | + } |
| 177 | + |
| 178 | + self.visited += count; |
| 179 | + |
| 180 | + Ok(count) |
| 181 | + } |
| 182 | +} |
| 183 | + |
111 | 184 | #[cfg(test)]
|
112 | 185 | mod tests {
|
113 | 186 | use super::*;
|
@@ -183,6 +256,40 @@ mod tests {
|
183 | 256 | Ok(())
|
184 | 257 | }
|
185 | 258 |
|
| 259 | + #[test] |
| 260 | + fn test_logwalker_without_filter() -> Result<()> { |
| 261 | + let file_path = Path::new("foo"); |
| 262 | + let (_td, repo) = repo_init_empty().unwrap(); |
| 263 | + let root = repo.path().parent().unwrap(); |
| 264 | + let repo_path: &RepoPath = |
| 265 | + &root.as_os_str().to_str().unwrap().into(); |
| 266 | + |
| 267 | + File::create(root.join(file_path))?.write_all(b"a")?; |
| 268 | + stage_add_file(repo_path, file_path).unwrap(); |
| 269 | + commit(repo_path, "commit1").unwrap(); |
| 270 | + File::create(root.join(file_path))?.write_all(b"a")?; |
| 271 | + stage_add_file(repo_path, file_path).unwrap(); |
| 272 | + let oid2 = commit(repo_path, "commit2").unwrap(); |
| 273 | + |
| 274 | + let mut repo = gix::open(repo_path.gitpath()).unwrap(); |
| 275 | + let mut walk = LogWalkerWithoutFilter::new(&mut repo, 100)?; |
| 276 | + let mut items = Vec::new(); |
| 277 | + assert!(matches!(walk.read(&mut items), Ok(2))); |
| 278 | + |
| 279 | + let info = get_commits_info(repo_path, &items, 50).unwrap(); |
| 280 | + dbg!(&info); |
| 281 | + |
| 282 | + assert_eq!(items.len(), 2); |
| 283 | + assert_eq!(items[0], oid2); |
| 284 | + |
| 285 | + let mut items = Vec::new(); |
| 286 | + assert!(matches!(walk.read(&mut items), Ok(0))); |
| 287 | + |
| 288 | + assert_eq!(items.len(), 0); |
| 289 | + |
| 290 | + Ok(()) |
| 291 | + } |
| 292 | + |
186 | 293 | #[test]
|
187 | 294 | fn test_logwalker_with_filter() -> Result<()> {
|
188 | 295 | let file_path = Path::new("foo");
|
|
0 commit comments