git_revise/git/
diff.rs

1// use std::cell::RefCell;
2
3// pub trait GitDiff {
4//     fn git_diff(
5//         repo: &git2::Repository,
6//         exclude_files: &[String],
7//     ) -> crate::error::ReviseResult<String> {
8//         let mut opts = git2::DiffOptions::new();
9
10//         // 设置 diff 选项以只包含暂存区的更改
11//         opts.include_untracked(false)
12//             .show_untracked_content(false)
13//             .include_ignored(false);
14
15//         // 获取 HEAD 指向的 tree
16//         let head = repo.head()?.peel_to_tree()?;
17
18//         // 获取暂存区的 index
19//         let index = repo.index()?;
20
21//         // 比较 HEAD 和暂存区的 index
22//         let diff = repo.diff_tree_to_index(
23//             Some(&head),
24//             Some(&index),
25//             Some(&mut opts),
26//         )?;
27
28//         let content = RefCell::new(String::new());
29//         let current_file_excluded = RefCell::new(false);
30
31//         diff.foreach(
32//             &mut |delta, _| {
33//                 if let Some(path) = delta.new_file().path() {
34//                     if let Some(file_name) = path.file_name() {
35//                         if let Some(file_name_str) = file_name.to_str() {
36//                             *current_file_excluded.borrow_mut() =
37// exclude_files
38// .contains(&file_name_str.to_string());                         }
39//                     }
40//                 }
41//                 true // 总是返回 true 以继续处理下一个文件
42//             },
43//             None,
44//             None,
45//             Some(&mut |_, _, line| {
46//                 if !*current_file_excluded.borrow() {
47//                     let prefix = match line.origin() {
48//                         '+' => "+",
49//                         '-' => "-",
50//                         _ => " ",
51//                     };
52//                     content.borrow_mut().push_str(prefix);
53//                     content
54//                         .borrow_mut()
55//                         .push_str(&String::from_utf8_lossy(line.content()));
56//                 }
57//                 true // 总是返回 true 以继续处理下一行
58//             }),
59//         )?;
60
61//         Ok(content.into_inner())
62//     }
63// }
64
65// // pub trait GitDiff {
66// //     fn git_diff(repo: &git2::Repository) ->
67// // crate::error::ReviseResult<String> {         let mut opts =
68// // git2::DiffOptions::new(); // 创建一个新的DiffOptions实例
69
70// //         let head = repo.head()?.peel_to_tree()?; // 获取HEAD指向的tree
71// //         let diff = repo.diff_tree_to_workdir(Some(&head), Some(&mut
72// opts))?; // // 获取当前工作目录与HEAD的差异
73
74// //         let mut content = String::new(); // 预分配内存
75
76// //         diff.foreach(
77// //             &mut |_, _| true,
78// //             None,
79// //             None,
80// //             Some(&mut |_, _, line| {
81// //                 let prefix = match line.origin() {
82// //                     '+' => "+",
83// //                     '-' => "-",
84// //                     _ => " ",
85// //                 };
86// //                 content.push_str(prefix);
87// //
88// content.push_str(&String::from_utf8_lossy(line.content())); //
89// content.push('\n'); //                 true
90// //             }),
91// //         )?;
92// //         Ok(content)
93// //     }
94// // }
95
96// #[cfg(test)]
97// mod tests {
98//     use super::*;
99
100//     #[test]
101//     fn test_git_diff() {
102//         struct GItDiffImpl;
103//         impl GitDiff for GItDiffImpl {}
104
105//         let _ =
106//             GItDiffImpl::git_diff(&git2::Repository::open(".").unwrap(), &[
107//                 "README.md".to_string(),
108//             ])
109//             .unwrap();
110//     }
111// }
112
113use std::process::Command;
114
115use crate::error::ReviseResult;
116
117pub trait GitDiff {
118    fn git_diff(exclude_files: &[String]) -> ReviseResult<String> {
119        let mut args = vec!["diff", "--staged"];
120
121        let exclude_args: Vec<String> = exclude_files
122            .iter()
123            .map(|file| format!(":!{file}"))
124            .collect();
125        args.extend(exclude_args.iter().map(std::string::String::as_str));
126
127        let mut binding = Command::new("git");
128        let command = binding.args(&args);
129        let output = command.output()?;
130
131        if !output.status.success() {
132            return Err(anyhow::anyhow!(
133                "Git diff failed: {}",
134                String::from_utf8_lossy(&output.stderr)
135            ));
136        }
137        let diff = String::from_utf8(output.stdout)?;
138        Ok(diff)
139    }
140}
141
142#[cfg(test)]
143mod tests {
144    use super::*;
145
146    #[test]
147    #[ignore]
148    fn test_git_diff() {
149        struct GitDiffImpl;
150        impl GitDiff for GitDiffImpl {}
151
152        let diff =
153            GitDiffImpl::git_diff(&["CHANGELOG.md".to_string()]).unwrap();
154        println!("Git diff: {diff}");
155    }
156}