git: Work around windows command length limit message fetching (#39115)
Release Notes: - Fix git blame failing on windows for files with lots of blame entries
This commit is contained in:
+36
-17
@@ -8,34 +8,53 @@ pub async fn get_messages(working_directory: &Path, shas: &[Oid]) -> Result<Hash
|
||||
return Ok(HashMap::default());
|
||||
}
|
||||
|
||||
const MARKER: &str = "<MARKER>";
|
||||
let output = if cfg!(windows) {
|
||||
// Windows has a maximum invocable command length, so we chunk the input.
|
||||
// Actual max is 32767, but we leave some room for the rest of the command as we aren't in precise control of what std might do here
|
||||
const MAX_CMD_LENGTH: usize = 30000;
|
||||
// 40 bytes of hash, 2 quotes and a separating space
|
||||
const SHA_LENGTH: usize = 40 + 2 + 1;
|
||||
const MAX_ENTRIES_PER_INVOCATION: usize = MAX_CMD_LENGTH / SHA_LENGTH;
|
||||
|
||||
let output = util::command::new_smol_command("git")
|
||||
.current_dir(working_directory)
|
||||
let mut result = vec![];
|
||||
for shas in shas.chunks(MAX_ENTRIES_PER_INVOCATION) {
|
||||
let partial = get_messages_impl(working_directory, shas).await?;
|
||||
result.extend(partial);
|
||||
}
|
||||
result
|
||||
} else {
|
||||
get_messages_impl(working_directory, shas).await?
|
||||
};
|
||||
|
||||
Ok(shas
|
||||
.iter()
|
||||
.cloned()
|
||||
.zip(output)
|
||||
.collect::<HashMap<Oid, String>>())
|
||||
}
|
||||
|
||||
async fn get_messages_impl(working_directory: &Path, shas: &[Oid]) -> Result<Vec<String>> {
|
||||
const MARKER: &str = "<MARKER>";
|
||||
let mut cmd = util::command::new_smol_command("git");
|
||||
cmd.current_dir(working_directory)
|
||||
.arg("show")
|
||||
.arg("-s")
|
||||
.arg(format!("--format=%B{}", MARKER))
|
||||
.args(shas.iter().map(ToString::to_string))
|
||||
.args(shas.iter().map(ToString::to_string));
|
||||
let output = cmd
|
||||
.output()
|
||||
.await
|
||||
.context("starting git blame process")?;
|
||||
|
||||
.with_context(|| format!("starting git blame process: {:?}", cmd))?;
|
||||
anyhow::ensure!(
|
||||
output.status.success(),
|
||||
"'git show' failed with error {:?}",
|
||||
output.status
|
||||
);
|
||||
|
||||
Ok(shas
|
||||
.iter()
|
||||
.cloned()
|
||||
.zip(
|
||||
String::from_utf8_lossy(&output.stdout)
|
||||
.trim()
|
||||
.split_terminator(MARKER)
|
||||
.map(|str| str.trim().replace("<", "<").replace(">", ">")),
|
||||
)
|
||||
.collect::<HashMap<Oid, String>>())
|
||||
Ok(String::from_utf8_lossy(&output.stdout)
|
||||
.trim()
|
||||
.split_terminator(MARKER)
|
||||
.map(|str| str.trim().replace("<", "<").replace(">", ">"))
|
||||
.collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
/// Parse the output of `git diff --name-status -z`
|
||||
|
||||
Reference in New Issue
Block a user