Async implementation of the pull command. This is the main entry point for the pull operation. It coordinates all the steps required to download and apply remote changes.
(&self)
| 478 | /// This is the main entry point for the pull operation. It coordinates |
| 479 | /// all the steps required to download and apply remote changes. |
| 480 | async fn run_async(&self) -> CliResult<()> { |
| 481 | // Find and open repository |
| 482 | let repo_root = find_repository_root()?; |
| 483 | let mut repo = Repository::open(&repo_root).map_err(CliError::Repository)?; |
| 484 | |
| 485 | // Resolve remote name, URL, and identity hint |
| 486 | let (remote_name, remote_url, identity_hint) = self.resolve_remote_url(&repo)?; |
| 487 | |
| 488 | // Determine views. The remote view defaults to the current view; the |
| 489 | // local view defaults to the remote view being pulled, so |
| 490 | // `atomic pull --from-view X` pulls into a local view named `X`. |
| 491 | let remote_view = self.get_remote_view(repo.current_view()); |
| 492 | let local_view = self.get_local_view(&remote_view); |
| 493 | |
| 494 | // Whether the local target view already exists. Creation is deferred |
| 495 | // until the remote manifest is known (below), so a pulled draft can be |
| 496 | // created with the remote's identity (scope + parent) — inheriting its |
| 497 | // parent's history — instead of a flat shared view. |
| 498 | let local_view_exists = repo |
| 499 | .view_exists(&local_view) |
| 500 | .map_err(CliError::Repository)?; |
| 501 | |
| 502 | // Print header |
| 503 | println!( |
| 504 | "Pulling from {} ({})", |
| 505 | style_view(&remote_name), |
| 506 | hint(&remote_url) |
| 507 | ); |
| 508 | |
| 509 | // Connect to remote |
| 510 | let spinner = create_spinner("Connecting to remote..."); |
| 511 | let config = self |
| 512 | .build_remote_config(&remote_url, identity_hint.as_deref()) |
| 513 | .await; |
| 514 | let remote = HttpRemote::with_config(&remote_url, config).map_err(|e| { |
| 515 | finish_error(&spinner, "Failed to connect"); |
| 516 | convert_remote_error(e, &remote_url) |
| 517 | })?; |
| 518 | finish_success(&spinner, "Connected"); |
| 519 | |
| 520 | // Load the target view's history for display only. Object negotiation is |
| 521 | // graph-wide: `haves` is every change node already registered in the |
| 522 | // common pristine, regardless of which view metadata currently exposes |
| 523 | // it. This prevents the server from resending shared-view nodes when a |
| 524 | // draft's own local log is empty. |
| 525 | let spinner = create_spinner("Loading local history..."); |
| 526 | let local_entries = if local_view_exists { |
| 527 | repo.log(HistoryOptions::new().view(&local_view)) |
| 528 | .map_err(CliError::Repository)? |
| 529 | } else { |
| 530 | Vec::new() |
| 531 | }; |
| 532 | let haves: Vec<String> = repo |
| 533 | .registered_change_hashes() |
| 534 | .map_err(CliError::Repository)? |
| 535 | .into_iter() |
| 536 | .map(|hash| hash.to_base32()) |
| 537 | .collect(); |
no test coverage detected