我正在使用
Command
来生成 git diff {HASH} --unified=100000 --no-color --patch-with-raw --exit-code -- {FILE}
。但我的标准输出和标准错误都是空的。我尝试了很多方法来获取标准输出(cli.wait()
,cli.stdout.take()
),但它也没有按预期工作。
pub fn get_commit_file_changes(hash: &str, file: &str) -> Option<String> {
let file_path = format!("\"./{}\"", file);
let mut cli = std::process::Command::new("git")
.args([
"diff",
hash,
"--unified=100000", // to get the whole file
"--no-color",
"--patch-with-raw",
"--exit-code",
"--", // to separate paths from revisions
&file_path,
])
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()
.expect("failed to spawn cmd process");
let output = cli
.wait_with_output()
.expect("failed to wait for child process");
if !output.status.success() {
return None;
}
let stdout = String::from_utf8(output.stdout).unwrap();
let stderr = String::from_utf8(output.stderr).unwrap();
if !stderr.is_empty() {
return None;
}
if stdout.is_empty() {
return None;
}
return Some(stdout.split("\n").skip(4).collect::<Vec<&str>>().join("\n"));
}
我尝试过生成 shell(特定于操作系统),然后执行 git 命令,但它也不起作用。
#[cfg(target_os = "windows")]
const SHELL: &str = "cmd";
#[cfg(not(target_os = "windows"))]
const SHELL: &str = "sh";
#[cfg(target_os = "windows")]
const SHELL_FLAG: &str = "/C";
#[cfg(not(target_os = "windows"))]
const SHELL_FLAG: &str = "-c";
let mut cli = std::process::Command::new(SHELL)
.args([
SHELL_FLAG,
"git",
"diff",
// ...
所需输出
C:\Users\dallen\Desktop\gitty>git diff 76a9cea6bcdfc716a769e75001dadb770023a27f --unified=100000 --no-color --patch-with-raw --exit-code -- "./src-tauri/src/git/commit/mod.rs"
:100644 100644 309edda 0000000 M src-tauri/src/git/commit/mod.rs
diff --git a/src-tauri/src/git/commit/mod.rs b/src-tauri/src/git/commit/mod.rs
index 309edda..ba536fc 100644
--- a/src-tauri/src/git/commit/mod.rs
+++ b/src-tauri/src/git/commit/mod.rs
@@ -1,286 +1,331 @@
-use std::io::Read;
+use std::{
+ io::{BufRead, BufReader, Read},
+ process::Output,
+};
use serde::Serialize;
-use crate::utils::subprocess::create_git_cli;
+use crate::utils::subprocess::{create_git_cli, read_to_string};
#[derive(Serialize)]
pub struct Commit {
pub hash: String,
pub author: String,
pub timestamp: u64,
pub message: String,
pub description: Option<String>,
}
...
问题是由于您在文件路径周围放置的引号造成的。
如果您在 shell 中执行该命令,shell 将使用
"
,您将得到输出:
$ git diff -- "foo"
diff --git a/foo b/foo
index e69de29..5716ca5 100644
--- a/foo
+++ b/foo
@@ -0,0 +1 @@
+bar
如果在 Rust 中执行
Command::new("git").args(r#""foo""##)
,Rust 会将 "
传递给 git
,结果与此等效:
$ git diff -- \"foo\"
因为新文件名不存在,所以
git diff
输出没有差异。
通过相信 Rust 能够正确地转义路径,并通过删除
--exit-code
以确保成功退出代码(尽管存在差异),我们得到了工作代码:
pub fn get_commit_file_changes(hash: &str, file_path: &str) -> Option<String> {
let mut cli = std::process::Command::new("git")
.args([
"diff",
hash,
"--unified=100000", // to get the whole file
"--no-color",
"--patch-with-raw",
"--", // to separate paths from revisions
&file_path,
])
.stdout(std::process::Stdio::piped())
.stderr(std::process::Stdio::piped())
.spawn()
.expect("failed to spawn cmd process");
let output = cli
.wait_with_output()
.expect("failed to wait for child process");
if !output.status.success() {
return None;
}
let stdout = String::from_utf8(output.stdout).unwrap();
let stderr = String::from_utf8(output.stderr).unwrap();
if !stderr.is_empty() {
return None;
}
if stdout.is_empty() {
return None;
}
return Some(stdout.split("\n").skip(4).collect::<Vec<&str>>().join("\n"));
}