1 use super::{stash::is_stash_commit, utils::repo, CommitId};
2 use crate::{error::Result, StatusItem, StatusItemType};
3 use git2::{Diff, DiffDelta, DiffOptions, Repository};
4 use scopetime::scope_time;
5
6 /// get all files that are part of a commit
get_commit_files( repo_path: &str, id: CommitId, ) -> Result<Vec<StatusItem>>7 pub fn get_commit_files(
8 repo_path: &str,
9 id: CommitId,
10 ) -> Result<Vec<StatusItem>> {
11 scope_time!("get_commit_files");
12
13 let repo = repo(repo_path)?;
14
15 let diff = get_commit_diff(&repo, id, None)?;
16
17 let mut res = Vec::new();
18
19 diff.foreach(
20 &mut |delta: DiffDelta<'_>, _progress| {
21 res.push(StatusItem {
22 path: delta
23 .new_file()
24 .path()
25 .map(|p| p.to_str().unwrap_or("").to_string())
26 .unwrap_or_default(),
27 status: StatusItemType::from(delta.status()),
28 });
29 true
30 },
31 None,
32 None,
33 None,
34 )?;
35
36 Ok(res)
37 }
38
39 ///
get_commit_diff( repo: &Repository, id: CommitId, pathspec: Option<String>, ) -> Result<Diff<'_>>40 pub(crate) fn get_commit_diff(
41 repo: &Repository,
42 id: CommitId,
43 pathspec: Option<String>,
44 ) -> Result<Diff<'_>> {
45 // scope_time!("get_commit_diff");
46
47 let commit = repo.find_commit(id.into())?;
48 let commit_tree = commit.tree()?;
49 let parent = if commit.parent_count() > 0 {
50 Some(repo.find_commit(commit.parent_id(0)?)?.tree()?)
51 } else {
52 None
53 };
54
55 let mut opt = pathspec.as_ref().map(|p| {
56 let mut opts = DiffOptions::new();
57 opts.pathspec(p);
58 opts.show_binary(true);
59 opts
60 });
61
62 let mut diff = repo.diff_tree_to_tree(
63 parent.as_ref(),
64 Some(&commit_tree),
65 opt.as_mut(),
66 )?;
67
68 if is_stash_commit(
69 repo.path().to_str().expect("repo path utf8 err"),
70 &id,
71 )? {
72 if let Ok(untracked_commit) = commit.parent_id(2) {
73 let untracked_diff = get_commit_diff(
74 repo,
75 CommitId::new(untracked_commit),
76 pathspec,
77 )?;
78
79 diff.merge(&untracked_diff)?;
80 }
81 }
82
83 Ok(diff)
84 }
85
86 #[cfg(test)]
87 mod tests {
88 use super::get_commit_files;
89 use crate::{
90 error::Result,
91 sync::{
92 commit, stage_add_file, stash_save,
93 tests::{get_statuses, repo_init},
94 },
95 StatusItemType,
96 };
97 use std::{fs::File, io::Write, path::Path};
98
99 #[test]
test_smoke() -> Result<()>100 fn test_smoke() -> Result<()> {
101 let file_path = Path::new("file1.txt");
102 let (_td, repo) = repo_init()?;
103 let root = repo.path().parent().unwrap();
104 let repo_path = root.as_os_str().to_str().unwrap();
105
106 File::create(&root.join(file_path))?
107 .write_all(b"test file1 content")?;
108
109 stage_add_file(repo_path, file_path)?;
110
111 let id = commit(repo_path, "commit msg")?;
112
113 let diff = get_commit_files(repo_path, id)?;
114
115 assert_eq!(diff.len(), 1);
116 assert_eq!(diff[0].status, StatusItemType::New);
117
118 Ok(())
119 }
120
121 #[test]
test_stashed_untracked() -> Result<()>122 fn test_stashed_untracked() -> Result<()> {
123 let file_path = Path::new("file1.txt");
124 let (_td, repo) = repo_init()?;
125 let root = repo.path().parent().unwrap();
126 let repo_path = root.as_os_str().to_str().unwrap();
127
128 File::create(&root.join(file_path))?
129 .write_all(b"test file1 content")?;
130
131 let id = stash_save(repo_path, None, true, false)?;
132
133 let diff = get_commit_files(repo_path, id)?;
134
135 assert_eq!(diff.len(), 1);
136 assert_eq!(diff[0].status, StatusItemType::New);
137
138 Ok(())
139 }
140
141 #[test]
test_stashed_untracked_and_modified() -> Result<()>142 fn test_stashed_untracked_and_modified() -> Result<()> {
143 let file_path1 = Path::new("file1.txt");
144 let file_path2 = Path::new("file2.txt");
145 let (_td, repo) = repo_init()?;
146 let root = repo.path().parent().unwrap();
147 let repo_path = root.as_os_str().to_str().unwrap();
148
149 File::create(&root.join(file_path1))?.write_all(b"test")?;
150 stage_add_file(repo_path, file_path1)?;
151 commit(repo_path, "c1")?;
152
153 File::create(&root.join(file_path1))?
154 .write_all(b"modified")?;
155 File::create(&root.join(file_path2))?.write_all(b"new")?;
156
157 assert_eq!(get_statuses(repo_path), (2, 0));
158
159 let id = stash_save(repo_path, None, true, false)?;
160
161 let diff = get_commit_files(repo_path, id)?;
162
163 assert_eq!(diff.len(), 2);
164 assert_eq!(diff[0].status, StatusItemType::Modified);
165 assert_eq!(diff[1].status, StatusItemType::New);
166
167 Ok(())
168 }
169 }
170