refactor: move walker to thread
This commit is contained in:
36
Cargo.lock
generated
36
Cargo.lock
generated
@@ -74,8 +74,6 @@ dependencies = [
|
||||
"libc",
|
||||
"libgit2-sys",
|
||||
"log",
|
||||
"openssl-probe",
|
||||
"openssl-sys",
|
||||
"url",
|
||||
]
|
||||
|
||||
@@ -212,26 +210,10 @@ checksum = "c9b3acc4b91781bb0b3386669d325163746af5f6e4f73e6d2d630e09a35f3487"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
"libssh2-sys",
|
||||
"libz-sys",
|
||||
"openssl-sys",
|
||||
"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]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.23"
|
||||
@@ -256,24 +238,6 @@ version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
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]]
|
||||
name = "percent-encoding"
|
||||
version = "2.3.2"
|
||||
|
||||
@@ -4,4 +4,4 @@ version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
git2 = "0.20.3"
|
||||
git2 = { version = "0.20.3", features = ["vendored-libgit2"], default-features = false }
|
||||
|
||||
128
src/main.rs
128
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;
|
||||
|
||||
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) => {
|
||||
match $e {
|
||||
Ok(val) => val,
|
||||
@@ -11,7 +22,37 @@ macro_rules! continue_if_error {
|
||||
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() {
|
||||
@@ -20,30 +61,55 @@ fn main() {
|
||||
None => env::current_dir(),
|
||||
};
|
||||
|
||||
for repo in find_git_repo(&dir.unwrap()).unwrap() {
|
||||
println!("{}", repo.path().parent().unwrap().display());
|
||||
let worktrees = continue_if_error!(repo.worktrees());
|
||||
let dir = ok_or_bail!(dir);
|
||||
|
||||
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() {
|
||||
let worktree = repo.find_worktree(worktree.unwrap()).unwrap();
|
||||
println!("{}", worktree.path().display());
|
||||
}
|
||||
let worktree_path = worktree.path().display();
|
||||
println!("{worktree_path}");
|
||||
}
|
||||
}
|
||||
|
||||
fn find_git_repo(path: &Path) -> io::Result<Vec<Repository>> {
|
||||
ok_or_bail!(join_handle.join().expect("thead paniced"));
|
||||
}
|
||||
|
||||
struct Walker {
|
||||
ignore_hidden: bool,
|
||||
ignore_caches: bool,
|
||||
}
|
||||
|
||||
impl Walker {
|
||||
fn walk(&self, path: &Path, tx: mpsc::Sender<Repository>) -> io::Result<()> {
|
||||
if !path.exists() {
|
||||
todo!();
|
||||
return io_error!(NotFound, path.display());
|
||||
}
|
||||
|
||||
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()? {
|
||||
let entry = continue_if_error!(entry);
|
||||
let file_type = continue_if_error!(entry.file_type());
|
||||
for entry in read_dir {
|
||||
let entry = ok_or_continue!(entry);
|
||||
let file_type = ok_or_continue!(entry.file_type());
|
||||
|
||||
if !file_type.is_dir() {
|
||||
continue;
|
||||
@@ -51,25 +117,41 @@ fn find_git_repo(path: &Path) -> io::Result<Vec<Repository>> {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
let cache_tag = entry_path.join("CACHEDIR.TAG");
|
||||
if cache_tag.exists() {
|
||||
if self.ignore_caches && ok_or_continue!(is_cache_dir(&entry_path)) {
|
||||
// Ignore directories that contain a valid CACHEDIR.TAG file.
|
||||
// <https://bford.info/cachedir/>
|
||||
continue;
|
||||
}
|
||||
|
||||
let git_dir = entry_path.join(".git");
|
||||
let git_dir = entry_path.join(GIT_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;
|
||||
}
|
||||
|
||||
let repo = continue_if_error!(Repository::open(git_dir));
|
||||
|
||||
r.push(repo);
|
||||
tx.send(ok_or_continue!(Repository::open(git_dir)))
|
||||
.expect("main thread paniced");
|
||||
}
|
||||
|
||||
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