From b977cd9dfa900324e90ffb496b42ac4b5bb1b799 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Fri, 8 May 2026 19:09:31 +0200 Subject: [PATCH] parallelize git pull fixes #21 --- Cargo.lock | 46 +++++++++++++++++++++++++++ Cargo.toml | 1 + src/git.rs | 92 ++++++++++++++++++++++++++++++++---------------------- 3 files changed, 101 insertions(+), 38 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 22fafea..90f1ad9 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -506,6 +506,31 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + [[package]] name = "crypto-common" version = "0.1.7" @@ -956,6 +981,7 @@ dependencies = [ "opener", "owo-colors", "petname", + "rayon", "reqwest", "serde", "serde_json", @@ -2002,6 +2028,26 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rayon" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fb39b166781f92d482534ef4b4b1b2568f42613b53e5b6c160e24cfbfa30926d" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + [[package]] name = "redox_syscall" version = "0.5.10" diff --git a/Cargo.toml b/Cargo.toml index 9318714..947de7c 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -39,6 +39,7 @@ zip = "8.1.0" sha2 = "0.11.0-rc.5" base16ct = { version = "1.0.0", features = ["alloc"] } indicatif = "0.18.4" +rayon = "1.12.0" hyper-reverse-proxy = { version = "0.5.2-dev", git = "https://github.com/chpio/hyper-reverse-proxy", rev = "6934877eb74465204f605cc1c05ca5a9772db7c0" } hyper = "1.8.1" diff --git a/src/git.rs b/src/git.rs index c20d450..50ebb05 100644 --- a/src/git.rs +++ b/src/git.rs @@ -4,6 +4,8 @@ use git2::build::CheckoutBuilder; use git2::{Branch, BranchType, Repository, RepositoryState}; use indicatif::{MultiProgress, ProgressBar, ProgressStyle}; use miette::{Context, IntoDiagnostic}; +use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; +use rayon::ThreadPoolBuilder; use std::fs::read_dir; use std::iter::once; use std::path::PathBuf; @@ -88,49 +90,63 @@ pub fn pull_all(config: &HazeConfig) -> Result<()> { let progress = MultiProgress::new(); let pull_style = ProgressStyle::with_template("{spinner:.green} {msg}").unwrap(); - for app_dir in find_app_repos(config)? { - let app_name = app_dir.file_name().unwrap().to_string_lossy(); - let repo = Repository::init(&app_dir) - .into_diagnostic() - .wrap_err_with(|| format!("Failed to open repository {}", app_dir.display()))?; - let branch_name = current_branch_name(&repo).unwrap_or("unknown".into()); + let pool = ThreadPoolBuilder::new() + .num_threads(8) + .build() + .into_diagnostic()?; + let repos = find_app_repos(config)?.collect::>(); - let bar = ProgressBar::new_spinner().with_style(pull_style.clone()); - bar.enable_steady_tick(Duration::from_millis(100)); - let bar = progress.add(bar); + pool.install(|| { + repos.par_iter().for_each(|app_dir| { + let app_name = app_dir.file_name().unwrap().to_string_lossy(); + let Ok(repo) = Repository::init(&app_dir) else { + return; + }; + let branch_name = current_branch_name(&repo).unwrap_or("unknown".into()); - let msg = |state: &str| { - format!( - "{app_name: output, + Err(error) => { + bar.set_message(msg(&format!(" ⨯ {error}"))); + return; + } + }; + + if output.status.success() { + bar.set_message(msg(" ✓")); + } else { + let err = String::from_utf8_lossy(&output.stderr); + let err_line = err.lines().next().unwrap(); + bar.set_message(msg(&format!(" ⨯ {err_line}"))); + } bar.finish(); - continue; - } + }); + }); - bar.set_message(msg("")); - - let output = Command::new(GIT_BINARY) - .arg("pull") - .current_dir(&app_dir) - .output() - .into_diagnostic() - .wrap_err_with(|| format!("Failed to run git pull for {}", app_dir.display()))?; - - if output.status.success() { - bar.set_message(msg(" ✓")); - } else { - let err = String::from_utf8_lossy(&output.stderr); - let err_line = err.lines().next().unwrap(); - bar.set_message(msg(&format!(" ❌ {err_line}"))); - } - bar.finish(); - } Ok(()) }