164 lines
5.5 KiB
Rust
164 lines
5.5 KiB
Rust
#![feature(io_error_more)]
|
|
#![feature(const_option_ext)]
|
|
#![forbid(unsafe_code)]
|
|
|
|
use std::path::Path;
|
|
|
|
pub mod auth;
|
|
pub mod config;
|
|
pub mod output;
|
|
pub mod path;
|
|
pub mod provider;
|
|
pub mod repo;
|
|
pub mod table;
|
|
pub mod tree;
|
|
pub mod worktree;
|
|
|
|
/// Find all git repositories under root, recursively
|
|
///
|
|
/// The bool in the return value specifies whether there is a repository
|
|
/// in root itself.
|
|
#[allow(clippy::type_complexity)]
|
|
fn find_repos(root: &Path) -> Result<Option<(Vec<repo::Repo>, Vec<String>, bool)>, String> {
|
|
let mut repos: Vec<repo::Repo> = Vec::new();
|
|
let mut repo_in_root = false;
|
|
let mut warnings = Vec::new();
|
|
|
|
for path in tree::find_repo_paths(root)? {
|
|
let is_worktree = repo::RepoHandle::detect_worktree(&path);
|
|
if path == root {
|
|
repo_in_root = true;
|
|
}
|
|
|
|
match repo::RepoHandle::open(&path, is_worktree) {
|
|
Err(error) => {
|
|
warnings.push(format!(
|
|
"Error opening repo {}{}: {}",
|
|
path.display(),
|
|
match is_worktree {
|
|
true => " as worktree",
|
|
false => "",
|
|
},
|
|
error
|
|
));
|
|
continue;
|
|
}
|
|
Ok(repo) => {
|
|
let remotes = match repo.remotes() {
|
|
Ok(remote) => remote,
|
|
Err(error) => {
|
|
warnings.push(format!(
|
|
"{}: Error getting remotes: {}",
|
|
&path::path_as_string(&path),
|
|
error
|
|
));
|
|
continue;
|
|
}
|
|
};
|
|
|
|
let mut results: Vec<repo::Remote> = Vec::new();
|
|
for remote_name in remotes.iter() {
|
|
match repo.find_remote(remote_name)? {
|
|
Some(remote) => {
|
|
let name = remote.name();
|
|
let url = remote.url();
|
|
let remote_type = match repo::detect_remote_type(&url) {
|
|
Some(t) => t,
|
|
None => {
|
|
warnings.push(format!(
|
|
"{}: Could not detect remote type of \"{}\"",
|
|
&path::path_as_string(&path),
|
|
&url
|
|
));
|
|
continue;
|
|
}
|
|
};
|
|
|
|
results.push(repo::Remote {
|
|
name,
|
|
url,
|
|
remote_type,
|
|
});
|
|
}
|
|
None => {
|
|
warnings.push(format!(
|
|
"{}: Remote {} not found",
|
|
&path::path_as_string(&path),
|
|
remote_name
|
|
));
|
|
continue;
|
|
}
|
|
};
|
|
}
|
|
let remotes = results;
|
|
|
|
let (namespace, name) = if path == root {
|
|
(
|
|
None,
|
|
match &root.parent() {
|
|
Some(parent) => {
|
|
path::path_as_string(path.strip_prefix(parent).unwrap())
|
|
}
|
|
None => {
|
|
warnings.push(String::from("Getting name of the search root failed. Do you have a git repository in \"/\"?"));
|
|
continue;
|
|
}
|
|
},
|
|
)
|
|
} else {
|
|
let name = path.strip_prefix(&root).unwrap();
|
|
let namespace = name.parent().unwrap();
|
|
(
|
|
if namespace != Path::new("") {
|
|
Some(path::path_as_string(namespace).to_string())
|
|
} else {
|
|
None
|
|
},
|
|
path::path_as_string(name),
|
|
)
|
|
};
|
|
|
|
repos.push(repo::Repo {
|
|
name,
|
|
namespace,
|
|
remotes: Some(remotes),
|
|
worktree_setup: is_worktree,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
Ok(Some((repos, warnings, repo_in_root)))
|
|
}
|
|
|
|
pub fn find_in_tree(path: &Path) -> Result<(tree::Tree, Vec<String>), String> {
|
|
let mut warnings = Vec::new();
|
|
|
|
let (repos, repo_in_root): (Vec<repo::Repo>, bool) = match find_repos(path)? {
|
|
Some((vec, mut repo_warnings, repo_in_root)) => {
|
|
warnings.append(&mut repo_warnings);
|
|
(vec, repo_in_root)
|
|
}
|
|
None => (Vec::new(), false),
|
|
};
|
|
|
|
let mut root = path.to_path_buf();
|
|
if repo_in_root {
|
|
root = match root.parent() {
|
|
Some(root) => root.to_path_buf(),
|
|
None => {
|
|
return Err(String::from(
|
|
"Cannot detect root directory. Are you working in /?",
|
|
));
|
|
}
|
|
}
|
|
}
|
|
|
|
Ok((
|
|
tree::Tree {
|
|
root: root.into_os_string().into_string().unwrap(),
|
|
repos,
|
|
},
|
|
warnings,
|
|
))
|
|
}
|