Skip to content

Commit 5d9efef

Browse files
committed
Make sure topology order is preserved
This is preparation for a log graph implementation. If commits are sorted by commit time, a graph may have to draw a parent before its child. This is confusing to the user and require extra memory for the graph render algorithm.
1 parent 74f3365 commit 5d9efef

1 file changed

Lines changed: 29 additions & 53 deletions

File tree

asyncgit/src/sync/logwalker.rs

Lines changed: 29 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,50 @@
11
use super::{CommitId, SharedCommitFilterFn};
22
use crate::error::Result;
3-
use git2::{Commit, Oid, Repository};
3+
use git2::{Repository, Revwalk, Sort};
44
use gix::revision::Walk;
5-
use std::{
6-
cmp::Ordering,
7-
collections::{BinaryHeap, HashSet},
8-
};
95

10-
struct TimeOrderedCommit<'a>(Commit<'a>);
11-
12-
impl Eq for TimeOrderedCommit<'_> {}
13-
14-
impl PartialEq for TimeOrderedCommit<'_> {
15-
fn eq(&self, other: &Self) -> bool {
16-
self.0.time().eq(&other.0.time())
17-
}
18-
}
19-
20-
impl PartialOrd for TimeOrderedCommit<'_> {
21-
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
22-
Some(self.cmp(other))
23-
}
24-
}
25-
26-
impl Ord for TimeOrderedCommit<'_> {
27-
fn cmp(&self, other: &Self) -> Ordering {
28-
self.0.time().cmp(&other.0.time())
29-
}
30-
}
31-
32-
///
6+
/// Visit commits in topological order, and date order where possible.
7+
/// If a filter is provided, only commits that pass the filter are returned.
338
pub struct LogWalker<'a> {
34-
commits: BinaryHeap<TimeOrderedCommit<'a>>,
35-
visited: HashSet<Oid>,
9+
/// Revision walk engine
10+
walk: Revwalk<'a>,
11+
/// Total number of commits that have been visited
12+
visited_count: usize,
13+
/// Upper limit on buffer size
3614
limit: usize,
15+
/// The source of commits
3716
repo: &'a Repository,
17+
/// Filter on which commits will be returned when reading
3818
filter: Option<SharedCommitFilterFn>,
3919
}
4020

4121
impl<'a> LogWalker<'a> {
42-
///
22+
/// Create a new log walker
23+
/// with an upper limit on number of commits visited in one batch.
4324
pub fn new(repo: &'a Repository, limit: usize) -> Result<Self> {
44-
let c = repo.head()?.peel_to_commit()?;
25+
let head = repo.head()?.peel_to_commit()?;
4526

46-
let mut commits = BinaryHeap::with_capacity(10);
47-
commits.push(TimeOrderedCommit(c));
27+
let mut walk = repo.revwalk()?;
28+
// TOPOLOGICAL + TIME guarantees parents come after children,
29+
// and ties/independent branches are ordered by timestamp (--date-order).
30+
walk.set_sorting(Sort::TOPOLOGICAL | Sort::TIME)?;
31+
walk.push(head.id())?;
4832

4933
Ok(Self {
50-
commits,
34+
walk,
35+
visited_count: 0,
5136
limit,
52-
visited: HashSet::with_capacity(1000),
5337
repo,
5438
filter: None,
5539
})
5640
}
5741

58-
///
59-
pub fn visited(&self) -> usize {
60-
self.visited.len()
42+
/// Number of visited commits
43+
pub const fn visited(&self) -> usize {
44+
self.visited_count
6145
}
6246

63-
///
47+
/// Add a filter to use when reading commits
6448
#[must_use]
6549
pub fn filter(
6650
self,
@@ -69,16 +53,14 @@ impl<'a> LogWalker<'a> {
6953
Self { filter, ..self }
7054
}
7155

72-
///
56+
/// Get a batch of commits
7357
pub fn read(&mut self, out: &mut Vec<CommitId>) -> Result<usize> {
7458
let mut count = 0_usize;
7559

76-
while let Some(c) = self.commits.pop() {
77-
for p in c.0.parents() {
78-
self.visit(p);
79-
}
60+
for oid_result in self.walk.by_ref() {
61+
let oid = oid_result?;
62+
let id: CommitId = oid.into();
8063

81-
let id: CommitId = c.0.id().into();
8264
let commit_should_be_included =
8365
if let Some(ref filter) = self.filter {
8466
filter(self.repo, &id)?
@@ -90,6 +72,7 @@ impl<'a> LogWalker<'a> {
9072
out.push(id);
9173
}
9274

75+
self.visited_count += 1;
9376
count += 1;
9477
if count == self.limit {
9578
break;
@@ -98,13 +81,6 @@ impl<'a> LogWalker<'a> {
9881

9982
Ok(count)
10083
}
101-
102-
//
103-
fn visit(&mut self, c: Commit<'a>) {
104-
if self.visited.insert(c.id()) {
105-
self.commits.push(TimeOrderedCommit(c));
106-
}
107-
}
10884
}
10985

11086
/// This is separate from `LogWalker` because filtering currently (June 2024) works through

0 commit comments

Comments
 (0)