From e882fde7d3fc7f2113196585b450f7065061abb9 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Thu, 12 Sep 2024 15:13:50 +0100 Subject: fix(remote): improve `push` status updates to bring them more into line to the native git client --- src/bin/git_remote_nostr/fetch.rs | 84 +++++++++------------------------------ src/bin/git_remote_nostr/push.rs | 23 ++++++++++- src/bin/git_remote_nostr/utils.rs | 82 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 68 deletions(-) diff --git a/src/bin/git_remote_nostr/fetch.rs b/src/bin/git_remote_nostr/fetch.rs index 1b19347..1e38e0e 100644 --- a/src/bin/git_remote_nostr/fetch.rs +++ b/src/bin/git_remote_nostr/fetch.rs @@ -18,6 +18,7 @@ use ngit::{ use crate::utils::{ fetch_or_list_error_is_not_authentication_failure, find_proposal_and_patches_by_branch_name, get_oids_from_fetch_batch, get_open_proposals, get_read_protocols_to_try, join_with_and, + report_on_sideband_progress, report_on_transfer_progress, ProgressStatus, TransferDirection, }; pub async fn run_fetch( @@ -187,31 +188,19 @@ fn fetch_from_git_server_url( let mut fetch_options = git2::FetchOptions::new(); let mut remote_callbacks = git2::RemoteCallbacks::new(); remote_callbacks.sideband_progress(|data| { - if let Ok(data) = str::from_utf8(data) { - let data = data - .split(['\n', '\r']) - .find(|line| !line.is_empty()) - .unwrap_or(""); - if !data.is_empty() { - let s = format!("remote: {data}"); - let _ = term.clear_last_lines(1); - let _ = term.write_line(s.as_str()); - if !s.contains('%') || s.contains("100%") { - // print it twice so the next sideband_progress doesn't delete it - let _ = term.write_line(s.as_str()); - } - } - } + report_on_sideband_progress(data, term); + true + }); + remote_callbacks.transfer_progress(|stats| { + let _ = term.clear_last_lines(1); + report_on_transfer_progress( + &stats, + term, + TransferDirection::Fetch, + ProgressStatus::InProgress, + ); true }); - remote_callbacks.transfer_progress( - #[allow(clippy::cast_precision_loss)] - |stats| { - let _ = term.clear_last_lines(1); - report_on_transfer_progress(&stats, term, false); - true - }, - ); if !dont_authenticate { remote_callbacks.credentials(auth.credentials(&git_config)); @@ -220,50 +209,13 @@ fn fetch_from_git_server_url( term.write_line("")?; git_server_remote.download(oids, Some(&mut fetch_options))?; - report_on_transfer_progress(&git_server_remote.stats(), term, true); + report_on_transfer_progress( + &git_server_remote.stats(), + term, + TransferDirection::Fetch, + ProgressStatus::Complete, + ); git_server_remote.disconnect()?; Ok(()) } - -#[allow(clippy::cast_precision_loss)] -#[allow(clippy::float_cmp)] -fn report_on_transfer_progress(stats: &git2::Progress<'_>, term: &console::Term, complete: bool) { - let total = stats.total_objects() as f64; - if total == 0.0 { - return; - } - let received = stats.received_objects() as f64; - let percentage = (received / total) * 100.0; - - // Get the total received bytes - let received_bytes = stats.received_bytes() as f64; - - // Determine whether to use KiB or MiB - let (size, unit) = if received_bytes >= (1024.0 * 1024.0) { - // Convert to MiB - (received_bytes / (1024.0 * 1024.0), "MiB") - } else { - // Convert to KiB - (received_bytes / 1024.0, "KiB") - }; - - // Format the output for receiving objects - if received < total || complete { - let _ = term.write_line( - format!( - "Receiving objects: {percentage:.0}% ({received}/{total}) {size:.2} {unit}, done.\r" - ) - .as_str(), - ); - } - if received == total || complete { - let indexed_deltas = stats.indexed_deltas() as f64; - let total_deltas = stats.total_deltas() as f64; - let percentage = (indexed_deltas / total_deltas) * 100.0; - let _ = term.write_line( - format!("Resolving deltas: {percentage:.0}% ({indexed_deltas}/{total_deltas}) done.\r") - .as_str(), - ); - } -} diff --git a/src/bin/git_remote_nostr/push.rs b/src/bin/git_remote_nostr/push.rs index 7f63eb0..5a19bb0 100644 --- a/src/bin/git_remote_nostr/push.rs +++ b/src/bin/git_remote_nostr/push.rs @@ -38,7 +38,8 @@ use crate::{ utils::{ find_proposal_and_patches_by_branch_name, get_all_proposals, get_remote_name_by_url, get_short_git_server_name, get_write_protocols_to_try, join_with_and, - push_error_is_not_authentication_failure, read_line, + push_error_is_not_authentication_failure, read_line, report_on_sideband_progress, + report_on_transfer_progress, ProgressStatus, TransferDirection, }, }; @@ -355,7 +356,11 @@ fn push_to_remote( for protocol in &protocols_to_attempt { term.write_line( - format!("fetching {} ref list over {protocol}...", server_url.short_name(),).as_str(), + format!( + "fetching {} ref list over {protocol}...", + server_url.short_name(), + ) + .as_str(), )?; let formatted_url = server_url.format_as(protocol, &decoded_nostr_url.user)?; @@ -419,6 +424,20 @@ fn push_to_remote_url( } Ok(()) }); + remote_callbacks.transfer_progress(|stats| { + let _ = term.clear_last_lines(1); + report_on_transfer_progress( + &stats, + term, + TransferDirection::Push, + ProgressStatus::InProgress, + ); + true + }); + remote_callbacks.sideband_progress(|data| { + report_on_sideband_progress(data, term); + true + }); push_options.remote_callbacks(remote_callbacks); git_server_remote.push(remote_refspecs, Some(&mut push_options))?; let _ = git_server_remote.disconnect(); diff --git a/src/bin/git_remote_nostr/utils.rs b/src/bin/git_remote_nostr/utils.rs index 9be2580..1d65e3c 100644 --- a/src/bin/git_remote_nostr/utils.rs +++ b/src/bin/git_remote_nostr/utils.rs @@ -1,3 +1,4 @@ +use core::str; use std::{ collections::HashMap, io::{self, Stdin}, @@ -307,6 +308,87 @@ pub fn error_might_be_authentication_related(error: &anyhow::Error) -> bool { false } +pub enum TransferDirection { + Fetch, + Push, +} + +pub enum ProgressStatus { + InProgress, + Complete, +} + +#[allow(clippy::cast_precision_loss)] +#[allow(clippy::float_cmp)] +#[allow(clippy::needless_pass_by_value)] +pub fn report_on_transfer_progress( + progress_stats: &git2::Progress<'_>, + term: &console::Term, + direction: TransferDirection, + status: ProgressStatus, +) { + let total = progress_stats.total_objects() as f64; + if total == 0.0 { + return; + } + let received = progress_stats.received_objects() as f64; + let percentage = (received / total) * 100.0; + + // Get the total received bytes + let received_bytes = progress_stats.received_bytes() as f64; + + // Determine whether to use KiB or MiB + let (size, unit) = if received_bytes >= (1024.0 * 1024.0) { + // Convert to MiB + (received_bytes / (1024.0 * 1024.0), "MiB") + } else { + // Convert to KiB + (received_bytes / 1024.0, "KiB") + }; + + // Format the output for receiving objects + if received < total || matches!(status, ProgressStatus::Complete) { + let _ = term.write_line( + format!( + "{} objects: {percentage:.0}% ({received}/{total}) {size:.2} {unit}, done.\r", + if matches!(direction, TransferDirection::Fetch) { + "Receiving" + } else { + "Writing" + }, + ) + .as_str(), + ); + } + if received == total || matches!(status, ProgressStatus::Complete) { + let indexed_deltas = progress_stats.indexed_deltas() as f64; + let total_deltas = progress_stats.total_deltas() as f64; + let percentage = (indexed_deltas / total_deltas) * 100.0; + let _ = term.write_line( + format!("Resolving deltas: {percentage:.0}% ({indexed_deltas}/{total_deltas}) done.\r") + .as_str(), + ); + } +} + +pub fn report_on_sideband_progress(data: &[u8], term: &console::Term) { + if let Ok(data) = str::from_utf8(data) { + let data = data + .split(['\n', '\r']) + .find(|line| !line.is_empty()) + .unwrap_or(""); + if !data.is_empty() { + let s = format!("remote: {data}"); + let _ = term.clear_last_lines(1); + let _ = term.write_line(s.as_str()); + if !s.contains('%') || s.contains("100%") { + // print it twice so the next sideband_progress doesn't delete it + let _ = term.write_line(s.as_str()); + } + } + } +} + #[cfg(test)] mod tests { use super::*; -- cgit v1.2.3