diff options
Diffstat (limited to 'src/bin/git_remote_nostr/list.rs')
| -rw-r--r-- | src/bin/git_remote_nostr/list.rs | 56 |
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}; | |||
| 4 | use client::get_state_from_cache; | 4 | use client::get_state_from_cache; |
| 5 | use git::RepoActions; | 5 | use git::RepoActions; |
| 6 | use ngit::{ | 6 | use 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 | }; |
| 16 | use repo_ref::RepoRef; | 17 | use 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() |