From baf959b7a48a07009001717e4874587d2b1ba465 Mon Sep 17 00:00:00 2001 From: Paul Campbell Date: Mon, 27 May 2024 07:33:06 +0100 Subject: [PATCH] WIP: git::remote_branches --- crates/git/src/branch.rs | 6 ++++ crates/git/src/repository/mock.rs | 23 +++++++++----- crates/git/src/repository/open/mod.rs | 1 + crates/git/src/repository/open/oreal.rs | 23 ++++++++++++++ crates/repo-actor/src/load.rs | 42 ++++++++++++++++++++----- 5 files changed, 81 insertions(+), 14 deletions(-) diff --git a/crates/git/src/branch.rs b/crates/git/src/branch.rs index 01978ba..f4533e4 100644 --- a/crates/git/src/branch.rs +++ b/crates/git/src/branch.rs @@ -11,6 +11,12 @@ pub enum Error { Fetch(git::fetch::Error), Push(git::push::Error), + + Lock, + + Generic(String), + I1(gix::reference::iter::Error), + I2(gix::reference::iter::init::Error), } impl std::error::Error for Error {} diff --git a/crates/git/src/repository/mock.rs b/crates/git/src/repository/mock.rs index f4d4f88..da53d19 100644 --- a/crates/git/src/repository/mock.rs +++ b/crates/git/src/repository/mock.rs @@ -15,7 +15,7 @@ use crate::{ }, GitRemote, RepoDetails, }; -use git_next_config::GitDir; +use git_next_config as config; #[derive(Debug, Default, Clone)] pub struct MockRepository(Arc>); @@ -23,7 +23,7 @@ impl MockRepository { pub fn new() -> Self { Self(Arc::new(Mutex::new(Reality::default()))) } - pub fn can_open_repo(&mut self, gitdir: &GitDir) -> Result { + pub fn can_open_repo(&mut self, gitdir: &config::GitDir) -> Result { self.0 .lock() .map_err(|_| Error::MockLock) @@ -31,7 +31,7 @@ impl MockRepository { } fn open_repository( &self, - gitdir: &GitDir, + gitdir: &config::GitDir, ) -> std::result::Result { self.0.lock().map_err(|_| Error::MockLock).and_then(|r| { r.open_repository(gitdir) @@ -47,7 +47,7 @@ impl MockRepository { impl RepositoryLike for MockRepository { fn open( &self, - gitdir: &GitDir, + gitdir: &config::GitDir, ) -> std::result::Result { Ok(OpenRepository::Mock(self.open_repository(gitdir)?)) } @@ -62,10 +62,10 @@ impl RepositoryLike for MockRepository { #[derive(Debug, Default)] pub struct Reality { - openable_repos: HashMap, + openable_repos: HashMap, } impl Reality { - pub fn can_open_repo(&mut self, gitdir: &GitDir) -> MockOpenRepository { + pub fn can_open_repo(&mut self, gitdir: &config::GitDir) -> MockOpenRepository { let mor = self.openable_repos.get(gitdir); match mor { Some(mor) => mor.clone(), @@ -76,7 +76,7 @@ impl Reality { } } } - pub fn open_repository(&self, gitdir: &GitDir) -> Option { + pub fn open_repository(&self, gitdir: &config::GitDir) -> Option { self.openable_repos.get(gitdir).cloned() } } @@ -109,6 +109,12 @@ impl InnerMockOpenRepository { #[allow(clippy::unwrap_used)] impl OpenRepositoryLike for MockOpenRepository { + fn remote_branches(&self) -> git::branch::Result> { + self.inner + .lock() + .map(|inner| inner.remote_branches()) + .unwrap() + } fn find_default_remote(&self, direction: Direction) -> Option { self.inner .lock() @@ -156,6 +162,9 @@ impl OpenRepositoryLike for MockOpenRepository { } } impl OpenRepositoryLike for InnerMockOpenRepository { + fn remote_branches(&self) -> git::branch::Result> { + todo!(); + } fn find_default_remote(&self, direction: Direction) -> Option { match direction { Direction::Push => self.default_push_remote.clone(), diff --git a/crates/git/src/repository/open/mod.rs b/crates/git/src/repository/open/mod.rs index 907b247..4d1c267 100644 --- a/crates/git/src/repository/open/mod.rs +++ b/crates/git/src/repository/open/mod.rs @@ -28,6 +28,7 @@ impl OpenRepository { } } pub trait OpenRepositoryLike { + fn remote_branches(&self) -> git::branch::Result>; fn find_default_remote(&self, direction: Direction) -> Option; fn fetch(&self) -> Result<(), git::fetch::Error>; fn push( diff --git a/crates/git/src/repository/open/oreal.rs b/crates/git/src/repository/open/oreal.rs index 033af33..2f6a500 100644 --- a/crates/git/src/repository/open/oreal.rs +++ b/crates/git/src/repository/open/oreal.rs @@ -1,5 +1,6 @@ // use crate as git; +use config::BranchName; use git_next_config as config; use gix::bstr::BStr; @@ -9,6 +10,28 @@ use tracing::{info, warn}; #[derive(Clone, Debug, derive_more::Constructor)] pub struct RealOpenRepository(Arc>); impl super::OpenRepositoryLike for RealOpenRepository { + fn remote_branches(&self) -> git::branch::Result> { + let refs = self + .0 + .lock() + .map_err(|_| git::branch::Error::Lock) + .and_then(|repo| { + Ok(repo.references()?).and_then(|refs| { + Ok(refs.remote_branches().map(|rb| { + rb.filter_map(|rbi| rbi.ok()) + .map(|r| r.name().to_owned()) + .map(|n| n.to_string()) + .filter_map(|p| { + p.strip_prefix("refs/remotes/origin/").map(|v| v.to_owned()) + }) + .filter(|b| b.as_str() != "HEAD") + .map(BranchName::new) + .collect::>() + })?) + }) + })?; + Ok(refs) + } fn find_default_remote(&self, direction: git::repository::Direction) -> Option { let Ok(repository) = self.0.lock() else { #[cfg(not(tarpaulin_include))] // don't test mutex lock failure diff --git a/crates/repo-actor/src/load.rs b/crates/repo-actor/src/load.rs index c17412b..c158a89 100644 --- a/crates/repo-actor/src/load.rs +++ b/crates/repo-actor/src/load.rs @@ -1,7 +1,7 @@ // use actix::prelude::*; -use tracing::{error, info}; +use tracing::{error, info, warn}; use git_next_config as config; use git_next_forge as forge; @@ -18,7 +18,7 @@ pub async fn load_file( open_repository: git::OpenRepository, ) { info!("Loading .git-next.toml from repo"); - let repo_config = match load(&repo_details, &forge, open_repository).await { + let repo_config = match load(&repo_details, &forge, &open_repository).await { Ok(repo_config) => repo_config, Err(err) => { error!(?err, "Failed to load config"); @@ -32,11 +32,11 @@ pub async fn load_file( async fn load( details: &git::RepoDetails, forge: &forge::Forge, - open_repository: git::OpenRepository, + open_repository: &git::OpenRepository, ) -> Result { let contents = open_repository.read_file(&details.branch, ".git-next.toml")?; let config = config::RepoConfig::load(&contents)?; - let config = validate(config, forge).await?; + let config = validate(config, forge, open_repository).await?; Ok(config) } @@ -45,18 +45,46 @@ pub enum Error { File(git::file::Error), Config(config::server::Error), Toml(toml::de::Error), - Forge(git::branch::Error), + Branch(git::branch::Error), BranchNotFound(config::BranchName), } pub async fn validate( config: config::RepoConfig, forge: &forge::Forge, + open_repository: &git::OpenRepository, ) -> Result { + let new = open_repository.remote_branches(); let branches = forge.branches_get_all().await.map_err(|e| { error!(?e, "Failed to list branches"); - Error::Forge(e) - })?; + Error::Branch(e) + }); + let branches = match (branches, new) { + (Ok(old), Ok(new)) => { + if old == new { + info!("remote_branches old and new match"); + } else { + warn!("remote_branches old and new differ:"); + warn!("old: {old:?}"); + warn!("new: {new:?}"); + } + Ok(old) + } + (Ok(old), Err(new)) => { + warn!("remote_branches old okay, new error: {new:?}"); + Ok(old) + } + (Err(old), Ok(_new)) => { + warn!("remote_branches new okay, old error: {old:?}"); + Err(old) + } + (Err(old), Err(new)) => { + warn!("remote_branches both error:"); + warn!("old: {old:?}"); + warn!("new: {new:?}"); + Err(old) + } + }?; if !branches .iter() .any(|branch| branch == &config.branches().main())