Merge branch 'main' into cv-main-xx1jlt

This commit is contained in:
xintaofei
2026-03-15 23:26:27 +08:00

View File

@@ -195,6 +195,7 @@ fn git_command_error(operation: &str, stderr: &[u8]) -> AppCommandError {
async fn detect_conflicts(path: &str) -> Result<Vec<String>, AppCommandError> { async fn detect_conflicts(path: &str) -> Result<Vec<String>, AppCommandError> {
let output = crate::process::tokio_command("git") let output = crate::process::tokio_command("git")
.args(["-c", "core.quotePath=false"])
.args(["diff", "--name-only", "--diff-filter=U"]) .args(["diff", "--name-only", "--diff-filter=U"])
.current_dir(path) .current_dir(path)
.output() .output()
@@ -207,7 +208,7 @@ async fn detect_conflicts(path: &str) -> Result<Vec<String>, AppCommandError> {
Ok(String::from_utf8_lossy(&output.stdout) Ok(String::from_utf8_lossy(&output.stdout)
.lines() .lines()
.map(|l| l.trim().to_string()) .map(|l| unquote_git_path(l))
.filter(|l| !l.is_empty()) .filter(|l| !l.is_empty())
.collect()) .collect())
} }
@@ -1046,6 +1047,7 @@ pub async fn git_stash_show(
stash_ref: String, stash_ref: String,
) -> Result<Vec<GitStatusEntry>, AppCommandError> { ) -> Result<Vec<GitStatusEntry>, AppCommandError> {
let output = crate::process::tokio_command("git") let output = crate::process::tokio_command("git")
.args(["-c", "core.quotePath=false"])
.args(["stash", "show", "--name-status", &stash_ref]) .args(["stash", "show", "--name-status", &stash_ref])
.current_dir(&path) .current_dir(&path)
.output() .output()
@@ -1063,7 +1065,7 @@ pub async fn git_stash_show(
.filter_map(|line| { .filter_map(|line| {
let mut parts = line.splitn(2, '\t'); let mut parts = line.splitn(2, '\t');
let status = parts.next()?.trim().to_string(); let status = parts.next()?.trim().to_string();
let file = parts.next()?.trim().to_string(); let file = unquote_git_path(parts.next()?);
Some(GitStatusEntry { status, file }) Some(GitStatusEntry { status, file })
}) })
.collect(); .collect();
@@ -1074,6 +1076,7 @@ pub async fn git_stash_show(
#[tauri::command] #[tauri::command]
pub async fn git_status(path: String) -> Result<Vec<GitStatusEntry>, AppCommandError> { pub async fn git_status(path: String) -> Result<Vec<GitStatusEntry>, AppCommandError> {
let output = crate::process::tokio_command("git") let output = crate::process::tokio_command("git")
.args(["-c", "core.quotePath=false"])
.args(["status", "--porcelain=v1", "-uall"]) .args(["status", "--porcelain=v1", "-uall"])
.current_dir(&path) .current_dir(&path)
.output() .output()
@@ -1089,7 +1092,7 @@ pub async fn git_status(path: String) -> Result<Vec<GitStatusEntry>, AppCommandE
.filter(|l| !l.is_empty()) .filter(|l| !l.is_empty())
.map(|line| { .map(|line| {
let status = line[..2].trim().to_string(); let status = line[..2].trim().to_string();
let file = line[3..].to_string(); let file = unquote_git_path(&line[3..]);
GitStatusEntry { status, file } GitStatusEntry { status, file }
}) })
.collect(); .collect();
@@ -1837,6 +1840,20 @@ fn to_git_literal_pathspec(path: &str) -> String {
format!(":(literal){path}") format!(":(literal){path}")
} }
/// Remove surrounding quotes from a git output path.
/// Git quotes paths containing non-ASCII or special characters, e.g.
/// `"path/\344\270\255\346\226\207.txt"`. With `core.quotePath=false`
/// the octal escapes are gone, but the quotes may still appear for paths
/// with spaces, tabs, etc.
fn unquote_git_path(path: &str) -> String {
let trimmed = path.trim();
if trimmed.len() >= 2 && trimmed.starts_with('"') && trimmed.ends_with('"') {
trimmed[1..trimmed.len() - 1].to_string()
} else {
trimmed.to_string()
}
}
fn normalize_slash_path(path: &Path) -> String { fn normalize_slash_path(path: &Path) -> String {
path.to_string_lossy().replace('\\', "/") path.to_string_lossy().replace('\\', "/")
} }
@@ -3174,6 +3191,7 @@ pub async fn git_log(
args.push(b.clone()); args.push(b.clone());
} }
let output = crate::process::tokio_command("git") let output = crate::process::tokio_command("git")
.args(["-c", "core.quotePath=false"])
.args(&args) .args(&args)
.current_dir(&path) .current_dir(&path)
.output() .output()
@@ -3367,7 +3385,7 @@ impl GitLogEntryBuilder {
fn parse_raw_file_line(line: &str) -> Option<(String, String)> { fn parse_raw_file_line(line: &str) -> Option<(String, String)> {
let mut parts = line.split('\t'); let mut parts = line.split('\t');
let meta = parts.next()?; let meta = parts.next()?;
let file_path = parts.next()?.to_string(); let file_path = unquote_git_path(parts.next()?);
let status = meta let status = meta
.split_whitespace() .split_whitespace()
.last() .last()
@@ -3381,7 +3399,7 @@ fn parse_numstat_file_line(line: &str) -> Option<(u32, u32, String)> {
let mut parts = line.splitn(3, '\t'); let mut parts = line.splitn(3, '\t');
let additions = parse_numstat_count(parts.next()?); let additions = parse_numstat_count(parts.next()?);
let deletions = parse_numstat_count(parts.next()?); let deletions = parse_numstat_count(parts.next()?);
let file_path = parts.next()?.to_string(); let file_path = unquote_git_path(parts.next()?);
Some((additions, deletions, file_path)) Some((additions, deletions, file_path))
} }