| 88 | 88 | | } |
| 89 | 89 | | |
| 90 | 90 | | /// The last commit of each entry of `path` at `git_ref`, and whether every |
| 91 | | − | /// entry was given one. |
| 92 | | − | pub async fn last_commits<R: GitRepo>(repo: &R, git_ref: &str, path: &str) -> Result<(Vec<LastCommit>, bool)> { |
| 91 | + | /// entry was given one. `out_of_time` is asked between batches; once it |
| 92 | + | /// says so the walk stops with what it has, incomplete. |
| 93 | + | pub async fn last_commits<R: GitRepo>( |
| 94 | + | repo: &R, |
| 95 | + | git_ref: &str, |
| 96 | + | path: &str, |
| 97 | + | out_of_time: &dyn Fn() -> bool, |
| 98 | + | ) -> Result<(Vec<LastCommit>, bool)> { |
| 93 | 99 | | let history = repo.log(git_ref, MAX_COMMITS).await?; |
| 94 | 100 | | let Some(head) = history.first() else { |
| 95 | 101 | | return Ok((Vec::new(), true)); |
| ⋯ |
| 105 | 111 | | break; |
| 106 | 112 | | } |
| 107 | 113 | | if index % READ_AHEAD == 0 { |
| 114 | + | if index > 0 && out_of_time() { |
| 115 | + | break; |
| 116 | + | } |
| 108 | 117 | | let ahead = history.iter().skip(index + 1).take(READ_AHEAD).map(|commit| commit.tree_hash.clone()).collect(); |
| 109 | 118 | | trees.prefetch_dirs(ahead, path).await?; |
| 110 | 119 | | } |
| ⋯ |
| 219 | 228 | | |
| 220 | 229 | | #[test] |
| 221 | 230 | | fn each_root_entry_gets_the_newest_commit_that_changed_it() { |
| 222 | | − | let (found, complete) = run(last_commits(&repo(), "main", "")).unwrap(); |
| 231 | + | let (found, complete) = run(last_commits(&repo(), "main", "", &|| false)).unwrap(); |
| 223 | 232 | | assert!(complete); |
| 224 | 233 | | let found = by_name(found); |
| 225 | 234 | | assert_eq!(found["README.md"], "c3"); |
| ⋯ |
| 228 | 237 | | |
| 229 | 238 | | #[test] |
| 230 | 239 | | fn a_subdirectory_is_walked_by_its_own_tree() { |
| 231 | | − | let (found, complete) = run(last_commits(&repo(), "main", "src")).unwrap(); |
| 240 | + | let (found, complete) = run(last_commits(&repo(), "main", "src", &|| false)).unwrap(); |
| 232 | 241 | | assert!(complete); |
| 233 | 242 | | assert_eq!(by_name(found)["a.rs"], "c2"); |
| 234 | 243 | | } |
| ⋯ |
| 238 | 247 | | let mut fake = repo(); |
| 239 | 248 | | fake.trees.insert("root2".into(), vec![entry("README.md", "r1", EntryKind::Blob), entry("src", "src1", EntryKind::Tree)]); |
| 240 | 249 | | fake.trees.insert("root3".into(), vec![entry("README.md", "r2", EntryKind::Blob), entry("src", "src1", EntryKind::Tree)]); |
| 241 | | − | let (found, complete) = run(last_commits(&fake, "main", "")).unwrap(); |
| 250 | + | let (found, complete) = run(last_commits(&fake, "main", "", &|| false)).unwrap(); |
| 242 | 251 | | assert!(complete); |
| 243 | 252 | | assert_eq!(by_name(found)["src"], "c1"); |
| 244 | 253 | | } |
| ⋯ |
| 250 | 259 | | fake.history[2].parents = vec!["c0".into()]; |
| 251 | 260 | | fake.trees.insert("root2".into(), vec![entry("README.md", "r1", EntryKind::Blob), entry("src", "src1", EntryKind::Tree)]); |
| 252 | 261 | | fake.trees.insert("root3".into(), vec![entry("README.md", "r2", EntryKind::Blob), entry("src", "src1", EntryKind::Tree)]); |
| 253 | | − | let (found, complete) = run(last_commits(&fake, "main", "")).unwrap(); |
| 262 | + | let (found, complete) = run(last_commits(&fake, "main", "", &|| false)).unwrap(); |
| 254 | 263 | | assert!(!complete); |
| 255 | 264 | | let found = by_name(found); |
| 256 | 265 | | assert_eq!(found["README.md"], "c3"); |