Skip to content

Commit c943a5d

Browse files
committed
parents on demand
1 parent 6b19516 commit c943a5d

3 files changed

Lines changed: 54 additions & 53 deletions

File tree

asyncgit/src/revlog.rs

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ use crate::{
33
graph::{GraphRow, GraphWalker},
44
sync::{
55
gix_repo, repo, CommitId, LogWalker, LogWalkerWithoutFilter,
6-
RepoPath, SharedCommitFilterFn, WalkEntry,
6+
RepoPath, SharedCommitFilterFn,
77
},
88
AsyncGitNotification, Error,
99
};
@@ -47,9 +47,10 @@ pub struct AsyncLog {
4747
filter: Option<SharedCommitFilterFn>,
4848
partial_extract: AtomicBool,
4949
repo: RepoPath,
50-
/// All walk entries collected by the background thread, in walk order.
51-
/// The graph walker reads these lazily, only as far as the viewport requires.
52-
walk_entries: Arc<Mutex<Vec<WalkEntry>>>,
50+
/// All commit ids collected by the background thread, in walk order.
51+
/// The graph walker reads these lazily, only as far as the viewport
52+
/// requires, looking up each commit's parents on demand.
53+
walk_entries: Arc<Mutex<Vec<CommitId>>>,
5354
graph_walker: Arc<Mutex<GraphWalker>>,
5455
}
5556

@@ -83,8 +84,8 @@ impl AsyncLog {
8384

8485
/// Computes graph rows for `commit_slice` starting at `global_start`.
8586
///
86-
/// Driven lazily. Processes only as many
87-
/// [`WalkEntry`]s as the viewport requires.
87+
/// Driven lazily. Processes only as many walked commits as the
88+
/// viewport requires, resolving their parents on demand.
8889
///
8990
/// Returns `None` when the background walk hasn't reached
9091
/// `global_start + commit_slice.len()` yet.
@@ -111,8 +112,18 @@ impl AsyncLog {
111112
// we know it is yet to have seen
112113
let processed =
113114
walker.processed_commits().min(needed_end);
114-
for entry in &entries[processed..needed_end] {
115-
walker.process(entry.id, &entry.parents);
115+
116+
// The graph only needs topology for the commits it is
117+
// about to fold in, so parents are looked up here on
118+
// demand instead of being carried along the whole walk.
119+
if processed < needed_end {
120+
let mut repo = gix_repo(&self.repo).ok()?;
121+
repo.object_cache_size_if_unset(2_usize.pow(14));
122+
123+
for id in &entries[processed..needed_end] {
124+
let parents = Self::parents_of(&repo, *id).ok()?;
125+
walker.process(*id, &parents);
126+
}
116127
}
117128
}
118129

@@ -125,6 +136,22 @@ impl AsyncLog {
125136
))
126137
}
127138

139+
/// Looks up a commit's (up to two) parents on demand.
140+
///
141+
/// The graph caps support at two parents, ignoring octopus
142+
/// merges, so anything beyond the first two is dropped here.
143+
fn parents_of(
144+
repo: &gix::Repository,
145+
id: CommitId,
146+
) -> Result<Vec<CommitId>> {
147+
Ok(repo
148+
.find_commit(id)?
149+
.parent_ids()
150+
.take(2)
151+
.map(Into::into)
152+
.collect())
153+
}
154+
128155
///
129156
pub fn count(&self) -> Result<usize> {
130157
Ok(self.current.lock()?.commits.len())
@@ -252,7 +279,7 @@ impl AsyncLog {
252279
arc_current: &Arc<Mutex<AsyncLogResult>>,
253280
arc_background: &Arc<AtomicBool>,
254281
sender: &Sender<AsyncGitNotification>,
255-
arc_walk_entries: &Arc<Mutex<Vec<WalkEntry>>>,
282+
arc_walk_entries: &Arc<Mutex<Vec<CommitId>>>,
256283
filter: Option<SharedCommitFilterFn>,
257284
) -> Result<()> {
258285
filter.map_or_else(
@@ -309,7 +336,7 @@ impl AsyncLog {
309336
arc_current: &Arc<Mutex<AsyncLogResult>>,
310337
arc_background: &Arc<AtomicBool>,
311338
sender: &Sender<AsyncGitNotification>,
312-
arc_walk_entries: &Arc<Mutex<Vec<WalkEntry>>>,
339+
arc_walk_entries: &Arc<Mutex<Vec<CommitId>>>,
313340
) -> Result<()> {
314341
let mut repo: gix::Repository = gix_repo(repo_path)?;
315342
let mut walker =
@@ -332,23 +359,23 @@ impl AsyncLog {
332359
/// to `arc_current` and (when given) moving the full entries into
333360
/// `walk_entries` for the graph.
334361
fn walk_loop(
335-
mut read: impl FnMut(&mut Vec<WalkEntry>) -> Result<usize>,
362+
mut read: impl FnMut(&mut Vec<CommitId>) -> Result<usize>,
336363
arc_current: &Arc<Mutex<AsyncLogResult>>,
337364
arc_background: &Arc<AtomicBool>,
338365
sender: &Sender<AsyncGitNotification>,
339-
walk_entries: Option<&Mutex<Vec<WalkEntry>>>,
366+
walk_entries: Option<&Mutex<Vec<CommitId>>>,
340367
) -> Result<()> {
341368
let start_time = Instant::now();
342369

343-
let mut entries: Vec<WalkEntry> =
370+
let mut entries: Vec<CommitId> =
344371
Vec::with_capacity(LIMIT_COUNT);
345372

346373
loop {
347374
let read_count = read(&mut entries)?;
348375

349376
{
350377
let mut current = arc_current.lock()?;
351-
current.commits.extend(entries.iter().map(|e| e.id));
378+
current.commits.extend(entries.iter().copied());
352379
current.duration = start_time.elapsed();
353380
}
354381

asyncgit/src/sync/logwalker.rs

Lines changed: 11 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,11 @@ use super::{CommitId, SharedCommitFilterFn};
22
use crate::error::Result;
33
use git2::{Commit, Oid, Repository};
44
use gix::revision::Walk;
5-
use smallvec::SmallVec;
65
use std::{
76
cmp::Ordering,
87
collections::{BinaryHeap, HashSet},
98
};
109

11-
/// A commit id together with the ids of its TWO parents.
12-
///
13-
/// The parents come for free during a walk.
14-
/// Collecting here avoid a second, convulted pass.
15-
#[derive(Debug, Clone)]
16-
pub struct WalkEntry {
17-
/// The commit's own unique identifier.
18-
pub id: CommitId,
19-
20-
/// The commit's parent identifiers.
21-
pub parents: SmallVec<[CommitId; 2]>,
22-
}
23-
2410
struct TimeOrderedCommit<'a>(Commit<'a>);
2511

2612
impl Eq for TimeOrderedCommit<'_> {}
@@ -86,16 +72,12 @@ impl<'a> LogWalker<'a> {
8672
///
8773
pub fn read(
8874
&mut self,
89-
out: &mut Vec<WalkEntry>,
75+
out: &mut Vec<CommitId>,
9076
) -> Result<usize> {
9177
let mut count = 0_usize;
9278

9379
while let Some(c) = self.commits.pop() {
94-
let mut parents = SmallVec::new();
9580
for p in c.0.parents() {
96-
if parents.len() < 2 {
97-
parents.push(p.id().into());
98-
}
9981
self.visit(p);
10082
}
10183

@@ -108,7 +90,7 @@ impl<'a> LogWalker<'a> {
10890
};
10991

11092
if commit_should_be_included {
111-
out.push(WalkEntry { id, parents });
93+
out.push(id);
11294
}
11395

11496
count += 1;
@@ -180,20 +162,12 @@ impl<'a> LogWalkerWithoutFilter<'a> {
180162
///
181163
pub fn read(
182164
&mut self,
183-
out: &mut Vec<WalkEntry>,
165+
out: &mut Vec<CommitId>,
184166
) -> Result<usize> {
185167
let mut count = 0_usize;
186168

187169
while let Some(Ok(info)) = self.walk.next() {
188-
out.push(WalkEntry {
189-
id: info.id.into(),
190-
parents: info
191-
.parent_ids
192-
.iter()
193-
.take(2)
194-
.map(|id| CommitId::from(*id))
195-
.collect(),
196-
});
170+
out.push(info.id.into());
197171

198172
count += 1;
199173

@@ -246,7 +220,7 @@ mod tests {
246220
walk.read(&mut items).unwrap();
247221

248222
assert_eq!(items.len(), 1);
249-
assert_eq!(items[0].id, oid2);
223+
assert_eq!(items[0], oid2);
250224

251225
Ok(())
252226
}
@@ -270,12 +244,12 @@ mod tests {
270244
let mut walk = LogWalker::new(&repo, 100)?;
271245
walk.read(&mut items).unwrap();
272246

273-
let ids: Vec<CommitId> = items.iter().map(|e| e.id).collect();
247+
let ids: Vec<CommitId> = items.clone();
274248
let info = get_commits_info(repo_path, &ids, 50).unwrap();
275249
dbg!(&info);
276250

277251
assert_eq!(items.len(), 2);
278-
assert_eq!(items[0].id, oid2);
252+
assert_eq!(items[0], oid2);
279253

280254
let mut items = Vec::new();
281255
walk.read(&mut items).unwrap();
@@ -305,12 +279,12 @@ mod tests {
305279
let mut items = Vec::new();
306280
assert!(matches!(walk.read(&mut items), Ok(2)));
307281

308-
let ids: Vec<CommitId> = items.iter().map(|e| e.id).collect();
282+
let ids: Vec<CommitId> = items.clone();
309283
let info = get_commits_info(repo_path, &ids, 50).unwrap();
310284
dbg!(&info);
311285

312286
assert_eq!(items.len(), 2);
313-
assert_eq!(items[0].id, oid2);
287+
assert_eq!(items[0], oid2);
314288

315289
let mut items = Vec::new();
316290
assert!(matches!(walk.read(&mut items), Ok(0)));
@@ -352,7 +326,7 @@ mod tests {
352326
walker.read(&mut items).unwrap();
353327

354328
assert_eq!(items.len(), 1);
355-
assert_eq!(items[0].id, second_commit_id);
329+
assert_eq!(items[0], second_commit_id);
356330

357331
let mut items = Vec::new();
358332
walker.read(&mut items).unwrap();
@@ -399,7 +373,7 @@ mod tests {
399373
walker.read(&mut items).unwrap();
400374

401375
assert_eq!(items.len(), 1);
402-
assert_eq!(items[0].id, second_commit_id);
376+
assert_eq!(items[0], second_commit_id);
403377

404378
let log_filter = filter_commit_by_search(
405379
LogFilterSearch::new(LogFilterSearchOptions {

asyncgit/src/sync/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ pub use hooks::{
7272
};
7373
pub use hunks::{reset_hunk, stage_hunk, unstage_hunk};
7474
pub use ignore::add_to_ignore;
75-
pub use logwalker::{LogWalker, LogWalkerWithoutFilter, WalkEntry};
75+
pub use logwalker::{LogWalker, LogWalkerWithoutFilter};
7676
pub use merge::{
7777
abort_pending_rebase, abort_pending_state,
7878
continue_pending_rebase, merge_branch, merge_commit, merge_msg,
@@ -267,7 +267,7 @@ pub mod tests {
267267
.read(&mut entries)
268268
.unwrap();
269269

270-
entries.iter().map(|e| e.id).collect()
270+
entries
271271
}
272272

273273
/// Same as `repo_init`, but the repo is a bare repo (--bare)

0 commit comments

Comments
 (0)