Give repos a namespace to allow subdirectories

This commit is contained in:
2022-05-26 16:30:32 +02:00
parent f2d2482476
commit b8c552fb62
7 changed files with 92 additions and 36 deletions

View File

@@ -5,7 +5,7 @@ use crate::output::*;
use std::path::Path; use std::path::Path;
use crate::{get_token_from_command, Remote, Repo, Tree}; use crate::{get_token_from_command, path_as_string, Remote, Repo, Tree};
use crate::provider; use crate::provider;
use crate::provider::Filter; use crate::provider::Filter;
@@ -104,8 +104,15 @@ impl RepoConfig {
} }
pub fn into_repo(self) -> Repo { pub fn into_repo(self) -> Repo {
let (namespace, name) = if let Some((namespace, name)) = self.name.rsplit_once('/') {
(Some(namespace.to_string()), name.to_string())
} else {
(None, self.name)
};
Repo { Repo {
name: self.name, name,
namespace,
worktree_setup: self.worktree_setup, worktree_setup: self.worktree_setup,
remotes: self.remotes.map(|remotes| { remotes: self.remotes.map(|remotes| {
remotes remotes
@@ -209,7 +216,11 @@ impl Config {
.map(RepoConfig::from_repo) .map(RepoConfig::from_repo)
.collect(); .collect();
let tree = ConfigTree { let tree = ConfigTree {
root: crate::path_as_string(&Path::new(&config.root).join(namespace)), root: if let Some(namespace) = namespace {
path_as_string(&Path::new(&config.root).join(namespace))
} else {
path_as_string(Path::new(&config.root))
},
repos: Some(repos), repos: Some(repos),
}; };
trees.push(tree); trees.push(tree);

View File

@@ -82,10 +82,13 @@ fn main() {
let mut trees: Vec<config::ConfigTree> = vec![]; let mut trees: Vec<config::ConfigTree> = vec![];
for (namespace, repolist) in repos { for (namespace, repolist) in repos {
let tree = config::ConfigTree::from_repos( let root = if let Some(namespace) = namespace {
Path::new(&args.root).join(namespace).display().to_string(), path_as_string(&Path::new(&args.root).join(namespace))
repolist, } else {
); path_as_string(Path::new(&args.root))
};
let tree = config::ConfigTree::from_repos(root, repolist);
trees.push(tree); trees.push(tree);
} }
@@ -310,7 +313,11 @@ fn main() {
for (namespace, namespace_repos) in repos { for (namespace, namespace_repos) in repos {
let tree = config::ConfigTree { let tree = config::ConfigTree {
root: path_as_string(&Path::new(&config.root).join(namespace)), root: if let Some(namespace) = namespace {
path_as_string(&Path::new(&config.root).join(namespace))
} else {
path_as_string(Path::new(&config.root))
},
repos: Some( repos: Some(
namespace_repos namespace_repos
.into_iter() .into_iter()
@@ -402,7 +409,11 @@ fn main() {
for (namespace, repolist) in repos { for (namespace, repolist) in repos {
let tree = config::ConfigTree { let tree = config::ConfigTree {
root: Path::new(&args.root).join(namespace).display().to_string(), root: if let Some(namespace) = namespace {
path_as_string(&Path::new(&args.root).join(namespace))
} else {
path_as_string(Path::new(&args.root))
},
repos: Some( repos: Some(
repolist repolist
.into_iter() .into_iter()

View File

@@ -147,7 +147,7 @@ pub fn get_token_from_command(command: &str) -> Result<String, String> {
} }
fn sync_repo(root_path: &Path, repo: &Repo, 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 repo_path = root_path.join(&repo.fullname());
let actual_git_directory = get_actual_git_directory(&repo_path, repo.worktree_setup); let actual_git_directory = get_actual_git_directory(&repo_path, repo.worktree_setup);
let mut newly_created = false; let mut newly_created = false;
@@ -460,17 +460,33 @@ fn find_repos(root: &Path) -> Result<Option<(Vec<Repo>, Vec<String>, bool)>, Str
} }
let remotes = results; let remotes = results;
repos.push(Repo{ let (namespace, name) = if path == root {
name: match path == root { (
true => match &root.parent() { None,
match &root.parent() {
Some(parent) => path_as_string(path.strip_prefix(parent).unwrap()), Some(parent) => path_as_string(path.strip_prefix(parent).unwrap()),
None => { None => {
warnings.push(String::from("Getting name of the search root failed. Do you have a git repository in \"/\"?")); warnings.push(String::from("Getting name of the search root failed. Do you have a git repository in \"/\"?"));
continue continue;
}, }
} },
false => path_as_string(path.strip_prefix(&root).unwrap()), )
}, } else {
let name = path.strip_prefix(&root).unwrap();
let namespace = name.parent().unwrap();
(
if namespace != Path::new("") {
Some(path_as_string(namespace).to_string())
} else {
None
},
path_as_string(name),
)
};
repos.push(Repo {
name,
namespace,
remotes: Some(remotes), remotes: Some(remotes),
worktree_setup: is_worktree, worktree_setup: is_worktree,
}); });

View File

@@ -32,12 +32,12 @@ impl Project for GithubProject {
self.name.clone() self.name.clone()
} }
fn namespace(&self) -> String { fn namespace(&self) -> Option<String> {
self.full_name if let Some((namespace, _name)) = self.full_name.rsplit_once('/') {
.rsplit_once('/') Some(namespace.to_string())
.expect("Github project name did not include a namespace") } else {
.0 None
.to_string() }
} }
fn ssh_url(&self) -> String { fn ssh_url(&self) -> String {

View File

@@ -39,12 +39,12 @@ impl Project for GitlabProject {
self.name.clone() self.name.clone()
} }
fn namespace(&self) -> String { fn namespace(&self) -> Option<String> {
self.path_with_namespace if let Some((namespace, _name)) = self.path_with_namespace.rsplit_once('/') {
.rsplit_once('/') Some(namespace.to_string())
.expect("Gitlab project name did not include a namespace") } else {
.0 None
.to_string() }
} }
fn ssh_url(&self) -> String { fn ssh_url(&self) -> String {

View File

@@ -35,6 +35,7 @@ pub trait Project {
{ {
Repo { Repo {
name: self.name(), name: self.name(),
namespace: self.namespace(),
worktree_setup, worktree_setup,
remotes: Some(vec![Remote { remotes: Some(vec![Remote {
name: String::from(provider_name), name: String::from(provider_name),
@@ -53,7 +54,7 @@ pub trait Project {
} }
fn name(&self) -> String; fn name(&self) -> String;
fn namespace(&self) -> String; fn namespace(&self) -> Option<String>;
fn ssh_url(&self) -> String; fn ssh_url(&self) -> String;
fn http_url(&self) -> String; fn http_url(&self) -> String;
fn private(&self) -> bool; fn private(&self) -> bool;
@@ -200,7 +201,7 @@ pub trait Provider {
&self, &self,
worktree_setup: bool, worktree_setup: bool,
force_ssh: bool, force_ssh: bool,
) -> Result<HashMap<String, Vec<Repo>>, String> { ) -> Result<HashMap<Option<String>, Vec<Repo>>, String> {
let mut repos = vec![]; let mut repos = vec![];
if self.filter().owner { if self.filter().owner {
@@ -277,12 +278,16 @@ pub trait Provider {
} }
} }
let mut ret: HashMap<String, Vec<Repo>> = HashMap::new(); let mut ret: HashMap<Option<String>, Vec<Repo>> = HashMap::new();
for repo in repos { for repo in repos {
let namespace = repo.namespace().clone(); let namespace = repo.namespace();
let repo = repo.into_repo_config(&self.name(), worktree_setup, force_ssh); let mut repo = repo.into_repo_config(&self.name(), worktree_setup, force_ssh);
// Namespace is already part of the hashmap key. I'm not too happy
// about the data exchange format here.
repo.remove_namespace();
ret.entry(namespace).or_insert(vec![]).push(repo); ret.entry(namespace).or_insert(vec![]).push(repo);
} }

View File

@@ -114,11 +114,24 @@ pub struct Remote {
#[derive(Debug)] #[derive(Debug)]
pub struct Repo { pub struct Repo {
pub name: String, pub name: String,
pub namespace: Option<String>,
pub worktree_setup: bool, pub worktree_setup: bool,
pub remotes: Option<Vec<Remote>>, pub remotes: Option<Vec<Remote>>,
} }
impl Repo {
pub fn fullname(&self) -> String {
match &self.namespace {
Some(namespace) => format!("{}/{}", namespace, self.name),
None => self.name.clone(),
}
}
pub fn remove_namespace(&mut self) {
self.namespace = None
}
}
pub struct RepoChanges { pub struct RepoChanges {
pub files_new: usize, pub files_new: usize,
pub files_modified: usize, pub files_modified: usize,