upleb.uk

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

summaryrefslogtreecommitdiff
path: root/src/bin/git_remote_nostr/list.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/bin/git_remote_nostr/list.rs')
-rw-r--r--src/bin/git_remote_nostr/list.rs56
1 files changed, 53 insertions, 3 deletions
diff --git a/src/bin/git_remote_nostr/list.rs b/src/bin/git_remote_nostr/list.rs
index 4a7c1ec..a32ed67 100644
--- a/src/bin/git_remote_nostr/list.rs
+++ b/src/bin/git_remote_nostr/list.rs
@@ -4,13 +4,14 @@ use anyhow::{Context, Result};
4use client::get_state_from_cache; 4use client::get_state_from_cache;
5use git::RepoActions; 5use git::RepoActions;
6use ngit::{ 6use ngit::{
7 client::{self, is_verbose}, 7 client::{self, FetchReport, is_verbose},
8 fetch::fetch_from_git_server, 8 fetch::fetch_from_git_server,
9 git::{self}, 9 git::{self},
10 git_events::{KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE, event_to_cover_letter, tag_value}, 10 git_events::{KIND_PULL_REQUEST, KIND_PULL_REQUEST_UPDATE, event_to_cover_letter, tag_value},
11 list::list_from_remotes, 11 list::list_from_remotes,
12 login::get_curent_user, 12 login::get_curent_user,
13 repo_ref::{self}, 13 repo_ref::{self},
14 repo_state::RepoState,
14 utils::{get_all_proposals, get_open_or_draft_proposals}, 15 utils::{get_all_proposals, get_open_or_draft_proposals},
15}; 16};
16use repo_ref::RepoRef; 17use repo_ref::RepoRef;
@@ -22,6 +23,7 @@ pub async fn run_list(
22 git_repo: &Repo, 23 git_repo: &Repo,
23 repo_ref: &RepoRef, 24 repo_ref: &RepoRef,
24 for_push: bool, 25 for_push: bool,
26 fetch_report: &FetchReport,
25) -> Result<HashMap<String, (HashMap<String, String>, bool)>> { 27) -> Result<HashMap<String, (HashMap<String, String>, bool)>> {
26 let nostr_state = (get_state_from_cache(Some(git_repo.get_path()?), repo_ref).await).ok(); 28 let nostr_state = (get_state_from_cache(Some(git_repo.get_path()?), repo_ref).await).ok();
27 29
@@ -30,6 +32,8 @@ pub async fn run_list(
30 if is_verbose() { 32 if is_verbose() {
31 term.write_line("git servers: listing refs...")?; 33 term.write_line("git servers: listing refs...")?;
32 } 34 }
35 // nostr_state is passed to list_from_remotes only for the sync-status
36 // display; the actual ref state we advertise is determined below.
33 let remote_states = list_from_remotes( 37 let remote_states = list_from_remotes(
34 &term, 38 &term,
35 git_repo, 39 git_repo,
@@ -39,9 +43,55 @@ pub async fn run_list(
39 ) 43 )
40 .await; 44 .await;
41 45
42 let mut state = if let Some(nostr_state) = nostr_state { 46 // Collect all OIDs confirmed present on at least one git server.
43 nostr_state.state 47 let git_server_oids: std::collections::HashSet<String> = remote_states
48 .values()
49 .flat_map(|(state, _)| state.values())
50 .filter(|v| !v.starts_with("ref: "))
51 .cloned()
52 .collect();
53
54 // From the per-relay state events captured during the nostr fetch, find
55 // the newest state event whose every OID is either:
56 // (a) confirmed present on at least one git server, or
57 // (b) already available locally.
58 // This prevents advertising refs whose git objects haven't been pushed to
59 // any server yet, which would cause `git clone` / `git fetch` to fail.
60 let mut candidates: Vec<&nostr::Event> = fetch_report
61 .state_per_relay
62 .values()
63 .filter_map(|maybe| maybe.as_ref())
64 .collect();
65 // Sort newest-first (by created_at, then by id for tie-breaking).
66 candidates.sort_by(|a, b| {
67 b.created_at
68 .cmp(&a.created_at)
69 .then_with(|| b.id.cmp(&a.id))
70 });
71 // Deduplicate by event id so we don't check the same event twice.
72 candidates.dedup_by_key(|e| e.id);
73
74 let best_state: Option<HashMap<String, String>> = candidates.into_iter().find_map(|event| {
75 if let Ok(rs) = RepoState::try_from(vec![event.clone()]) {
76 let all_resolvable = rs.state.values().all(|v| {
77 v.starts_with("ref: ")
78 || git_server_oids.contains(v)
79 || git_repo.does_commit_exist(v).is_ok_and(|exists| exists)
80 });
81 if all_resolvable { Some(rs.state) } else { None }
82 } else {
83 None
84 }
85 });
86
87 let mut state = if let Some(state) = best_state {
88 state
44 } else { 89 } else {
90 // No relay returned a state event whose OIDs are all resolvable
91 // (either no state events were seen on any relay, or every candidate
92 // references git objects not yet on any server). Fall back to
93 // whatever the git servers actually report so we never advertise OIDs
94 // that cannot be fetched.
45 let (state, _is_grasp_server) = repo_ref 95 let (state, _is_grasp_server) = repo_ref
46 .git_server 96 .git_server
47 .iter() 97 .iter()