Upload a single change using the threshold-gated delta transfer protocol. - Changes smaller than [`DELTA_TRANSFER_THRESHOLD`]: sent directly (fast path) - Changes larger: manifest exchange → send only missing chunks (delta path) # Arguments `remote` - The HTTP remote to push to. `repo` - The local repository. `hash` - The change hash. `view` - The target view name on the remote. # Returns A [
(
remote: &atomic_remote::HttpRemote,
repo: &Repository,
hash: &Hash,
view: &str,
)
| 301 | /// |
| 302 | /// A [`PushTransferResult`] describing the transfer, or an error. |
| 303 | pub async fn upload_change_smart( |
| 304 | remote: &atomic_remote::HttpRemote, |
| 305 | repo: &Repository, |
| 306 | hash: &Hash, |
| 307 | view: &str, |
| 308 | ) -> CliResult<PushTransferResult> { |
| 309 | let hash_str = hash.to_base32(); |
| 310 | |
| 311 | // Prefer streaming from the on-disk change file for large changes. |
| 312 | // This avoids loading 50MB+ into memory and uses chunked transfer |
| 313 | // encoding so the server can stream-to-disk instead of buffering. |
| 314 | let change_path = repo.change_store().change_path(hash); |
| 315 | let file_size = change_path.metadata().map(|m| m.len()).unwrap_or(0); |
| 316 | |
| 317 | log::debug!( |
| 318 | "push: uploading {} ({} bytes / {}){}", |
| 319 | &hash_str[..12.min(hash_str.len())], |
| 320 | file_size, |
| 321 | format_bytes(file_size), |
| 322 | if file_size as usize >= DELTA_TRANSFER_THRESHOLD { |
| 323 | " [streamed]" |
| 324 | } else { |
| 325 | "" |
| 326 | }, |
| 327 | ); |
| 328 | |
| 329 | // For large changes, stream directly from disk. |
| 330 | if file_size as usize >= DELTA_TRANSFER_THRESHOLD && change_path.exists() { |
| 331 | remote |
| 332 | .upload_change_streamed(&hash_str, view, &change_path) |
| 333 | .await |
| 334 | .map_err(|e| convert_remote_error(e, remote.url().as_ref()))?; |
| 335 | |
| 336 | return Ok(PushTransferResult::direct(file_size)); |
| 337 | } |
| 338 | |
| 339 | // Small changes: load into memory and send directly. |
| 340 | let change_data = load_change_data(repo, hash)?; |
| 341 | let data_len = change_data.len(); |
| 342 | |
| 343 | remote |
| 344 | .upload_change(&hash_str, view, change_data) |
| 345 | .await |
| 346 | .map_err(|e| convert_remote_error(e, remote.url().as_ref()))?; |
| 347 | |
| 348 | Ok(PushTransferResult::direct(data_len as u64)) |
| 349 | } |
| 350 | |
| 351 | /// Format a byte count for display. |
| 352 | fn format_bytes(bytes: u64) -> String { |
no test coverage detected