Skip to content

Commit e9c14a8

Browse files
committed
refactor retrieval of commit graph information to make output clear
This is in preparation for creating a multi-threaded version of it.
1 parent 192c259 commit e9c14a8

File tree

4 files changed

+73
-8
lines changed

4 files changed

+73
-8
lines changed

.github/workflows/ci.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ jobs:
5252
- name: Run onefetch
5353
run: cargo run
5454

55+
- name: Run onefetch (with commitgraph)
56+
run: git commit-graph write --no-progress --reachable && cargo run
57+
5558
formatting:
5659
runs-on: ubuntu-latest
5760
steps:

Cargo.lock

Lines changed: 3 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ byte-unit = "4.0.19"
2323
bytecount = "0.6.3"
2424
clap = { version = "4.3.1", features = ["derive"] }
2525
clap_complete = "4.3.1"
26+
crossbeam-channel = "0.5.8"
2627
gix-features-for-configuration-only = { package = "gix-features", version = "0.30.0", features = [
2728
"zlib-ng",
2829
] }

src/info/utils/git.rs

Lines changed: 66 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,14 +45,60 @@ impl CommitMetrics {
4545
let bot_regex_pattern = get_no_bots_regex(&options.info.no_bots)?;
4646
let mut time_of_most_recent_commit = None;
4747
let mut time_of_first_commit = None;
48+
49+
let num_threads = std::thread::available_parallelism()
50+
.map(|p| p.get())
51+
.unwrap_or(1);
52+
let commit_graph = repo.commit_graph().ok();
53+
let can_use_author_threads = num_threads > 1 && commit_graph.is_some();
4854
let commit_iter = repo
4955
.head_commit()?
5056
.id()
5157
.ancestors()
5258
.sorting(Sorting::ByCommitTimeNewestFirst)
59+
.use_commit_graph(can_use_author_threads)
60+
.with_commit_graph(commit_graph)
5361
.all()?;
5462

55-
let mailmap_config = repo.open_mailmap();
63+
let mailmap = repo.open_mailmap();
64+
let author_threads = can_use_author_threads.then(|| {
65+
// we intentionally over-allocate threads a little as the main thread won't be very busy anyway
66+
// traversing commits with the graph available.
67+
// The channel is generously bounded to assure all threads are fed, without consuming excessive memory.
68+
// We have to wait for the threads to finish anyway so some synchronization here will be fine, while tests
69+
// show that this is about as fast as if it was unbounded.
70+
let (tx, rx) = crossbeam_channel::bounded(num_threads * 100);
71+
let threads: Vec<_> = (0..num_threads)
72+
.map(|_| {
73+
std::thread::spawn({
74+
let mut repo = repo.clone();
75+
let mailmap = mailmap.clone();
76+
let bot_regex_pattern = bot_regex_pattern.clone();
77+
let rx = rx.clone();
78+
move || -> anyhow::Result<_> {
79+
let mut number_of_commits_by_signature: HashMap<Sig, usize> =
80+
HashMap::new();
81+
// We are sure to see each object only once.
82+
repo.object_cache_size(0);
83+
for commit_id in rx {
84+
if let Some(commit) =
85+
repo.try_find_object(commit_id)?.map(|c| c.into_commit())
86+
{
87+
let sig = mailmap.resolve(commit.author()?);
88+
if !is_bot(&sig.name, &bot_regex_pattern) {
89+
*number_of_commits_by_signature
90+
.entry(sig.into())
91+
.or_insert(0) += 1;
92+
}
93+
}
94+
}
95+
Ok(number_of_commits_by_signature)
96+
}
97+
})
98+
})
99+
.collect();
100+
(threads, tx)
101+
});
56102
let mut number_of_commits_by_signature: HashMap<Sig, usize> = HashMap::new();
57103
let (sender, receiver) = std::sync::mpsc::channel::<gix::hash::ObjectId>();
58104
let has_graph_commit_traversal_ended = Arc::new(AtomicBool::default());
@@ -93,11 +139,15 @@ impl CommitMetrics {
93139
continue;
94140
}
95141

96-
let sig = mailmap_config.resolve(commit.object()?.author()?);
97-
if !is_bot(&sig.name, &bot_regex_pattern) {
98-
*number_of_commits_by_signature
99-
.entry(sig.into())
100-
.or_insert(0) += 1;
142+
if let Some((_threads, send_commit)) = author_threads.as_ref() {
143+
send_commit.send(commit.id)?;
144+
} else {
145+
let sig = mailmap.resolve(commit.object()?.author()?);
146+
if !is_bot(&sig.name, &bot_regex_pattern) {
147+
*number_of_commits_by_signature
148+
.entry(sig.into())
149+
.or_insert(0) += 1;
150+
}
101151
}
102152
let commit_time = gix::actor::Time::new(
103153
commit
@@ -118,6 +168,16 @@ impl CommitMetrics {
118168
has_graph_commit_traversal_ended.store(true, Ordering::SeqCst);
119169
total_number_of_commits.store(count, Ordering::SeqCst);
120170

171+
if let Some((threads, sender)) = author_threads {
172+
drop(sender);
173+
for thread in threads {
174+
let mapping = thread.join().expect("no panic")?;
175+
for (sig, num_commits) in mapping {
176+
*number_of_commits_by_signature.entry(sig).or_insert(0) += num_commits;
177+
}
178+
}
179+
}
180+
121181
let (authors_to_display, total_number_of_authors) = compute_authors(
122182
number_of_commits_by_signature,
123183
count,

0 commit comments

Comments
 (0)