diff --git a/gitoxide-core/src/hours/mod.rs b/gitoxide-core/src/hours/mod.rs index d9713e4ccc9..efbc0b064d2 100644 --- a/gitoxide-core/src/hours/mod.rs +++ b/gitoxide-core/src/hours/mod.rs @@ -18,6 +18,8 @@ pub struct Context { pub file_stats: bool, /// Collect how many lines in files have been added, removed and modified (without rename tracking). pub line_stats: bool, + /// The amount of threads to use. If unset, use all cores, if 0 use al physical cores. + pub threads: Option, /// Omit unifying identities by name and email which can lead to the same author appear multiple times /// due to using different names or email addresses. pub omit_unify_identities: bool, @@ -41,6 +43,7 @@ pub fn estimate( file_stats, line_stats, omit_unify_identities, + threads, mut out, }: Context, ) -> anyhow::Result<()> @@ -52,6 +55,14 @@ where let commit_id = repo.rev_parse_single(rev_spec)?.detach(); let mut string_heap = BTreeSet::<&'static [u8]>::new(); let needs_stats = file_stats || line_stats; + let threads = { + let t = threads.unwrap_or_else(num_cpus::get); + (t == 0) + .then(num_cpus::get_physical) + .unwrap_or(t) + .saturating_sub(1 /*main thread*/) + .max(1) + }; let (commit_authors, stats, is_shallow, skipped_merge_commits) = { let stat_progress = needs_stats.then(|| progress.add_child("extract stats")).map(|mut p| { @@ -125,10 +136,9 @@ where let (tx_tree_id, stat_threads) = needs_stats .then(|| { - let num_threads = num_cpus::get().saturating_sub(1 /*main thread*/).max(1); let (tx, rx) = crossbeam_channel::unbounded::<(u32, Option, git::hash::ObjectId)>(); - let stat_workers = (0..num_threads) + let stat_workers = (0..threads) .map(|_| { scope.spawn({ let commit_counter = stat_counter.clone(); diff --git a/src/porcelain/main.rs b/src/porcelain/main.rs index 710dca6434e..4cccac697a7 100644 --- a/src/porcelain/main.rs +++ b/src/porcelain/main.rs @@ -40,6 +40,7 @@ pub fn main() -> Result<()> { working_dir, rev_spec, no_bots, + threads, file_stats, line_stats, show_pii, @@ -60,6 +61,7 @@ pub fn main() -> Result<()> { hours::Context { show_pii, ignore_bots: no_bots, + threads, file_stats, line_stats, omit_unify_identities, diff --git a/src/porcelain/options.rs b/src/porcelain/options.rs index 5c60f71e5df..08ebddca5d3 100644 --- a/src/porcelain/options.rs +++ b/src/porcelain/options.rs @@ -109,6 +109,9 @@ pub struct EstimateHours { /// Note that this implies the work to be done for file-stats, so it should be set as well. #[clap(short = 'l', long)] pub line_stats: bool, + /// The amount of threads to use. If unset, use all cores, if 0 use al physical cores. + #[clap(short = 't', long)] + pub threads: Option, /// Show personally identifiable information before the summary. Includes names and email addresses. #[clap(short = 'p', long)] pub show_pii: bool,