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)
| 383 | /// This is the main entry point for the pull operation. It coordinates |
| 384 | /// all the steps required to download and apply remote changes. |
| 385 | async fn run_async(&self) -> CliResult<()> { |
| 386 | // Find and open repository |
| 387 | let repo_root = find_repository_root()?; |
| 388 | let mut repo = Repository::open(&repo_root).map_err(CliError::Repository)?; |
| 389 | |
| 390 | // Resolve remote name, URL, and identity hint |
| 391 | let (remote_name, remote_url, identity_hint) = self.resolve_remote_url(&repo)?; |
| 392 | |
| 393 | // Determine views. The remote view defaults to the current view; the |
| 394 | // local view defaults to the remote view being pulled, so |
| 395 | // `atomic pull --from-view X` pulls into a local view named `X`. |
| 396 | let remote_view = self.get_remote_view(repo.current_view()); |
| 397 | let local_view = self.get_local_view(&remote_view); |
| 398 | |
| 399 | // Ensure the local target view exists. When pulling a view that is |
| 400 | // not present locally (a teammate's view, or a `--to-view` naming a |
| 401 | // fresh view), create it so downloaded changes have somewhere to go |
| 402 | // instead of failing to apply with "View not found". A dry run must |
| 403 | // not mutate the repository, so it only records the view's absence. |
| 404 | let mut local_view_exists = repo |
| 405 | .view_exists(&local_view) |
| 406 | .map_err(CliError::Repository)?; |
| 407 | if !local_view_exists && !self.dry_run { |
| 408 | repo.create_shared_view(&local_view) |
| 409 | .map_err(CliError::Repository)?; |
| 410 | local_view_exists = true; |
| 411 | print_info(&format!("Created local view '{}'", local_view)); |
| 412 | } |
| 413 | |
| 414 | // Print header |
| 415 | println!( |
| 416 | "Pulling from {} ({})", |
| 417 | style_view(&remote_name), |
| 418 | hint(&remote_url) |
| 419 | ); |
| 420 | |
| 421 | // Connect to remote |
| 422 | let spinner = create_spinner("Connecting to remote..."); |
| 423 | let config = self |
| 424 | .build_remote_config(&remote_url, identity_hint.as_deref()) |
| 425 | .await; |
| 426 | let remote = HttpRemote::with_config(&remote_url, config).map_err(|e| { |
| 427 | finish_error(&spinner, "Failed to connect"); |
| 428 | convert_remote_error(e, &remote_url) |
| 429 | })?; |
| 430 | finish_success(&spinner, "Connected"); |
| 431 | |
| 432 | // Query remote state |
| 433 | let spinner = create_spinner("Querying remote state..."); |
| 434 | let remote_state = remote.get_state(&remote_view).await.map_err(|e| { |
| 435 | finish_error(&spinner, "Failed to query state"); |
| 436 | convert_remote_error(e, &remote_url) |
| 437 | })?; |
| 438 | finish_success(&spinner, "Got remote state"); |
| 439 | |
| 440 | // Get remote changelist |
| 441 | let spinner = create_spinner("Fetching remote changelist..."); |
| 442 | let remote_from = 0; // Always get full changelist for comparison |
no test coverage detected