upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2024-08-02 16:29:46 +0100
committerDanConwayDev <DanConwayDev@protonmail.com>2024-08-02 16:29:46 +0100
commit2014d1990081c4e802bf9a68e9a55c79faaa6328 (patch)
tree7ffd0edbf808c8401f61518b670c511cfa5cac9b
parent098bb88b4f313d9a0b32808b42f4b4c17e1771a2 (diff)
feat(remote): warn when out of sync
with git servers. this will also form the basis of preventing failed pushes
-rw-r--r--src/git_remote_helper.rs113
1 files changed, 91 insertions, 22 deletions
diff --git a/src/git_remote_helper.rs b/src/git_remote_helper.rs
index 2fa1aad..8417415 100644
--- a/src/git_remote_helper.rs
+++ b/src/git_remote_helper.rs
@@ -213,38 +213,100 @@ async fn fetching_with_report_for_helper(
213 Ok(()) 213 Ok(())
214} 214}
215 215
216async fn list(git_repo: &Repo, repo_ref: &RepoRef, for_push: bool) -> Result<()> { 216async fn list(
217 if let Ok(repo_state) = get_state_from_cache(git_repo.get_path()?, repo_ref).await { 217 git_repo: &Repo,
218 for (name, value) in &repo_state.state { 218 repo_ref: &RepoRef,
219 if value.starts_with("ref: ") { 219 for_push: bool,
220 if !for_push { 220) -> Result<HashMap<String, HashMap<String, String>>> {
221 println!("{} {name}", value.replace("ref: ", "@")); 221 let nostr_state =
222 if let Ok(nostr_state) = get_state_from_cache(git_repo.get_path()?, repo_ref).await {
223 Some(nostr_state)
224 } else {
225 None
226 };
227
228 let term = console::Term::stderr();
229
230 let mut remote_states = HashMap::new();
231 for url in &repo_ref.git_server {
232 match list_from_remote(git_repo, url) {
233 Ok(remote_state) => {
234 remote_states.insert(url.clone(), remote_state);
235 }
236 Err(error) => {
237 term.write_line(
238 format!("WARNING: failed to list refs git server in nostr events from {url} error: {error}").as_str(),
239 )?;
240 }
241 }
242 }
243
244 let state = if let Some(nostr_state) = nostr_state {
245 let term = console::Term::stderr();
246 for (name, value) in &nostr_state.state {
247 for (url, remote_state) in &remote_states {
248 if let Some(remote_value) = remote_state.get(name) {
249 if value.ne(remote_value) {
250 term.write_line(
251 format!(
252 "WARNING: git server {url} is out of sync with nostr for {name}"
253 )
254 .as_str(),
255 )?;
256 }
257 } else {
258 term.write_line(
259 format!("WARNING: git server {url} is out of sync with nostr. it is missing {name}. if you are a maintainer consider pushing it.").as_str(),
260 )?;
222 } 261 }
223 } else {
224 println!("{value} {name}");
225 } 262 }
226 } 263 }
264 nostr_state.state
227 } else { 265 } else {
228 let git_server_remote_url = repo_ref 266 repo_ref
229 .git_server 267 .git_server
268 .iter()
269 .filter_map(|server| remote_states.get(server))
270 .cloned()
271 .collect::<Vec<HashMap<String, String>>>()
230 .first() 272 .first()
231 .context("no git server listed in nostr repository announcement")?; 273 .context("failed to get refs from git server")?
232 let mut git_server_remote = git_repo.git_repo.remote_anonymous(git_server_remote_url)?; 274 .clone()
233 git_server_remote.connect(git2::Direction::Fetch)?; 275 };
234 276
235 for head in git_server_remote.list()? { 277 for (name, value) in state {
236 if let Some(symbolic_reference) = head.symref_target() { 278 if value.starts_with("ref: ") {
237 if !for_push { 279 if !for_push {
238 println!("@{} {}", symbolic_reference, head.name()); 280 println!("{} {name}", value.replace("ref: ", "@"));
239 }
240 } else {
241 println!("{} {}", head.oid(), head.name());
242 } 281 }
282 } else {
283 println!("{value} {name}");
243 } 284 }
244 git_server_remote.disconnect()?;
245 } 285 }
286
246 println!(); 287 println!();
247 Ok(()) 288 Ok(remote_states)
289}
290
291fn list_from_remote(
292 git_repo: &Repo,
293 git_server_remote_url: &str,
294) -> Result<HashMap<String, String>> {
295 let mut git_server_remote = git_repo.git_repo.remote_anonymous(git_server_remote_url)?;
296 git_server_remote.connect(git2::Direction::Fetch)?;
297 let mut state = HashMap::new();
298 for head in git_server_remote.list()? {
299 if let Some(symbolic_reference) = head.symref_target() {
300 state.insert(
301 head.name().to_string(),
302 format!("ref: {symbolic_reference}"),
303 );
304 } else {
305 state.insert(head.name().to_string(), head.oid().to_string());
306 }
307 }
308 git_server_remote.disconnect()?;
309 Ok(state)
248} 310}
249 311
250fn fetch(git_repo: &Repository, repo_ref: &RepoRef, stdin: &Stdin, oid: &str) -> Result<()> { 312fn fetch(git_repo: &Repository, repo_ref: &RepoRef, stdin: &Stdin, oid: &str) -> Result<()> {
@@ -290,6 +352,13 @@ async fn push(
290 #[cfg(test)] client: &crate::client::MockConnect, 352 #[cfg(test)] client: &crate::client::MockConnect,
291 #[cfg(not(test))] client: &Client, 353 #[cfg(not(test))] client: &Client,
292) -> Result<()> { 354) -> Result<()> {
355 // TODO check
356 // bail!(
357 // "git server {} tip for branch {} conflicts with nostr and local branch.
358 // to resolve either:\r\n 1. pull from that git server and resolve\r\n 2.
359 // force push your branch to the git server before pushing to nostr remote"
360 // )?;
361
293 // if no state events - create from first git server listed 362 // if no state events - create from first git server listed
294 let refspecs = get_refspecs_from_push_batch(stdin, initial_refspec)?; 363 let refspecs = get_refspecs_from_push_batch(stdin, initial_refspec)?;
295 let git_server_url = repo_ref 364 let git_server_url = repo_ref