Separate config structs from internal structs
This commit is contained in:
145
src/config.rs
145
src/config.rs
@@ -3,27 +3,32 @@ use std::process;
|
||||
|
||||
use crate::output::*;
|
||||
|
||||
use super::repo::RepoConfig;
|
||||
use std::path::Path;
|
||||
|
||||
use crate::get_token_from_command;
|
||||
use crate::{get_token_from_command, Remote, Repo, Tree};
|
||||
|
||||
use crate::provider;
|
||||
use crate::provider::Filter;
|
||||
use crate::provider::Provider;
|
||||
|
||||
pub type RemoteProvider = crate::provider::RemoteProvider;
|
||||
pub type RemoteType = crate::repo::RemoteType;
|
||||
|
||||
fn worktree_setup_default() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub enum Config {
|
||||
ConfigTree(ConfigTree),
|
||||
ConfigTrees(ConfigTrees),
|
||||
ConfigProvider(ConfigProvider),
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct ConfigTree {
|
||||
pub trees: Trees,
|
||||
pub struct ConfigTrees {
|
||||
pub trees: Vec<ConfigTree>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
@@ -50,30 +55,100 @@ pub struct ConfigProvider {
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
pub struct Trees(Vec<Tree>);
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RemoteConfig {
|
||||
pub name: String,
|
||||
pub url: String,
|
||||
#[serde(rename = "type")]
|
||||
pub remote_type: RemoteType,
|
||||
}
|
||||
|
||||
impl Trees {
|
||||
impl RemoteConfig {
|
||||
pub fn from_remote(remote: Remote) -> Self {
|
||||
Self {
|
||||
name: remote.name,
|
||||
url: remote.url,
|
||||
remote_type: remote.remote_type,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_remote(self) -> Remote {
|
||||
Remote {
|
||||
name: self.name,
|
||||
url: self.url,
|
||||
remote_type: self.remote_type,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RepoConfig {
|
||||
pub name: String,
|
||||
|
||||
#[serde(default = "worktree_setup_default")]
|
||||
pub worktree_setup: bool,
|
||||
|
||||
pub remotes: Option<Vec<RemoteConfig>>,
|
||||
}
|
||||
|
||||
impl RepoConfig {
|
||||
pub fn from_repo(repo: Repo) -> Self {
|
||||
Self {
|
||||
name: repo.name,
|
||||
worktree_setup: repo.worktree_setup,
|
||||
remotes: repo
|
||||
.remotes
|
||||
.map(|remotes| remotes.into_iter().map(RemoteConfig::from_remote).collect()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn into_repo(self) -> Repo {
|
||||
Repo {
|
||||
name: self.name,
|
||||
worktree_setup: self.worktree_setup,
|
||||
remotes: self.remotes.map(|remotes| {
|
||||
remotes
|
||||
.into_iter()
|
||||
.map(|remote| remote.into_remote())
|
||||
.collect()
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ConfigTrees {
|
||||
pub fn to_config(self) -> Config {
|
||||
Config::ConfigTree(ConfigTree { trees: self })
|
||||
Config::ConfigTrees(self)
|
||||
}
|
||||
|
||||
pub fn from_vec(vec: Vec<Tree>) -> Self {
|
||||
Trees(vec)
|
||||
pub fn from_vec(vec: Vec<ConfigTree>) -> Self {
|
||||
ConfigTrees { trees: vec }
|
||||
}
|
||||
|
||||
pub fn as_vec(self) -> Vec<Tree> {
|
||||
self.0
|
||||
pub fn from_trees(vec: Vec<Tree>) -> Self {
|
||||
ConfigTrees {
|
||||
trees: vec.into_iter().map(ConfigTree::from_tree).collect(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn as_vec_ref(&self) -> &Vec<Tree> {
|
||||
self.0.as_ref()
|
||||
pub fn trees(self) -> Vec<ConfigTree> {
|
||||
self.trees
|
||||
}
|
||||
|
||||
pub fn trees_mut(&mut self) -> &mut Vec<ConfigTree> {
|
||||
&mut self.trees
|
||||
}
|
||||
|
||||
pub fn trees_ref(&self) -> &Vec<ConfigTree> {
|
||||
self.trees.as_ref()
|
||||
}
|
||||
}
|
||||
|
||||
impl Config {
|
||||
pub fn trees(self) -> Result<Trees, String> {
|
||||
pub fn trees(self) -> Result<Vec<ConfigTree>, String> {
|
||||
match self {
|
||||
Config::ConfigTree(config) => Ok(config.trees),
|
||||
Config::ConfigTrees(config) => Ok(config.trees),
|
||||
Config::ConfigProvider(config) => {
|
||||
let token = match get_token_from_command(&config.token_command) {
|
||||
Ok(token) => token,
|
||||
@@ -129,27 +204,29 @@ impl Config {
|
||||
let mut trees = vec![];
|
||||
|
||||
for (namespace, namespace_repos) in repos {
|
||||
let tree = Tree {
|
||||
let repos = namespace_repos
|
||||
.into_iter()
|
||||
.map(RepoConfig::from_repo)
|
||||
.collect();
|
||||
let tree = ConfigTree {
|
||||
root: crate::path_as_string(&Path::new(&config.root).join(namespace)),
|
||||
repos: Some(namespace_repos),
|
||||
repos: Some(repos),
|
||||
};
|
||||
trees.push(tree);
|
||||
}
|
||||
Ok(Trees(trees))
|
||||
Ok(trees)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_trees(trees: Vec<Tree>) -> Self {
|
||||
Config::ConfigTree(ConfigTree {
|
||||
trees: Trees::from_vec(trees),
|
||||
})
|
||||
pub fn from_trees(trees: Vec<ConfigTree>) -> Self {
|
||||
Config::ConfigTrees(ConfigTrees { trees })
|
||||
}
|
||||
|
||||
pub fn normalize(&mut self) {
|
||||
if let Config::ConfigTree(config) = self {
|
||||
if let Config::ConfigTrees(config) = self {
|
||||
let home = super::env_home().display().to_string();
|
||||
for tree in &mut config.trees.0 {
|
||||
for tree in &mut config.trees_mut().iter_mut() {
|
||||
if tree.root.starts_with(&home) {
|
||||
// The tilde is not handled differently, it's just a normal path component for `Path`.
|
||||
// Therefore we can treat it like that during **output**.
|
||||
@@ -181,11 +258,27 @@ impl Config {
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct Tree {
|
||||
pub struct ConfigTree {
|
||||
pub root: String,
|
||||
pub repos: Option<Vec<RepoConfig>>,
|
||||
}
|
||||
|
||||
impl ConfigTree {
|
||||
pub fn from_repos(root: String, repos: Vec<Repo>) -> Self {
|
||||
Self {
|
||||
root,
|
||||
repos: Some(repos.into_iter().map(RepoConfig::from_repo).collect()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_tree(tree: Tree) -> Self {
|
||||
Self {
|
||||
root: tree.root,
|
||||
repos: Some(tree.repos.into_iter().map(RepoConfig::from_repo).collect()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_config<'a, T>(path: &str) -> Result<T, String>
|
||||
where
|
||||
T: for<'de> serde::Deserialize<'de>,
|
||||
|
||||
@@ -5,6 +5,7 @@ mod cmd;
|
||||
|
||||
use grm::config;
|
||||
use grm::output::*;
|
||||
use grm::path_as_string;
|
||||
use grm::provider;
|
||||
use grm::provider::Provider;
|
||||
use grm::repo;
|
||||
@@ -78,16 +79,13 @@ fn main() {
|
||||
|
||||
match repos {
|
||||
Ok(repos) => {
|
||||
let mut trees: Vec<config::Tree> = vec![];
|
||||
let mut trees: Vec<config::ConfigTree> = vec![];
|
||||
|
||||
for (namespace, repolist) in repos {
|
||||
let tree = config::Tree {
|
||||
root: Path::new(&args.root)
|
||||
.join(namespace)
|
||||
.display()
|
||||
.to_string(),
|
||||
repos: Some(repolist),
|
||||
};
|
||||
let tree = config::ConfigTree::from_repos(
|
||||
Path::new(&args.root).join(namespace).display().to_string(),
|
||||
repolist,
|
||||
);
|
||||
trees.push(tree);
|
||||
}
|
||||
|
||||
@@ -191,8 +189,8 @@ fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
let trees = grm::config::Trees::from_vec(vec![found_repos]);
|
||||
if trees.as_vec_ref().iter().all(|t| match &t.repos {
|
||||
let trees = grm::config::ConfigTrees::from_trees(vec![found_repos]);
|
||||
if trees.trees_ref().iter().all(|t| match &t.repos {
|
||||
None => false,
|
||||
Some(r) => r.is_empty(),
|
||||
}) {
|
||||
@@ -311,9 +309,14 @@ fn main() {
|
||||
let mut trees = vec![];
|
||||
|
||||
for (namespace, namespace_repos) in repos {
|
||||
let tree = config::Tree {
|
||||
root: grm::path_as_string(&Path::new(&config.root).join(namespace)),
|
||||
repos: Some(namespace_repos),
|
||||
let tree = config::ConfigTree {
|
||||
root: path_as_string(&Path::new(&config.root).join(namespace)),
|
||||
repos: Some(
|
||||
namespace_repos
|
||||
.into_iter()
|
||||
.map(grm::config::RepoConfig::from_repo)
|
||||
.collect(),
|
||||
),
|
||||
};
|
||||
trees.push(tree);
|
||||
}
|
||||
@@ -395,12 +398,17 @@ fn main() {
|
||||
process::exit(1);
|
||||
});
|
||||
|
||||
let mut trees: Vec<config::Tree> = vec![];
|
||||
let mut trees: Vec<config::ConfigTree> = vec![];
|
||||
|
||||
for (namespace, repolist) in repos {
|
||||
let tree = config::Tree {
|
||||
let tree = config::ConfigTree {
|
||||
root: Path::new(&args.root).join(namespace).display().to_string(),
|
||||
repos: Some(repolist),
|
||||
repos: Some(
|
||||
repolist
|
||||
.into_iter()
|
||||
.map(grm::config::RepoConfig::from_repo)
|
||||
.collect(),
|
||||
),
|
||||
};
|
||||
trees.push(tree);
|
||||
}
|
||||
@@ -506,7 +514,7 @@ fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
let repo = grm::Repo::open(&cwd, true).unwrap_or_else(|error| {
|
||||
let repo = grm::RepoHandle::open(&cwd, true).unwrap_or_else(|error| {
|
||||
print_error(&format!("Error opening repository: {}", error));
|
||||
process::exit(1);
|
||||
});
|
||||
@@ -539,7 +547,7 @@ fn main() {
|
||||
}
|
||||
}
|
||||
cmd::WorktreeAction::Status(_args) => {
|
||||
let repo = grm::Repo::open(&cwd, true).unwrap_or_else(|error| {
|
||||
let repo = grm::RepoHandle::open(&cwd, true).unwrap_or_else(|error| {
|
||||
print_error(&format!("Error opening repository: {}", error));
|
||||
process::exit(1);
|
||||
});
|
||||
@@ -564,7 +572,7 @@ fn main() {
|
||||
// * Remove all files
|
||||
// * Set `core.bare` to `true`
|
||||
|
||||
let repo = grm::Repo::open(&cwd, false).unwrap_or_else(|error| {
|
||||
let repo = grm::RepoHandle::open(&cwd, false).unwrap_or_else(|error| {
|
||||
if error.kind == grm::RepoErrorKind::NotFound {
|
||||
print_error("Directory does not contain a git repository");
|
||||
} else {
|
||||
@@ -592,7 +600,7 @@ fn main() {
|
||||
}
|
||||
}
|
||||
cmd::WorktreeAction::Clean(_args) => {
|
||||
let repo = grm::Repo::open(&cwd, true).unwrap_or_else(|error| {
|
||||
let repo = grm::RepoHandle::open(&cwd, true).unwrap_or_else(|error| {
|
||||
if error.kind == grm::RepoErrorKind::NotFound {
|
||||
print_error("Directory does not contain a git repository");
|
||||
} else {
|
||||
@@ -626,7 +634,7 @@ fn main() {
|
||||
}
|
||||
}
|
||||
cmd::WorktreeAction::Fetch(_args) => {
|
||||
let repo = grm::Repo::open(&cwd, true).unwrap_or_else(|error| {
|
||||
let repo = grm::RepoHandle::open(&cwd, true).unwrap_or_else(|error| {
|
||||
if error.kind == grm::RepoErrorKind::NotFound {
|
||||
print_error("Directory does not contain a git repository");
|
||||
} else {
|
||||
@@ -642,7 +650,7 @@ fn main() {
|
||||
print_success("Fetched from all remotes");
|
||||
}
|
||||
cmd::WorktreeAction::Pull(args) => {
|
||||
let repo = grm::Repo::open(&cwd, true).unwrap_or_else(|error| {
|
||||
let repo = grm::RepoHandle::open(&cwd, true).unwrap_or_else(|error| {
|
||||
if error.kind == grm::RepoErrorKind::NotFound {
|
||||
print_error("Directory does not contain a git repository");
|
||||
} else {
|
||||
@@ -683,7 +691,7 @@ fn main() {
|
||||
print_error("There is no point in using --rebase without --pull");
|
||||
process::exit(1);
|
||||
}
|
||||
let repo = grm::Repo::open(&cwd, true).unwrap_or_else(|error| {
|
||||
let repo = grm::RepoHandle::open(&cwd, true).unwrap_or_else(|error| {
|
||||
if error.kind == grm::RepoErrorKind::NotFound {
|
||||
print_error("Directory does not contain a git repository");
|
||||
} else {
|
||||
|
||||
53
src/lib.rs
53
src/lib.rs
@@ -11,12 +11,14 @@ pub mod provider;
|
||||
pub mod repo;
|
||||
pub mod table;
|
||||
|
||||
use config::{Config, Tree};
|
||||
use config::Config;
|
||||
use output::*;
|
||||
|
||||
use repo::{clone_repo, detect_remote_type, Remote, RemoteType, RepoConfig};
|
||||
use repo::{clone_repo, detect_remote_type, Remote, RemoteType};
|
||||
|
||||
pub use repo::{RemoteTrackingStatus, Repo, RepoErrorKind, WorktreeRemoveFailureReason};
|
||||
pub use repo::{
|
||||
RemoteTrackingStatus, Repo, RepoErrorKind, RepoHandle, WorktreeRemoveFailureReason,
|
||||
};
|
||||
|
||||
const GIT_MAIN_WORKTREE_DIRECTORY: &str = ".git-main-working-tree";
|
||||
const BRANCH_NAMESPACE_SEPARATOR: &str = "/";
|
||||
@@ -24,6 +26,11 @@ const BRANCH_NAMESPACE_SEPARATOR: &str = "/";
|
||||
const GIT_CONFIG_BARE_KEY: &str = "core.bare";
|
||||
const GIT_CONFIG_PUSH_DEFAULT: &str = "push.default";
|
||||
|
||||
pub struct Tree {
|
||||
root: String,
|
||||
repos: Vec<Repo>,
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
@@ -139,7 +146,7 @@ pub fn get_token_from_command(command: &str) -> Result<String, String> {
|
||||
Ok(token.to_string())
|
||||
}
|
||||
|
||||
fn sync_repo(root_path: &Path, repo: &RepoConfig, init_worktree: bool) -> Result<(), String> {
|
||||
fn sync_repo(root_path: &Path, repo: &Repo, init_worktree: bool) -> Result<(), String> {
|
||||
let repo_path = root_path.join(&repo.name);
|
||||
let actual_git_directory = get_actual_git_directory(&repo_path, repo.worktree_setup);
|
||||
|
||||
@@ -156,7 +163,7 @@ fn sync_repo(root_path: &Path, repo: &RepoConfig, init_worktree: bool) -> Result
|
||||
&repo.name,
|
||||
"Repository does not have remotes configured, initializing new",
|
||||
);
|
||||
match Repo::init(&repo_path, repo.worktree_setup) {
|
||||
match RepoHandle::init(&repo_path, repo.worktree_setup) {
|
||||
Ok(r) => {
|
||||
print_repo_success(&repo.name, "Repository created");
|
||||
Some(r)
|
||||
@@ -180,10 +187,10 @@ fn sync_repo(root_path: &Path, repo: &RepoConfig, init_worktree: bool) -> Result
|
||||
newly_created = true;
|
||||
}
|
||||
|
||||
let repo_handle = match Repo::open(&repo_path, repo.worktree_setup) {
|
||||
let repo_handle = match RepoHandle::open(&repo_path, repo.worktree_setup) {
|
||||
Ok(repo) => repo,
|
||||
Err(error) => {
|
||||
if !repo.worktree_setup && Repo::open(&repo_path, true).is_ok() {
|
||||
if !repo.worktree_setup && RepoHandle::open(&repo_path, true).is_ok() {
|
||||
return Err(String::from(
|
||||
"Repo already exists, but is using a worktree setup",
|
||||
));
|
||||
@@ -264,7 +271,7 @@ fn sync_repo(root_path: &Path, repo: &RepoConfig, init_worktree: bool) -> Result
|
||||
|
||||
pub fn find_unmanaged_repos(
|
||||
root_path: &Path,
|
||||
managed_repos: &[RepoConfig],
|
||||
managed_repos: &[Repo],
|
||||
) -> Result<Vec<String>, String> {
|
||||
let mut unmanaged_repos = Vec::new();
|
||||
|
||||
@@ -279,8 +286,16 @@ pub fn find_unmanaged_repos(
|
||||
|
||||
pub fn sync_trees(config: Config, init_worktree: bool) -> Result<bool, String> {
|
||||
let mut failures = false;
|
||||
for tree in config.trees()?.as_vec() {
|
||||
let repos = tree.repos.unwrap_or_default();
|
||||
|
||||
let trees = config.trees()?;
|
||||
|
||||
for tree in trees {
|
||||
let repos: Vec<Repo> = tree
|
||||
.repos
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.map(|repo| repo.into_repo())
|
||||
.collect();
|
||||
|
||||
let root_path = expand_path(Path::new(&tree.root));
|
||||
|
||||
@@ -372,18 +387,18 @@ fn get_actual_git_directory(path: &Path, is_worktree: bool) -> PathBuf {
|
||||
/// 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<RepoConfig>, Vec<String>, bool)>, String> {
|
||||
let mut repos: Vec<RepoConfig> = Vec::new();
|
||||
fn find_repos(root: &Path) -> Result<Option<(Vec<Repo>, Vec<String>, bool)>, String> {
|
||||
let mut repos: Vec<Repo> = Vec::new();
|
||||
let mut repo_in_root = false;
|
||||
let mut warnings = Vec::new();
|
||||
|
||||
for path in find_repo_paths(root)? {
|
||||
let is_worktree = Repo::detect_worktree(&path);
|
||||
let is_worktree = RepoHandle::detect_worktree(&path);
|
||||
if path == root {
|
||||
repo_in_root = true;
|
||||
}
|
||||
|
||||
match Repo::open(&path, is_worktree) {
|
||||
match RepoHandle::open(&path, is_worktree) {
|
||||
Err(error) => {
|
||||
warnings.push(format!(
|
||||
"Error opening repo {}{}: {}",
|
||||
@@ -445,7 +460,7 @@ fn find_repos(root: &Path) -> Result<Option<(Vec<RepoConfig>, Vec<String>, bool)
|
||||
}
|
||||
let remotes = results;
|
||||
|
||||
repos.push(RepoConfig {
|
||||
repos.push(Repo{
|
||||
name: match path == root {
|
||||
true => match &root.parent() {
|
||||
Some(parent) => path_as_string(path.strip_prefix(parent).unwrap()),
|
||||
@@ -468,7 +483,7 @@ fn find_repos(root: &Path) -> Result<Option<(Vec<RepoConfig>, Vec<String>, bool)
|
||||
pub fn find_in_tree(path: &Path) -> Result<(Tree, Vec<String>), String> {
|
||||
let mut warnings = Vec::new();
|
||||
|
||||
let (repos, repo_in_root): (Vec<RepoConfig>, bool) = match find_repos(path)? {
|
||||
let (repos, repo_in_root): (Vec<Repo>, bool) = match find_repos(path)? {
|
||||
Some((vec, mut repo_warnings, repo_in_root)) => {
|
||||
warnings.append(&mut repo_warnings);
|
||||
(vec, repo_in_root)
|
||||
@@ -491,7 +506,7 @@ pub fn find_in_tree(path: &Path) -> Result<(Tree, Vec<String>), String> {
|
||||
Ok((
|
||||
Tree {
|
||||
root: root.into_os_string().into_string().unwrap(),
|
||||
repos: Some(repos),
|
||||
repos,
|
||||
},
|
||||
warnings,
|
||||
))
|
||||
@@ -504,7 +519,7 @@ pub fn add_worktree(
|
||||
track: Option<(&str, &str)>,
|
||||
no_track: bool,
|
||||
) -> Result<(), String> {
|
||||
let repo = Repo::open(directory, true).map_err(|error| match error.kind {
|
||||
let repo = RepoHandle::open(directory, true).map_err(|error| match error.kind {
|
||||
RepoErrorKind::NotFound => {
|
||||
String::from("Current directory does not contain a worktree setup")
|
||||
}
|
||||
@@ -579,7 +594,7 @@ pub fn add_worktree(
|
||||
remote: &mut repo::RemoteHandle,
|
||||
branch_name: &str,
|
||||
remote_branch_name: &str,
|
||||
repo: &repo::Repo,
|
||||
repo: &repo::RepoHandle,
|
||||
) -> Result<(), String> {
|
||||
if !remote.is_pushable()? {
|
||||
return Err(format!(
|
||||
|
||||
@@ -9,7 +9,7 @@ pub mod gitlab;
|
||||
pub use github::Github;
|
||||
pub use gitlab::Gitlab;
|
||||
|
||||
use crate::{Remote, RemoteType, RepoConfig};
|
||||
use crate::{Remote, RemoteType, Repo};
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
@@ -29,16 +29,11 @@ enum ProjectResponse<T, U> {
|
||||
}
|
||||
|
||||
pub trait Project {
|
||||
fn into_repo_config(
|
||||
self,
|
||||
provider_name: &str,
|
||||
worktree_setup: bool,
|
||||
force_ssh: bool,
|
||||
) -> RepoConfig
|
||||
fn into_repo_config(self, provider_name: &str, worktree_setup: bool, force_ssh: bool) -> Repo
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
RepoConfig {
|
||||
Repo {
|
||||
name: self.name(),
|
||||
worktree_setup,
|
||||
remotes: Some(vec![Remote {
|
||||
@@ -205,7 +200,7 @@ pub trait Provider {
|
||||
&self,
|
||||
worktree_setup: bool,
|
||||
force_ssh: bool,
|
||||
) -> Result<HashMap<String, Vec<RepoConfig>>, String> {
|
||||
) -> Result<HashMap<String, Vec<Repo>>, String> {
|
||||
let mut repos = vec![];
|
||||
|
||||
if self.filter().owner {
|
||||
@@ -282,7 +277,7 @@ pub trait Provider {
|
||||
}
|
||||
}
|
||||
|
||||
let mut ret: HashMap<String, Vec<RepoConfig>> = HashMap::new();
|
||||
let mut ret: HashMap<String, Vec<Repo>> = HashMap::new();
|
||||
|
||||
for repo in repos {
|
||||
let namespace = repo.namespace().clone();
|
||||
|
||||
37
src/repo.rs
37
src/repo.rs
@@ -104,27 +104,18 @@ impl std::fmt::Display for RepoError {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
#[derive(Debug)]
|
||||
pub struct Remote {
|
||||
pub name: String,
|
||||
pub url: String,
|
||||
#[serde(rename = "type")]
|
||||
pub remote_type: RemoteType,
|
||||
}
|
||||
|
||||
fn worktree_setup_default() -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
#[derive(Debug, Serialize, Deserialize)]
|
||||
#[serde(deny_unknown_fields)]
|
||||
pub struct RepoConfig {
|
||||
#[derive(Debug)]
|
||||
pub struct Repo {
|
||||
pub name: String,
|
||||
|
||||
#[serde(default = "worktree_setup_default")]
|
||||
pub worktree_setup: bool,
|
||||
|
||||
pub remotes: Option<Vec<Remote>>,
|
||||
}
|
||||
|
||||
@@ -182,7 +173,7 @@ impl Worktree {
|
||||
}
|
||||
|
||||
pub fn forward_branch(&self, rebase: bool, stash: bool) -> Result<Option<String>, String> {
|
||||
let repo = Repo::open(Path::new(&self.name), false)
|
||||
let repo = RepoHandle::open(Path::new(&self.name), false)
|
||||
.map_err(|error| format!("Error opening worktree: {}", error))?;
|
||||
|
||||
if let Ok(remote_branch) = repo.find_local_branch(&self.name)?.upstream() {
|
||||
@@ -286,7 +277,7 @@ impl Worktree {
|
||||
config: &Option<WorktreeRootConfig>,
|
||||
stash: bool,
|
||||
) -> Result<Option<String>, String> {
|
||||
let repo = Repo::open(Path::new(&self.name), false)
|
||||
let repo = RepoHandle::open(Path::new(&self.name), false)
|
||||
.map_err(|error| format!("Error opening worktree: {}", error))?;
|
||||
|
||||
let guess_default_branch = || {
|
||||
@@ -468,14 +459,14 @@ pub fn detect_remote_type(remote_url: &str) -> Option<RemoteType> {
|
||||
None
|
||||
}
|
||||
|
||||
pub struct Repo(git2::Repository);
|
||||
pub struct RepoHandle(git2::Repository);
|
||||
pub struct Branch<'a>(git2::Branch<'a>);
|
||||
|
||||
fn convert_libgit2_error(error: git2::Error) -> String {
|
||||
error.message().to_string()
|
||||
}
|
||||
|
||||
impl Repo {
|
||||
impl RepoHandle {
|
||||
pub fn open(path: &Path, is_worktree: bool) -> Result<Self, RepoError> {
|
||||
let open_func = match is_worktree {
|
||||
true => Repository::open_bare,
|
||||
@@ -507,7 +498,7 @@ impl Repo {
|
||||
// Right now, we just open the repo AGAIN. It is safe, as we are only accessing the stash
|
||||
// with the second reference, so there are no cross effects. But it just smells. Also,
|
||||
// using `unwrap()` here as we are already sure that the repo is openable(?).
|
||||
let mut repo = Repo::open(self.0.path(), false).unwrap();
|
||||
let mut repo = RepoHandle::open(self.0.path(), false).unwrap();
|
||||
repo.0
|
||||
.stash_save2(&author, None, Some(git2::StashFlags::INCLUDE_UNTRACKED))
|
||||
.map_err(convert_libgit2_error)?;
|
||||
@@ -515,7 +506,7 @@ impl Repo {
|
||||
}
|
||||
|
||||
pub fn stash_pop(&self) -> Result<(), String> {
|
||||
let mut repo = Repo::open(self.0.path(), false).unwrap();
|
||||
let mut repo = RepoHandle::open(self.0.path(), false).unwrap();
|
||||
repo.0
|
||||
.stash_pop(
|
||||
0,
|
||||
@@ -659,7 +650,7 @@ impl Repo {
|
||||
.map_err(convert_libgit2_error)?,
|
||||
};
|
||||
|
||||
let repo = Repo(repo);
|
||||
let repo = RepoHandle(repo);
|
||||
|
||||
if is_worktree {
|
||||
repo.set_config_push(GitPushDefaultSetting::Upstream)?;
|
||||
@@ -788,7 +779,7 @@ impl Repo {
|
||||
}
|
||||
}
|
||||
|
||||
let worktree_repo = Repo::open(root_dir, true).map_err(|error| {
|
||||
let worktree_repo = RepoHandle::open(root_dir, true).map_err(|error| {
|
||||
WorktreeConversionFailureReason::Error(format!(
|
||||
"Opening newly converted repository failed: {}",
|
||||
error
|
||||
@@ -1068,7 +1059,7 @@ impl Repo {
|
||||
name
|
||||
)));
|
||||
}
|
||||
let worktree_repo = Repo::open(worktree_dir, false).map_err(|error| {
|
||||
let worktree_repo = RepoHandle::open(worktree_dir, false).map_err(|error| {
|
||||
WorktreeRemoveFailureReason::Error(format!("Error opening repo: {}", error))
|
||||
})?;
|
||||
|
||||
@@ -1427,7 +1418,7 @@ impl RemoteHandle<'_> {
|
||||
&mut self,
|
||||
local_branch_name: &str,
|
||||
remote_branch_name: &str,
|
||||
_repo: &Repo,
|
||||
_repo: &RepoHandle,
|
||||
) -> Result<(), String> {
|
||||
if !self.is_pushable()? {
|
||||
return Err(String::from("Trying to push to a non-pushable remote"));
|
||||
@@ -1493,7 +1484,7 @@ pub fn clone_repo(
|
||||
}
|
||||
}
|
||||
|
||||
let repo = Repo::open(&clone_target, false)?;
|
||||
let repo = RepoHandle::open(&clone_target, false)?;
|
||||
|
||||
if is_worktree {
|
||||
repo.set_config_push(GitPushDefaultSetting::Upstream)?;
|
||||
|
||||
20
src/table.rs
20
src/table.rs
@@ -1,4 +1,4 @@
|
||||
use crate::Repo;
|
||||
use crate::RepoHandle;
|
||||
|
||||
use comfy_table::{Cell, Table};
|
||||
|
||||
@@ -21,7 +21,7 @@ fn add_table_header(table: &mut Table) {
|
||||
fn add_repo_status(
|
||||
table: &mut Table,
|
||||
repo_name: &str,
|
||||
repo_handle: &crate::Repo,
|
||||
repo_handle: &crate::RepoHandle,
|
||||
is_worktree: bool,
|
||||
) -> Result<(), String> {
|
||||
let repo_status = repo_handle.status(is_worktree)?;
|
||||
@@ -99,7 +99,7 @@ fn add_repo_status(
|
||||
|
||||
// Don't return table, return a type that implements Display(?)
|
||||
pub fn get_worktree_status_table(
|
||||
repo: &crate::Repo,
|
||||
repo: &crate::RepoHandle,
|
||||
directory: &Path,
|
||||
) -> Result<(impl std::fmt::Display, Vec<String>), String> {
|
||||
let worktrees = repo.get_worktrees()?;
|
||||
@@ -111,7 +111,7 @@ pub fn get_worktree_status_table(
|
||||
for worktree in &worktrees {
|
||||
let worktree_dir = &directory.join(&worktree.name());
|
||||
if worktree_dir.exists() {
|
||||
let repo = match crate::Repo::open(worktree_dir, false) {
|
||||
let repo = match crate::RepoHandle::open(worktree_dir, false) {
|
||||
Ok(repo) => repo,
|
||||
Err(error) => {
|
||||
errors.push(format!(
|
||||
@@ -132,7 +132,7 @@ pub fn get_worktree_status_table(
|
||||
));
|
||||
}
|
||||
}
|
||||
for worktree in Repo::find_unmanaged_worktrees(repo, directory)? {
|
||||
for worktree in RepoHandle::find_unmanaged_worktrees(repo, directory)? {
|
||||
errors.push(format!(
|
||||
"Found {}, which is not a valid worktree directory!",
|
||||
&worktree
|
||||
@@ -144,7 +144,7 @@ pub fn get_worktree_status_table(
|
||||
pub fn get_status_table(config: crate::Config) -> Result<(Vec<Table>, Vec<String>), String> {
|
||||
let mut errors = Vec::new();
|
||||
let mut tables = Vec::new();
|
||||
for tree in config.trees()?.as_vec() {
|
||||
for tree in config.trees()? {
|
||||
let repos = tree.repos.unwrap_or_default();
|
||||
|
||||
let root_path = crate::expand_path(Path::new(&tree.root));
|
||||
@@ -163,7 +163,7 @@ pub fn get_status_table(config: crate::Config) -> Result<(Vec<Table>, Vec<String
|
||||
continue;
|
||||
}
|
||||
|
||||
let repo_handle = crate::Repo::open(&repo_path, repo.worktree_setup);
|
||||
let repo_handle = crate::RepoHandle::open(&repo_path, repo.worktree_setup);
|
||||
|
||||
let repo_handle = match repo_handle {
|
||||
Ok(repo) => repo,
|
||||
@@ -207,7 +207,7 @@ fn add_worktree_table_header(table: &mut Table) {
|
||||
fn add_worktree_status(
|
||||
table: &mut Table,
|
||||
worktree: &crate::repo::Worktree,
|
||||
repo: &crate::Repo,
|
||||
repo: &crate::RepoHandle,
|
||||
) -> Result<(), String> {
|
||||
let repo_status = repo.status(false)?;
|
||||
|
||||
@@ -272,10 +272,10 @@ pub fn show_single_repo_status(
|
||||
let mut table = Table::new();
|
||||
let mut warnings = Vec::new();
|
||||
|
||||
let is_worktree = crate::Repo::detect_worktree(path);
|
||||
let is_worktree = crate::RepoHandle::detect_worktree(path);
|
||||
add_table_header(&mut table);
|
||||
|
||||
let repo_handle = crate::Repo::open(path, is_worktree);
|
||||
let repo_handle = crate::RepoHandle::open(path, is_worktree);
|
||||
|
||||
if let Err(error) = repo_handle {
|
||||
if error.kind == crate::RepoErrorKind::NotFound {
|
||||
|
||||
@@ -8,13 +8,13 @@ use helpers::*;
|
||||
fn open_empty_repo() {
|
||||
let tmpdir = init_tmpdir();
|
||||
assert!(matches!(
|
||||
Repo::open(tmpdir.path(), true),
|
||||
RepoHandle::open(tmpdir.path(), true),
|
||||
Err(RepoError {
|
||||
kind: RepoErrorKind::NotFound
|
||||
})
|
||||
));
|
||||
assert!(matches!(
|
||||
Repo::open(tmpdir.path(), false),
|
||||
RepoHandle::open(tmpdir.path(), false),
|
||||
Err(RepoError {
|
||||
kind: RepoErrorKind::NotFound
|
||||
})
|
||||
@@ -25,7 +25,7 @@ fn open_empty_repo() {
|
||||
#[test]
|
||||
fn create_repo() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let tmpdir = init_tmpdir();
|
||||
let repo = Repo::init(tmpdir.path(), false)?;
|
||||
let repo = RepoHandle::init(tmpdir.path(), false)?;
|
||||
assert!(!repo.is_bare());
|
||||
assert!(repo.is_empty()?);
|
||||
cleanup_tmpdir(tmpdir);
|
||||
@@ -35,7 +35,7 @@ fn create_repo() -> Result<(), Box<dyn std::error::Error>> {
|
||||
#[test]
|
||||
fn create_repo_with_worktree() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let tmpdir = init_tmpdir();
|
||||
let repo = Repo::init(tmpdir.path(), true)?;
|
||||
let repo = RepoHandle::init(tmpdir.path(), true)?;
|
||||
assert!(repo.is_bare());
|
||||
assert!(repo.is_empty()?);
|
||||
cleanup_tmpdir(tmpdir);
|
||||
|
||||
Reference in New Issue
Block a user