diff options
Diffstat (limited to 'src/bin/ngit/sub_commands')
| -rw-r--r-- | src/bin/ngit/sub_commands/sync.rs | 115 |
1 files changed, 111 insertions, 4 deletions
diff --git a/src/bin/ngit/sub_commands/sync.rs b/src/bin/ngit/sub_commands/sync.rs index 5578a0f..1a2d4ee 100644 --- a/src/bin/ngit/sub_commands/sync.rs +++ b/src/bin/ngit/sub_commands/sync.rs | |||
| @@ -1,14 +1,19 @@ | |||
| 1 | use std::collections::HashMap; | ||
| 2 | |||
| 1 | use anyhow::{Context, Result, bail}; | 3 | use anyhow::{Context, Result, bail}; |
| 4 | use console::Term; | ||
| 2 | use ngit::{ | 5 | use ngit::{ |
| 3 | client::{ | 6 | client::{ |
| 4 | Client, Connect, Params, fetching_with_report, get_repo_ref_from_cache, | 7 | Client, Connect, Params, fetching_with_report, get_repo_ref_from_cache, |
| 5 | get_state_from_cache, | 8 | get_state_from_cache, |
| 6 | }, | 9 | }, |
| 7 | git::{Repo, RepoActions}, | 10 | fetch::fetch_from_git_server, |
| 11 | git::{Repo, RepoActions, nostr_url::NostrUrlDecoded}, | ||
| 8 | list::{get_ahead_behind, list_from_remotes}, | 12 | list::{get_ahead_behind, list_from_remotes}, |
| 9 | push::push_to_remote, | 13 | push::push_to_remote, |
| 10 | repo_ref::get_repo_coordinates_when_remote_unknown, | 14 | repo_ref::{get_repo_coordinates_when_remote_unknown, is_grasp_server_clone_url}, |
| 11 | utils::get_short_git_server_name, | 15 | repo_state::RepoState, |
| 16 | utils::{get_short_git_server_name, join_with_and}, | ||
| 12 | }; | 17 | }; |
| 13 | 18 | ||
| 14 | #[derive(Debug, clap::Args)] | 19 | #[derive(Debug, clap::Args)] |
| @@ -75,10 +80,13 @@ pub async fn launch(args: &SubCommandArgs) -> Result<()> { | |||
| 75 | &term, | 80 | &term, |
| 76 | &git_repo, | 81 | &git_repo, |
| 77 | &repo_ref.git_server, | 82 | &repo_ref.git_server, |
| 78 | &repo_ref.to_nostr_git_url(&None), | 83 | &decoded_nostr_url, |
| 79 | &repo_ref.grasp_servers(), | 84 | &repo_ref.grasp_servers(), |
| 80 | ); | 85 | ); |
| 81 | 86 | ||
| 87 | let missing_refs = | ||
| 88 | fetch_missing_refs(&git_repo, &nostr_state, &remote_states, &decoded_nostr_url); | ||
| 89 | |||
| 82 | for (url, (remote_state, is_grasp_server)) in &remote_states { | 90 | for (url, (remote_state, is_grasp_server)) in &remote_states { |
| 83 | let remote_name = get_short_git_server_name(&git_repo, url); | 91 | let remote_name = get_short_git_server_name(&git_repo, url); |
| 84 | let mut refspecs = vec![]; | 92 | let mut refspecs = vec![]; |
| @@ -116,6 +124,10 @@ pub async fn launch(args: &SubCommandArgs) -> Result<()> { | |||
| 116 | continue; | 124 | continue; |
| 117 | } | 125 | } |
| 118 | } | 126 | } |
| 127 | // skip refs missing locally | ||
| 128 | if missing_refs.contains(nostr_ref_name) { | ||
| 129 | continue; | ||
| 130 | } | ||
| 119 | if invalid_nostr_state_ref(nostr_ref_name) { | 131 | if invalid_nostr_state_ref(nostr_ref_name) { |
| 120 | // ensure nostr_state only supports refs/heads and refs/tags/ | 132 | // ensure nostr_state only supports refs/heads and refs/tags/ |
| 121 | // and not refs/heads/prs/* | 133 | // and not refs/heads/prs/* |
| @@ -209,6 +221,12 @@ pub async fn launch(args: &SubCommandArgs) -> Result<()> { | |||
| 209 | } | 221 | } |
| 210 | } | 222 | } |
| 211 | 223 | ||
| 224 | if !missing_refs.is_empty() { | ||
| 225 | println!( | ||
| 226 | "skipped the following refs as could not find them locally or on any git servers: {}", | ||
| 227 | join_with_and(&missing_refs) | ||
| 228 | ); | ||
| 229 | } | ||
| 212 | Ok(()) | 230 | Ok(()) |
| 213 | } | 231 | } |
| 214 | 232 | ||
| @@ -216,3 +234,92 @@ fn invalid_nostr_state_ref(ref_name: &str) -> bool { | |||
| 216 | ref_name.starts_with("refs/heads/pr/") | 234 | ref_name.starts_with("refs/heads/pr/") |
| 217 | && !(ref_name.starts_with("refs/heads/") || ref_name.starts_with("refs/tags/")) | 235 | && !(ref_name.starts_with("refs/heads/") || ref_name.starts_with("refs/tags/")) |
| 218 | } | 236 | } |
| 237 | |||
| 238 | fn identify_missing_refs(git_repo: &Repo, state: &HashMap<String, String>) -> Vec<String> { | ||
| 239 | let mut missing_oids = vec![]; | ||
| 240 | for tip in state.values() { | ||
| 241 | if let Ok(exist) = git_repo.does_commit_exist(tip) { | ||
| 242 | if !exist { | ||
| 243 | missing_oids.push(tip.to_string()); | ||
| 244 | } | ||
| 245 | } | ||
| 246 | } | ||
| 247 | missing_oids | ||
| 248 | } | ||
| 249 | |||
| 250 | /// returns refs that are still missing | ||
| 251 | fn fetch_missing_refs( | ||
| 252 | git_repo: &Repo, | ||
| 253 | nostr_state: &RepoState, | ||
| 254 | remote_states: &HashMap<String, (HashMap<String, String>, bool)>, | ||
| 255 | nostr_url_decoded: &NostrUrlDecoded, | ||
| 256 | ) -> Vec<String> { | ||
| 257 | let mut tried_remotes: Vec<String> = vec![]; | ||
| 258 | let required_oids = identify_missing_refs(git_repo, &nostr_state.state); | ||
| 259 | if !required_oids.is_empty() { | ||
| 260 | println!("fetching git data missing locally"); | ||
| 261 | } | ||
| 262 | loop { | ||
| 263 | let required_oids = identify_missing_refs(git_repo, &nostr_state.state); | ||
| 264 | let mut oids_on_remote: HashMap<String, Vec<String>> = HashMap::new(); | ||
| 265 | if !required_oids.is_empty() { | ||
| 266 | for (url, (state, _)) in remote_states { | ||
| 267 | if tried_remotes.contains(url) { | ||
| 268 | continue; | ||
| 269 | } | ||
| 270 | for oid in &required_oids { | ||
| 271 | if state.values().any(|v| v.eq(oid)) { | ||
| 272 | oids_on_remote | ||
| 273 | .entry(url.to_string()) | ||
| 274 | .or_default() | ||
| 275 | .push(oid.clone()); | ||
| 276 | } | ||
| 277 | } | ||
| 278 | } | ||
| 279 | } | ||
| 280 | if let Some((url, oids)) = oids_on_remote.iter().max_by_key(|(url, vec)| { | ||
| 281 | if tried_remotes.contains(url) { | ||
| 282 | 0 | ||
| 283 | } else { | ||
| 284 | vec.len() | ||
| 285 | } | ||
| 286 | }) { | ||
| 287 | if oids.is_empty() || tried_remotes.contains(url) { | ||
| 288 | break; | ||
| 289 | } | ||
| 290 | tried_remotes.push(url.clone()); | ||
| 291 | let _ = fetch_from_git_server( | ||
| 292 | git_repo, | ||
| 293 | oids, | ||
| 294 | url, | ||
| 295 | nostr_url_decoded, | ||
| 296 | &Term::stdout(), | ||
| 297 | is_grasp_server_clone_url(url), | ||
| 298 | ); | ||
| 299 | } else { | ||
| 300 | break; | ||
| 301 | } | ||
| 302 | } | ||
| 303 | |||
| 304 | let still_missing_oids = identify_missing_refs(git_repo, &nostr_state.state); | ||
| 305 | if still_missing_oids.is_empty() { | ||
| 306 | vec![] | ||
| 307 | } else { | ||
| 308 | let missing_refs: Vec<String> = nostr_state | ||
| 309 | .state | ||
| 310 | .iter() | ||
| 311 | .filter_map(|(key, value)| { | ||
| 312 | if still_missing_oids.contains(value) { | ||
| 313 | Some(key.clone()) | ||
| 314 | } else { | ||
| 315 | None | ||
| 316 | } | ||
| 317 | }) | ||
| 318 | .collect(); | ||
| 319 | println!( | ||
| 320 | "could not find refs on repo git servers: {}", | ||
| 321 | join_with_and(&missing_refs) | ||
| 322 | ); | ||
| 323 | missing_refs | ||
| 324 | } | ||
| 325 | } | ||