Skip to content

Commit

Permalink
Align sideband progress message printing with Git's implementation
Browse files Browse the repository at this point in the history
  • Loading branch information
bnjmnt4n committed Mar 16, 2024
1 parent 5ed17b7 commit ff65558
Showing 1 changed file with 62 additions and 8 deletions.
70 changes: 62 additions & 8 deletions cli/src/git_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@

//! Git utilities shared by various commands.
use std::io::{Read, Write};
use std::io::{IsTerminal, Read, Write};
use std::path::{Path, PathBuf};
use std::process::Stdio;
use std::sync::Mutex;
use std::time::Instant;
use std::{error, iter};

use gix::bstr::ByteSlice;
use itertools::Itertools;
use jj_lib::git::{self, FailedRefExport, FailedRefExportReason, GitImportStats, RefName};
use jj_lib::git_backend::GitBackend;
Expand Down Expand Up @@ -155,16 +156,69 @@ pub fn with_remote_git_callbacks<T>(
callbacks.progress = progress_callback
.as_mut()
.map(|x| x as &mut dyn FnMut(&git::Progress));
// Based on Git's implementation: https://github.com/git/git/blob/43072b4ca132437f21975ac6acc6b72dc22fd398/sideband.c#L178
let mut sideband_progress_callback = None;
let mut scratch = Vec::new();
let display_prefix = "remote: ";
let suffix = if std::io::stdout().is_terminal() {
"\x1B[K"
} else {
" "
};
if print_sideband_messages {
sideband_progress_callback = Some(|progress: &[u8]| {
let message = String::from_utf8_lossy(progress);
if message.contains("\n") {
for line in message.lines() {
_ = writeln!(ui.lock().unwrap().stderr(), "remote: {line}");
sideband_progress_callback = Some(|progress_message: &[u8]| {
let mut index = 0;
// Append a suffix to each nonempty line to clear the end of the screen line.
loop {
let Some(i) = progress_message[index..]
.find_char('\n')
.or_else(|| progress_message[index..].find_char('\r'))
.map(|i| index + i)
else {
break;
};
let line_length = i - index;

// For message across packet boundary, there would have a nonempty "scratch"
// buffer from last call of this function, and there may have a
// leading CR/LF in "buf". For this case we should add a clear-to-eol suffix to
// clean leftover letters we previously have written on the same line.
if !scratch.is_empty() && line_length == 0 {
scratch.extend_from_slice(suffix.as_bytes());
}

if scratch.is_empty() {
scratch.extend_from_slice(display_prefix.as_bytes());
}

// Do not add the clear-to-eol suffix to empty lines:
// For progress reporting we may receive a bunch of percentage updates followed
// by '\r' to remain on the same line, and at the end receive a
// single '\n' to move to the next line. We should preserve the final
// status report line by not appending clear-to-eol suffix to this single line
// break.
if line_length > 0 {
scratch.extend_from_slice(&progress_message[index..i]);
scratch.extend_from_slice(suffix.as_bytes());
}
scratch.extend_from_slice(&progress_message[i..i + 1]);

_ = write!(
ui.lock().unwrap().stderr(),
"{}",
String::from_utf8_lossy(&scratch)
);
scratch.clear();

index = i + 1;
}

// Add leftover message to "scratch" buffer to be printed in next call.
if index < progress_message.len() && progress_message[index] != 0 {
if scratch.is_empty() {
scratch.extend_from_slice(display_prefix.as_bytes());
}
} else {
_ = write!(ui.lock().unwrap().stderr(), "remote: {message}");
scratch.extend_from_slice(&progress_message[index..]);
}
});
}
Expand Down

0 comments on commit ff65558

Please sign in to comment.