Separate config structs from internal structs

This commit is contained in:
2022-05-26 16:30:11 +02:00
parent 10e02c20a1
commit 6ef759a14e
7 changed files with 217 additions and 115 deletions

View File

@@ -3,27 +3,32 @@ use std::process;
use crate::output::*; use crate::output::*;
use super::repo::RepoConfig;
use std::path::Path; 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;
use crate::provider::Filter; use crate::provider::Filter;
use crate::provider::Provider; use crate::provider::Provider;
pub type RemoteProvider = crate::provider::RemoteProvider; pub type RemoteProvider = crate::provider::RemoteProvider;
pub type RemoteType = crate::repo::RemoteType;
fn worktree_setup_default() -> bool {
false
}
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
#[serde(untagged)] #[serde(untagged)]
pub enum Config { pub enum Config {
ConfigTree(ConfigTree), ConfigTrees(ConfigTrees),
ConfigProvider(ConfigProvider), ConfigProvider(ConfigProvider),
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct ConfigTree { pub struct ConfigTrees {
pub trees: Trees, pub trees: Vec<ConfigTree>,
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
@@ -50,30 +55,100 @@ pub struct ConfigProvider {
} }
#[derive(Debug, Serialize, Deserialize)] #[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 { pub fn to_config(self) -> Config {
Config::ConfigTree(ConfigTree { trees: self }) Config::ConfigTrees(self)
} }
pub fn from_vec(vec: Vec<Tree>) -> Self { pub fn from_vec(vec: Vec<ConfigTree>) -> Self {
Trees(vec) ConfigTrees { trees: vec }
} }
pub fn as_vec(self) -> Vec<Tree> { pub fn from_trees(vec: Vec<Tree>) -> Self {
self.0 ConfigTrees {
trees: vec.into_iter().map(ConfigTree::from_tree).collect(),
}
} }
pub fn as_vec_ref(&self) -> &Vec<Tree> { pub fn trees(self) -> Vec<ConfigTree> {
self.0.as_ref() 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 { impl Config {
pub fn trees(self) -> Result<Trees, String> { pub fn trees(self) -> Result<Vec<ConfigTree>, String> {
match self { match self {
Config::ConfigTree(config) => Ok(config.trees), Config::ConfigTrees(config) => Ok(config.trees),
Config::ConfigProvider(config) => { Config::ConfigProvider(config) => {
let token = match get_token_from_command(&config.token_command) { let token = match get_token_from_command(&config.token_command) {
Ok(token) => token, Ok(token) => token,
@@ -129,27 +204,29 @@ impl Config {
let mut trees = vec![]; let mut trees = vec![];
for (namespace, namespace_repos) in repos { 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)), root: crate::path_as_string(&Path::new(&config.root).join(namespace)),
repos: Some(namespace_repos), repos: Some(repos),
}; };
trees.push(tree); trees.push(tree);
} }
Ok(Trees(trees)) Ok(trees)
} }
} }
} }
pub fn from_trees(trees: Vec<Tree>) -> Self { pub fn from_trees(trees: Vec<ConfigTree>) -> Self {
Config::ConfigTree(ConfigTree { Config::ConfigTrees(ConfigTrees { trees })
trees: Trees::from_vec(trees),
})
} }
pub fn normalize(&mut self) { 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(); 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) { if tree.root.starts_with(&home) {
// The tilde is not handled differently, it's just a normal path component for `Path`. // The tilde is not handled differently, it's just a normal path component for `Path`.
// Therefore we can treat it like that during **output**. // Therefore we can treat it like that during **output**.
@@ -181,11 +258,27 @@ impl Config {
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)] #[serde(deny_unknown_fields)]
pub struct Tree { pub struct ConfigTree {
pub root: String, pub root: String,
pub repos: Option<Vec<RepoConfig>>, 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> pub fn read_config<'a, T>(path: &str) -> Result<T, String>
where where
T: for<'de> serde::Deserialize<'de>, T: for<'de> serde::Deserialize<'de>,

View File

@@ -5,6 +5,7 @@ mod cmd;
use grm::config; use grm::config;
use grm::output::*; use grm::output::*;
use grm::path_as_string;
use grm::provider; use grm::provider;
use grm::provider::Provider; use grm::provider::Provider;
use grm::repo; use grm::repo;
@@ -78,16 +79,13 @@ fn main() {
match repos { match repos {
Ok(repos) => { Ok(repos) => {
let mut trees: Vec<config::Tree> = vec![]; let mut trees: Vec<config::ConfigTree> = vec![];
for (namespace, repolist) in repos { for (namespace, repolist) in repos {
let tree = config::Tree { let tree = config::ConfigTree::from_repos(
root: Path::new(&args.root) Path::new(&args.root).join(namespace).display().to_string(),
.join(namespace) repolist,
.display() );
.to_string(),
repos: Some(repolist),
};
trees.push(tree); trees.push(tree);
} }
@@ -191,8 +189,8 @@ fn main() {
} }
}; };
let trees = grm::config::Trees::from_vec(vec![found_repos]); let trees = grm::config::ConfigTrees::from_trees(vec![found_repos]);
if trees.as_vec_ref().iter().all(|t| match &t.repos { if trees.trees_ref().iter().all(|t| match &t.repos {
None => false, None => false,
Some(r) => r.is_empty(), Some(r) => r.is_empty(),
}) { }) {
@@ -311,9 +309,14 @@ fn main() {
let mut trees = vec![]; let mut trees = vec![];
for (namespace, namespace_repos) in repos { for (namespace, namespace_repos) in repos {
let tree = config::Tree { let tree = config::ConfigTree {
root: grm::path_as_string(&Path::new(&config.root).join(namespace)), root: path_as_string(&Path::new(&config.root).join(namespace)),
repos: Some(namespace_repos), repos: Some(
namespace_repos
.into_iter()
.map(grm::config::RepoConfig::from_repo)
.collect(),
),
}; };
trees.push(tree); trees.push(tree);
} }
@@ -395,12 +398,17 @@ fn main() {
process::exit(1); process::exit(1);
}); });
let mut trees: Vec<config::Tree> = vec![]; let mut trees: Vec<config::ConfigTree> = vec![];
for (namespace, repolist) in repos { for (namespace, repolist) in repos {
let tree = config::Tree { let tree = config::ConfigTree {
root: Path::new(&args.root).join(namespace).display().to_string(), 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); 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)); print_error(&format!("Error opening repository: {}", error));
process::exit(1); process::exit(1);
}); });
@@ -539,7 +547,7 @@ fn main() {
} }
} }
cmd::WorktreeAction::Status(_args) => { 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)); print_error(&format!("Error opening repository: {}", error));
process::exit(1); process::exit(1);
}); });
@@ -564,7 +572,7 @@ fn main() {
// * Remove all files // * Remove all files
// * Set `core.bare` to `true` // * 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 { if error.kind == grm::RepoErrorKind::NotFound {
print_error("Directory does not contain a git repository"); print_error("Directory does not contain a git repository");
} else { } else {
@@ -592,7 +600,7 @@ fn main() {
} }
} }
cmd::WorktreeAction::Clean(_args) => { 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 { if error.kind == grm::RepoErrorKind::NotFound {
print_error("Directory does not contain a git repository"); print_error("Directory does not contain a git repository");
} else { } else {
@@ -626,7 +634,7 @@ fn main() {
} }
} }
cmd::WorktreeAction::Fetch(_args) => { 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 { if error.kind == grm::RepoErrorKind::NotFound {
print_error("Directory does not contain a git repository"); print_error("Directory does not contain a git repository");
} else { } else {
@@ -642,7 +650,7 @@ fn main() {
print_success("Fetched from all remotes"); print_success("Fetched from all remotes");
} }
cmd::WorktreeAction::Pull(args) => { 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 { if error.kind == grm::RepoErrorKind::NotFound {
print_error("Directory does not contain a git repository"); print_error("Directory does not contain a git repository");
} else { } else {
@@ -683,7 +691,7 @@ fn main() {
print_error("There is no point in using --rebase without --pull"); print_error("There is no point in using --rebase without --pull");
process::exit(1); 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 { if error.kind == grm::RepoErrorKind::NotFound {
print_error("Directory does not contain a git repository"); print_error("Directory does not contain a git repository");
} else { } else {

View File

@@ -11,12 +11,14 @@ pub mod provider;
pub mod repo; pub mod repo;
pub mod table; pub mod table;
use config::{Config, Tree}; use config::Config;
use output::*; 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 GIT_MAIN_WORKTREE_DIRECTORY: &str = ".git-main-working-tree";
const BRANCH_NAMESPACE_SEPARATOR: &str = "/"; 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_BARE_KEY: &str = "core.bare";
const GIT_CONFIG_PUSH_DEFAULT: &str = "push.default"; const GIT_CONFIG_PUSH_DEFAULT: &str = "push.default";
pub struct Tree {
root: String,
repos: Vec<Repo>,
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use super::*; use super::*;
@@ -139,7 +146,7 @@ pub fn get_token_from_command(command: &str) -> Result<String, String> {
Ok(token.to_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 repo_path = root_path.join(&repo.name);
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);
@@ -156,7 +163,7 @@ fn sync_repo(root_path: &Path, repo: &RepoConfig, init_worktree: bool) -> Result
&repo.name, &repo.name,
"Repository does not have remotes configured, initializing new", "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) => { Ok(r) => {
print_repo_success(&repo.name, "Repository created"); print_repo_success(&repo.name, "Repository created");
Some(r) Some(r)
@@ -180,10 +187,10 @@ fn sync_repo(root_path: &Path, repo: &RepoConfig, init_worktree: bool) -> Result
newly_created = true; 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, Ok(repo) => repo,
Err(error) => { 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( return Err(String::from(
"Repo already exists, but is using a worktree setup", "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( pub fn find_unmanaged_repos(
root_path: &Path, root_path: &Path,
managed_repos: &[RepoConfig], managed_repos: &[Repo],
) -> Result<Vec<String>, String> { ) -> Result<Vec<String>, String> {
let mut unmanaged_repos = Vec::new(); 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> { pub fn sync_trees(config: Config, init_worktree: bool) -> Result<bool, String> {
let mut failures = false; 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)); 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 /// The bool in the return value specifies whether there is a repository
/// in root itself. /// in root itself.
#[allow(clippy::type_complexity)] #[allow(clippy::type_complexity)]
fn find_repos(root: &Path) -> Result<Option<(Vec<RepoConfig>, Vec<String>, bool)>, String> { fn find_repos(root: &Path) -> Result<Option<(Vec<Repo>, Vec<String>, bool)>, String> {
let mut repos: Vec<RepoConfig> = Vec::new(); let mut repos: Vec<Repo> = Vec::new();
let mut repo_in_root = false; let mut repo_in_root = false;
let mut warnings = Vec::new(); let mut warnings = Vec::new();
for path in find_repo_paths(root)? { for path in find_repo_paths(root)? {
let is_worktree = Repo::detect_worktree(&path); let is_worktree = RepoHandle::detect_worktree(&path);
if path == root { if path == root {
repo_in_root = true; repo_in_root = true;
} }
match Repo::open(&path, is_worktree) { match RepoHandle::open(&path, is_worktree) {
Err(error) => { Err(error) => {
warnings.push(format!( warnings.push(format!(
"Error opening repo {}{}: {}", "Error opening repo {}{}: {}",
@@ -445,7 +460,7 @@ fn find_repos(root: &Path) -> Result<Option<(Vec<RepoConfig>, Vec<String>, bool)
} }
let remotes = results; let remotes = results;
repos.push(RepoConfig { repos.push(Repo{
name: match path == root { name: match path == root {
true => match &root.parent() { true => match &root.parent() {
Some(parent) => path_as_string(path.strip_prefix(parent).unwrap()), 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> { pub fn find_in_tree(path: &Path) -> Result<(Tree, Vec<String>), String> {
let mut warnings = Vec::new(); 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)) => { Some((vec, mut repo_warnings, repo_in_root)) => {
warnings.append(&mut repo_warnings); warnings.append(&mut repo_warnings);
(vec, repo_in_root) (vec, repo_in_root)
@@ -491,7 +506,7 @@ pub fn find_in_tree(path: &Path) -> Result<(Tree, Vec<String>), String> {
Ok(( Ok((
Tree { Tree {
root: root.into_os_string().into_string().unwrap(), root: root.into_os_string().into_string().unwrap(),
repos: Some(repos), repos,
}, },
warnings, warnings,
)) ))
@@ -504,7 +519,7 @@ pub fn add_worktree(
track: Option<(&str, &str)>, track: Option<(&str, &str)>,
no_track: bool, no_track: bool,
) -> Result<(), String> { ) -> 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 => { RepoErrorKind::NotFound => {
String::from("Current directory does not contain a worktree setup") String::from("Current directory does not contain a worktree setup")
} }
@@ -579,7 +594,7 @@ pub fn add_worktree(
remote: &mut repo::RemoteHandle, remote: &mut repo::RemoteHandle,
branch_name: &str, branch_name: &str,
remote_branch_name: &str, remote_branch_name: &str,
repo: &repo::Repo, repo: &repo::RepoHandle,
) -> Result<(), String> { ) -> Result<(), String> {
if !remote.is_pushable()? { if !remote.is_pushable()? {
return Err(format!( return Err(format!(

View File

@@ -9,7 +9,7 @@ pub mod gitlab;
pub use github::Github; pub use github::Github;
pub use gitlab::Gitlab; pub use gitlab::Gitlab;
use crate::{Remote, RemoteType, RepoConfig}; use crate::{Remote, RemoteType, Repo};
use std::collections::HashMap; use std::collections::HashMap;
@@ -29,16 +29,11 @@ enum ProjectResponse<T, U> {
} }
pub trait Project { pub trait Project {
fn into_repo_config( fn into_repo_config(self, provider_name: &str, worktree_setup: bool, force_ssh: bool) -> Repo
self,
provider_name: &str,
worktree_setup: bool,
force_ssh: bool,
) -> RepoConfig
where where
Self: Sized, Self: Sized,
{ {
RepoConfig { Repo {
name: self.name(), name: self.name(),
worktree_setup, worktree_setup,
remotes: Some(vec![Remote { remotes: Some(vec![Remote {
@@ -205,7 +200,7 @@ pub trait Provider {
&self, &self,
worktree_setup: bool, worktree_setup: bool,
force_ssh: bool, force_ssh: bool,
) -> Result<HashMap<String, Vec<RepoConfig>>, String> { ) -> Result<HashMap<String, Vec<Repo>>, String> {
let mut repos = vec![]; let mut repos = vec![];
if self.filter().owner { 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 { for repo in repos {
let namespace = repo.namespace().clone(); let namespace = repo.namespace().clone();

View File

@@ -104,27 +104,18 @@ impl std::fmt::Display for RepoError {
} }
} }
#[derive(Debug, Serialize, Deserialize)] #[derive(Debug)]
#[serde(deny_unknown_fields)]
pub struct Remote { pub struct Remote {
pub name: String, pub name: String,
pub url: String, pub url: String,
#[serde(rename = "type")]
pub remote_type: RemoteType, pub remote_type: RemoteType,
} }
fn worktree_setup_default() -> bool { #[derive(Debug)]
false pub struct Repo {
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct RepoConfig {
pub name: String, pub name: String,
#[serde(default = "worktree_setup_default")]
pub worktree_setup: bool, pub worktree_setup: bool,
pub remotes: Option<Vec<Remote>>, pub remotes: Option<Vec<Remote>>,
} }
@@ -182,7 +173,7 @@ impl Worktree {
} }
pub fn forward_branch(&self, rebase: bool, stash: bool) -> Result<Option<String>, String> { 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))?; .map_err(|error| format!("Error opening worktree: {}", error))?;
if let Ok(remote_branch) = repo.find_local_branch(&self.name)?.upstream() { if let Ok(remote_branch) = repo.find_local_branch(&self.name)?.upstream() {
@@ -286,7 +277,7 @@ impl Worktree {
config: &Option<WorktreeRootConfig>, config: &Option<WorktreeRootConfig>,
stash: bool, stash: bool,
) -> Result<Option<String>, String> { ) -> 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))?; .map_err(|error| format!("Error opening worktree: {}", error))?;
let guess_default_branch = || { let guess_default_branch = || {
@@ -468,14 +459,14 @@ pub fn detect_remote_type(remote_url: &str) -> Option<RemoteType> {
None None
} }
pub struct Repo(git2::Repository); pub struct RepoHandle(git2::Repository);
pub struct Branch<'a>(git2::Branch<'a>); pub struct Branch<'a>(git2::Branch<'a>);
fn convert_libgit2_error(error: git2::Error) -> String { fn convert_libgit2_error(error: git2::Error) -> String {
error.message().to_string() error.message().to_string()
} }
impl Repo { impl RepoHandle {
pub fn open(path: &Path, is_worktree: bool) -> Result<Self, RepoError> { pub fn open(path: &Path, is_worktree: bool) -> Result<Self, RepoError> {
let open_func = match is_worktree { let open_func = match is_worktree {
true => Repository::open_bare, 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 // 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, // 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(?). // 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 repo.0
.stash_save2(&author, None, Some(git2::StashFlags::INCLUDE_UNTRACKED)) .stash_save2(&author, None, Some(git2::StashFlags::INCLUDE_UNTRACKED))
.map_err(convert_libgit2_error)?; .map_err(convert_libgit2_error)?;
@@ -515,7 +506,7 @@ impl Repo {
} }
pub fn stash_pop(&self) -> Result<(), String> { 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 repo.0
.stash_pop( .stash_pop(
0, 0,
@@ -659,7 +650,7 @@ impl Repo {
.map_err(convert_libgit2_error)?, .map_err(convert_libgit2_error)?,
}; };
let repo = Repo(repo); let repo = RepoHandle(repo);
if is_worktree { if is_worktree {
repo.set_config_push(GitPushDefaultSetting::Upstream)?; 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!( WorktreeConversionFailureReason::Error(format!(
"Opening newly converted repository failed: {}", "Opening newly converted repository failed: {}",
error error
@@ -1068,7 +1059,7 @@ impl Repo {
name 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)) WorktreeRemoveFailureReason::Error(format!("Error opening repo: {}", error))
})?; })?;
@@ -1427,7 +1418,7 @@ impl RemoteHandle<'_> {
&mut self, &mut self,
local_branch_name: &str, local_branch_name: &str,
remote_branch_name: &str, remote_branch_name: &str,
_repo: &Repo, _repo: &RepoHandle,
) -> Result<(), String> { ) -> Result<(), String> {
if !self.is_pushable()? { if !self.is_pushable()? {
return Err(String::from("Trying to push to a non-pushable remote")); 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 { if is_worktree {
repo.set_config_push(GitPushDefaultSetting::Upstream)?; repo.set_config_push(GitPushDefaultSetting::Upstream)?;

View File

@@ -1,4 +1,4 @@
use crate::Repo; use crate::RepoHandle;
use comfy_table::{Cell, Table}; use comfy_table::{Cell, Table};
@@ -21,7 +21,7 @@ fn add_table_header(table: &mut Table) {
fn add_repo_status( fn add_repo_status(
table: &mut Table, table: &mut Table,
repo_name: &str, repo_name: &str,
repo_handle: &crate::Repo, repo_handle: &crate::RepoHandle,
is_worktree: bool, is_worktree: bool,
) -> Result<(), String> { ) -> Result<(), String> {
let repo_status = repo_handle.status(is_worktree)?; 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(?) // Don't return table, return a type that implements Display(?)
pub fn get_worktree_status_table( pub fn get_worktree_status_table(
repo: &crate::Repo, repo: &crate::RepoHandle,
directory: &Path, directory: &Path,
) -> Result<(impl std::fmt::Display, Vec<String>), String> { ) -> Result<(impl std::fmt::Display, Vec<String>), String> {
let worktrees = repo.get_worktrees()?; let worktrees = repo.get_worktrees()?;
@@ -111,7 +111,7 @@ pub fn get_worktree_status_table(
for worktree in &worktrees { for worktree in &worktrees {
let worktree_dir = &directory.join(&worktree.name()); let worktree_dir = &directory.join(&worktree.name());
if worktree_dir.exists() { 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, Ok(repo) => repo,
Err(error) => { Err(error) => {
errors.push(format!( 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!( errors.push(format!(
"Found {}, which is not a valid worktree directory!", "Found {}, which is not a valid worktree directory!",
&worktree &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> { pub fn get_status_table(config: crate::Config) -> Result<(Vec<Table>, Vec<String>), String> {
let mut errors = Vec::new(); let mut errors = Vec::new();
let mut tables = 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 repos = tree.repos.unwrap_or_default();
let root_path = crate::expand_path(Path::new(&tree.root)); 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; 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 { let repo_handle = match repo_handle {
Ok(repo) => repo, Ok(repo) => repo,
@@ -207,7 +207,7 @@ fn add_worktree_table_header(table: &mut Table) {
fn add_worktree_status( fn add_worktree_status(
table: &mut Table, table: &mut Table,
worktree: &crate::repo::Worktree, worktree: &crate::repo::Worktree,
repo: &crate::Repo, repo: &crate::RepoHandle,
) -> Result<(), String> { ) -> Result<(), String> {
let repo_status = repo.status(false)?; let repo_status = repo.status(false)?;
@@ -272,10 +272,10 @@ pub fn show_single_repo_status(
let mut table = Table::new(); let mut table = Table::new();
let mut warnings = Vec::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); 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 let Err(error) = repo_handle {
if error.kind == crate::RepoErrorKind::NotFound { if error.kind == crate::RepoErrorKind::NotFound {

View File

@@ -8,13 +8,13 @@ use helpers::*;
fn open_empty_repo() { fn open_empty_repo() {
let tmpdir = init_tmpdir(); let tmpdir = init_tmpdir();
assert!(matches!( assert!(matches!(
Repo::open(tmpdir.path(), true), RepoHandle::open(tmpdir.path(), true),
Err(RepoError { Err(RepoError {
kind: RepoErrorKind::NotFound kind: RepoErrorKind::NotFound
}) })
)); ));
assert!(matches!( assert!(matches!(
Repo::open(tmpdir.path(), false), RepoHandle::open(tmpdir.path(), false),
Err(RepoError { Err(RepoError {
kind: RepoErrorKind::NotFound kind: RepoErrorKind::NotFound
}) })
@@ -25,7 +25,7 @@ fn open_empty_repo() {
#[test] #[test]
fn create_repo() -> Result<(), Box<dyn std::error::Error>> { fn create_repo() -> Result<(), Box<dyn std::error::Error>> {
let tmpdir = init_tmpdir(); 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_bare());
assert!(repo.is_empty()?); assert!(repo.is_empty()?);
cleanup_tmpdir(tmpdir); cleanup_tmpdir(tmpdir);
@@ -35,7 +35,7 @@ fn create_repo() -> Result<(), Box<dyn std::error::Error>> {
#[test] #[test]
fn create_repo_with_worktree() -> Result<(), Box<dyn std::error::Error>> { fn create_repo_with_worktree() -> Result<(), Box<dyn std::error::Error>> {
let tmpdir = init_tmpdir(); 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_bare());
assert!(repo.is_empty()?); assert!(repo.is_empty()?);
cleanup_tmpdir(tmpdir); cleanup_tmpdir(tmpdir);