refactor: move walker to thread
This commit is contained in:
36
Cargo.lock
generated
36
Cargo.lock
generated
@@ -74,8 +74,6 @@ dependencies = [
|
|||||||
"libc",
|
"libc",
|
||||||
"libgit2-sys",
|
"libgit2-sys",
|
||||||
"log",
|
"log",
|
||||||
"openssl-probe",
|
|
||||||
"openssl-sys",
|
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -212,26 +210,10 @@ checksum = "c9b3acc4b91781bb0b3386669d325163746af5f6e4f73e6d2d630e09a35f3487"
|
|||||||
dependencies = [
|
dependencies = [
|
||||||
"cc",
|
"cc",
|
||||||
"libc",
|
"libc",
|
||||||
"libssh2-sys",
|
|
||||||
"libz-sys",
|
"libz-sys",
|
||||||
"openssl-sys",
|
|
||||||
"pkg-config",
|
"pkg-config",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "libssh2-sys"
|
|
||||||
version = "0.3.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "220e4f05ad4a218192533b300327f5150e809b54c4ec83b5a1d91833601811b9"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
"libz-sys",
|
|
||||||
"openssl-sys",
|
|
||||||
"pkg-config",
|
|
||||||
"vcpkg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "libz-sys"
|
name = "libz-sys"
|
||||||
version = "1.1.23"
|
version = "1.1.23"
|
||||||
@@ -256,24 +238,6 @@ version = "0.4.29"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "openssl-probe"
|
|
||||||
version = "0.1.6"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e"
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "openssl-sys"
|
|
||||||
version = "0.9.111"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "82cab2d520aa75e3c58898289429321eb788c3106963d0dc886ec7a5f4adc321"
|
|
||||||
dependencies = [
|
|
||||||
"cc",
|
|
||||||
"libc",
|
|
||||||
"pkg-config",
|
|
||||||
"vcpkg",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "percent-encoding"
|
name = "percent-encoding"
|
||||||
version = "2.3.2"
|
version = "2.3.2"
|
||||||
|
|||||||
@@ -4,4 +4,4 @@ version = "0.1.0"
|
|||||||
edition = "2024"
|
edition = "2024"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
git2 = "0.20.3"
|
git2 = { version = "0.20.3", features = ["vendored-libgit2"], default-features = false }
|
||||||
|
|||||||
126
src/main.rs
126
src/main.rs
@@ -1,8 +1,19 @@
|
|||||||
use std::{env, io, path::{Path, PathBuf}};
|
use std::{
|
||||||
|
env,
|
||||||
|
fs::File,
|
||||||
|
io::{self, Read},
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
sync::mpsc,
|
||||||
|
thread,
|
||||||
|
};
|
||||||
|
|
||||||
use git2::Repository;
|
use git2::Repository;
|
||||||
|
|
||||||
macro_rules! continue_if_error {
|
const GIT_DIR: &str = ".git";
|
||||||
|
const CACHE_DIR_TAG: &str = "CACHEDIR.TAG";
|
||||||
|
const CACHE_DIR_TAG_HEADER: &[u8] = b"Signature: 8a477f597d28d172789f06886806bc55";
|
||||||
|
|
||||||
|
macro_rules! ok_or_continue {
|
||||||
($e:expr) => {
|
($e:expr) => {
|
||||||
match $e {
|
match $e {
|
||||||
Ok(val) => val,
|
Ok(val) => val,
|
||||||
@@ -11,7 +22,37 @@ macro_rules! continue_if_error {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! ok_or_bail {
|
||||||
|
($e:expr) => {
|
||||||
|
match $e {
|
||||||
|
Ok(val) => val,
|
||||||
|
Err(e) => {
|
||||||
|
eprintln!("error: {e}");
|
||||||
|
std::process::exit(1);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! io_error {
|
||||||
|
($kind:ident) => {
|
||||||
|
Err(std::io::Error::new(
|
||||||
|
std::io::ErrorKind::$kind,
|
||||||
|
format!("{}", std::io::ErrorKind::$kind),
|
||||||
|
))
|
||||||
|
};
|
||||||
|
($kind:ident, $prefix:expr) => {
|
||||||
|
Err(io::Error::new(
|
||||||
|
std::io::ErrorKind::$kind,
|
||||||
|
format!("{}: {}", $prefix, std::io::ErrorKind::$kind),
|
||||||
|
))
|
||||||
|
};
|
||||||
|
($kind:expr, $prefix:expr) => {
|
||||||
|
Err(io::Error::new($kind, format!("{}: {}", $prefix, $kind)))
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
@@ -20,30 +61,55 @@ fn main() {
|
|||||||
None => env::current_dir(),
|
None => env::current_dir(),
|
||||||
};
|
};
|
||||||
|
|
||||||
for repo in find_git_repo(&dir.unwrap()).unwrap() {
|
let dir = ok_or_bail!(dir);
|
||||||
println!("{}", repo.path().parent().unwrap().display());
|
|
||||||
let worktrees = continue_if_error!(repo.worktrees());
|
let walker = Walker {
|
||||||
|
ignore_hidden: true,
|
||||||
|
ignore_caches: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
let (tx, rx) = mpsc::channel();
|
||||||
|
let join_handle = thread::spawn(move || walker.walk(&dir, tx));
|
||||||
|
|
||||||
|
for repo in rx {
|
||||||
|
let repo_path = repo.path().parent().unwrap().display();
|
||||||
|
|
||||||
|
println!("{repo_path}");
|
||||||
|
|
||||||
|
let worktrees = ok_or_continue!(repo.worktrees());
|
||||||
for worktree in worktrees.iter() {
|
for worktree in worktrees.iter() {
|
||||||
let worktree = repo.find_worktree(worktree.unwrap()).unwrap();
|
let worktree = repo.find_worktree(worktree.unwrap()).unwrap();
|
||||||
println!("{}", worktree.path().display());
|
let worktree_path = worktree.path().display();
|
||||||
|
println!("{worktree_path}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ok_or_bail!(join_handle.join().expect("thead paniced"));
|
||||||
}
|
}
|
||||||
|
|
||||||
fn find_git_repo(path: &Path) -> io::Result<Vec<Repository>> {
|
struct Walker {
|
||||||
|
ignore_hidden: bool,
|
||||||
|
ignore_caches: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Walker {
|
||||||
|
fn walk(&self, path: &Path, tx: mpsc::Sender<Repository>) -> io::Result<()> {
|
||||||
if !path.exists() {
|
if !path.exists() {
|
||||||
todo!();
|
return io_error!(NotFound, path.display());
|
||||||
}
|
}
|
||||||
|
|
||||||
if path.is_file() {
|
if path.is_file() {
|
||||||
todo!();
|
return io_error!(NotADirectory, path.display());
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut r = Vec::new();
|
let read_dir = match path.read_dir() {
|
||||||
|
Ok(read_dir) => read_dir,
|
||||||
|
Err(e) => return io_error!(e.kind(), path.display()),
|
||||||
|
};
|
||||||
|
|
||||||
for entry in path.read_dir()? {
|
for entry in read_dir {
|
||||||
let entry = continue_if_error!(entry);
|
let entry = ok_or_continue!(entry);
|
||||||
let file_type = continue_if_error!(entry.file_type());
|
let file_type = ok_or_continue!(entry.file_type());
|
||||||
|
|
||||||
if !file_type.is_dir() {
|
if !file_type.is_dir() {
|
||||||
continue;
|
continue;
|
||||||
@@ -51,25 +117,41 @@ fn find_git_repo(path: &Path) -> io::Result<Vec<Repository>> {
|
|||||||
|
|
||||||
let entry_path = entry.path();
|
let entry_path = entry.path();
|
||||||
|
|
||||||
if entry.file_name().as_encoded_bytes().get(0) == Some(&b'.') {
|
if self.ignore_hidden && entry.file_name().as_encoded_bytes().first() == Some(&b'.') {
|
||||||
|
// Skip "hidden" dot-prefixed files.
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let cache_tag = entry_path.join("CACHEDIR.TAG");
|
if self.ignore_caches && ok_or_continue!(is_cache_dir(&entry_path)) {
|
||||||
if cache_tag.exists() {
|
// Ignore directories that contain a valid CACHEDIR.TAG file.
|
||||||
|
// <https://bford.info/cachedir/>
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let git_dir = entry_path.join(".git");
|
let git_dir = entry_path.join(GIT_DIR);
|
||||||
|
|
||||||
if !git_dir.is_dir() {
|
if !git_dir.is_dir() {
|
||||||
r.extend(continue_if_error!(find_git_repo(&entry_path)));
|
// Descend deeper into directories that are not git repositories.
|
||||||
|
ok_or_continue!(self.walk(&entry_path, tx.clone()));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
let repo = continue_if_error!(Repository::open(git_dir));
|
tx.send(ok_or_continue!(Repository::open(git_dir)))
|
||||||
|
.expect("main thread paniced");
|
||||||
r.push(repo);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(r)
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn is_cache_dir(path: &Path) -> io::Result<bool> {
|
||||||
|
let cache_dir_tag = path.join(CACHE_DIR_TAG);
|
||||||
|
if !cache_dir_tag.is_file() {
|
||||||
|
return Ok(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut buf = [0u8; CACHE_DIR_TAG_HEADER.len()];
|
||||||
|
let _ = File::open(cache_dir_tag)?.read(&mut buf)?;
|
||||||
|
|
||||||
|
Ok(CACHE_DIR_TAG_HEADER == buf)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user