git_quick_add/git/
status.rs1use crate::models::path_items::PathItems;
2use git2::{Repository, Status};
3use std::process;
4
5pub fn get_paths(repo: &Repository) -> Result<Vec<PathItems>, git2::Error> {
7 let statuses = repo.statuses(None)?;
8
9 if statuses.is_empty() {
10 println!("{}", console::style("✔ working tree clean ✔").green());
11 return Ok(vec![]);
12 }
13
14 let mut items: Vec<PathItems> = vec![];
15
16 for diff_entry in statuses.iter() {
17 if diff_entry.status() == Status::IGNORED {
18 continue;
19 }
20
21 let path_items = diff_entry
22 .head_to_index()
24 .and_then(|d| {
26 Some(PathItems {
27 path: String::from(d.new_file().path()?.display().to_string()),
28 is_staged: true,
29 is_selected: false,
30 })
31 })
32 .or_else(|| {
34 Option::from(
35 diff_entry
36 .index_to_workdir()
37 .and_then(|d| {
38 Some(PathItems {
39 path: String::from(d.new_file().path()?.display().to_string()),
40 is_staged: false,
41 is_selected: false,
42 })
43 })
44 .or_else(|| {
47 diff_entry.index_to_workdir().and_then(|d| {
48 Some(PathItems {
49 path: String::from(d.old_file().path()?.display().to_string()),
50 is_staged: false,
51 is_selected: false,
52 })
53 })
54 })
55 .unwrap_or_else(|| PathItems {
57 path: String::from("<unknown>"),
58 is_staged: false,
59 is_selected: false,
60 }),
61 )
62 })
63 .unwrap();
64
65 items.push(path_items);
66 }
67
68 if items.is_empty() {
70 println!("{}", console::style("✔ working tree clean ✔").green());
71 process::exit(1)
72 }
73
74 Ok(items)
75}
76
77#[cfg(test)]
78mod tests {
79 use super::*;
80 use git2::{Oid, Repository, Signature};
81 use std::fs::File;
82 use std::io::Write;
83 use std::path::Path;
84 use tempfile::TempDir;
85
86 fn init_repo() -> (TempDir, Repository) {
88 let tmp_dir = TempDir::new().expect("create temp dir");
89 let repo = Repository::init(tmp_dir.path()).expect("init repo");
90 (tmp_dir, repo)
91 }
92
93 fn commit_file(repo: &Repository, file_path: &str, content: &str, message: &str) -> Oid {
95 let mut file = File::create(repo.workdir().unwrap().join(file_path)).unwrap();
96 file.write_all(content.as_bytes()).unwrap();
97
98 let mut index = repo.index().unwrap();
99 index.add_path(Path::new(file_path)).unwrap();
100 let oid = index.write_tree().unwrap();
101
102 let sig = Signature::now("Test", "test@example.com").unwrap();
103 let tree = repo.find_tree(oid).unwrap();
104
105 let parent_commit = repo
106 .head()
107 .ok()
108 .and_then(|h| h.target())
109 .and_then(|oid| repo.find_commit(oid).ok());
110
111 let commit_oid = if let Some(parent) = parent_commit {
112 repo.commit(Some("HEAD"), &sig, &sig, message, &tree, &[&parent])
113 .unwrap()
114 } else {
115 repo.commit(Some("HEAD"), &sig, &sig, message, &tree, &[])
116 .unwrap()
117 };
118 commit_oid
119 }
120
121 #[test]
122 fn test_get_paths_empty_worktree() {
123 let (_tmp, repo) = init_repo();
124
125 let statuses = get_paths(&repo).unwrap();
127 assert!(statuses.is_empty());
128 }
129
130 #[test]
131 fn test_get_paths_unstaged_file() {
132 let (_tmp, repo) = init_repo();
133
134 let file_path = "foo.txt";
136 let file_full_path = repo.workdir().unwrap().join(file_path);
137 let mut file = File::create(&file_full_path).unwrap();
138 writeln!(file, "hello world").unwrap();
139
140 let paths = get_paths(&repo).unwrap();
142 assert_eq!(paths.len(), 1);
143 let item = &paths[0];
144 assert_eq!(item.path, file_path);
145 assert!(!item.is_staged);
146 assert!(!item.is_selected);
147 }
148
149 #[test]
150 fn test_get_paths_staged_file() {
151 let (_tmp, repo) = init_repo();
152
153 let file_path = "bar.txt";
155 let file_full_path = repo.workdir().unwrap().join(file_path);
156 let mut file = File::create(&file_full_path).unwrap();
157 writeln!(file, "hello staged").unwrap();
158
159 let mut index = repo.index().unwrap();
160 index.add_path(Path::new(file_path)).unwrap();
161 index.write().unwrap();
162
163 let paths = get_paths(&repo).unwrap();
165 assert_eq!(paths.len(), 1);
166 let item = &paths[0];
167 assert_eq!(item.path, file_path);
168 assert!(item.is_staged);
169 assert!(!item.is_selected);
170 }
171
172 #[test]
173 fn test_get_paths_staged_and_unstaged() {
174 let (_tmp, repo) = init_repo();
175
176 commit_file(&repo, "init.txt", "init", "init commit");
178
179 let staged_path = "staged.txt";
181 let staged_full_path = repo.workdir().unwrap().join(staged_path);
182 let mut staged_file = File::create(&staged_full_path).unwrap();
183 writeln!(staged_file, "staged content").unwrap();
184
185 let mut index = repo.index().unwrap();
186 index.add_path(Path::new(staged_path)).unwrap();
187 index.write().unwrap();
188
189 let unstaged_path = "unstaged.txt";
191 let unstaged_full_path = repo.workdir().unwrap().join(unstaged_path);
192 let mut unstaged_file = File::create(&unstaged_full_path).unwrap();
193 writeln!(unstaged_file, "unstaged content").unwrap();
194
195 let mut paths = get_paths(&repo).unwrap();
197 paths.sort_by(|a, b| a.path.cmp(&b.path));
198 assert_eq!(paths.len(), 2);
199
200 let staged = paths.iter().find(|p| p.path == staged_path).unwrap();
201 assert!(staged.is_staged);
202
203 let unstaged = paths.iter().find(|p| p.path == unstaged_path).unwrap();
204 assert!(!unstaged.is_staged);
205 }
206}