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 if diff_entry.path().unwrap().ends_with(".swp") {
23 continue;
24 }
25
26 let path_items = diff_entry
27 .head_to_index()
29 .and_then(|d| {
31 Some(PathItems {
32 path: String::from(d.new_file().path()?.display().to_string()),
33 is_staged: true,
34 is_selected: false,
35 })
36 })
37 .or_else(|| {
39 Option::from(
40 diff_entry
41 .index_to_workdir()
42 .and_then(|d| {
43 Some(PathItems {
44 path: String::from(d.new_file().path()?.display().to_string()),
45 is_staged: false,
46 is_selected: false,
47 })
48 })
49 .or_else(|| {
52 diff_entry.index_to_workdir().and_then(|d| {
53 Some(PathItems {
54 path: String::from(d.old_file().path()?.display().to_string()),
55 is_staged: false,
56 is_selected: false,
57 })
58 })
59 })
60 .unwrap_or_else(|| PathItems {
62 path: String::from("<unknown>"),
63 is_staged: false,
64 is_selected: false,
65 }),
66 )
67 })
68 .unwrap();
69
70 items.push(path_items);
71 }
72
73 if items.is_empty() {
75 println!("{}", console::style("✔ working tree clean ✔").green());
76 process::exit(1)
77 }
78
79 Ok(items)
80}
81
82#[cfg(test)]
83mod tests {
84 use super::*;
85 use git2::{Oid, Repository, Signature};
86 use std::fs::File;
87 use std::io::Write;
88 use std::path::Path;
89 use tempfile::TempDir;
90
91 fn init_repo() -> (TempDir, Repository) {
93 let tmp_dir = TempDir::new().expect("create temp dir");
94 let repo = Repository::init(tmp_dir.path()).expect("init repo");
95 (tmp_dir, repo)
96 }
97
98 fn commit_file(repo: &Repository, file_path: &str, content: &str, message: &str) -> Oid {
100 let mut file = File::create(repo.workdir().unwrap().join(file_path)).unwrap();
101 file.write_all(content.as_bytes()).unwrap();
102
103 let mut index = repo.index().unwrap();
104 index.add_path(Path::new(file_path)).unwrap();
105 let oid = index.write_tree().unwrap();
106
107 let sig = Signature::now("Test", "test@example.com").unwrap();
108 let tree = repo.find_tree(oid).unwrap();
109
110 let parent_commit = repo
111 .head()
112 .ok()
113 .and_then(|h| h.target())
114 .and_then(|oid| repo.find_commit(oid).ok());
115
116 let commit_oid = if let Some(parent) = parent_commit {
117 repo.commit(Some("HEAD"), &sig, &sig, message, &tree, &[&parent])
118 .unwrap()
119 } else {
120 repo.commit(Some("HEAD"), &sig, &sig, message, &tree, &[])
121 .unwrap()
122 };
123 commit_oid
124 }
125
126 #[test]
127 fn test_get_paths_empty_worktree() {
128 let (_tmp, repo) = init_repo();
129
130 let statuses = get_paths(&repo).unwrap();
132 assert!(statuses.is_empty());
133 }
134
135 #[test]
136 fn test_get_paths_unstaged_file() {
137 let (_tmp, repo) = init_repo();
138
139 let file_path = "foo.txt";
141 let file_full_path = repo.workdir().unwrap().join(file_path);
142 let mut file = File::create(&file_full_path).unwrap();
143 writeln!(file, "hello world").unwrap();
144
145 let paths = get_paths(&repo).unwrap();
147 assert_eq!(paths.len(), 1);
148 let item = &paths[0];
149 assert_eq!(item.path, file_path);
150 assert!(!item.is_staged);
151 assert!(!item.is_selected);
152 }
153
154 #[test]
155 fn test_get_paths_staged_file() {
156 let (_tmp, repo) = init_repo();
157
158 let file_path = "bar.txt";
160 let file_full_path = repo.workdir().unwrap().join(file_path);
161 let mut file = File::create(&file_full_path).unwrap();
162 writeln!(file, "hello staged").unwrap();
163
164 let mut index = repo.index().unwrap();
165 index.add_path(Path::new(file_path)).unwrap();
166 index.write().unwrap();
167
168 let paths = get_paths(&repo).unwrap();
170 assert_eq!(paths.len(), 1);
171 let item = &paths[0];
172 assert_eq!(item.path, file_path);
173 assert!(item.is_staged);
174 assert!(!item.is_selected);
175 }
176
177 #[test]
178 fn test_get_paths_staged_and_unstaged() {
179 let (_tmp, repo) = init_repo();
180
181 commit_file(&repo, "init.txt", "init", "init commit");
183
184 let staged_path = "staged.txt";
186 let staged_full_path = repo.workdir().unwrap().join(staged_path);
187 let mut staged_file = File::create(&staged_full_path).unwrap();
188 writeln!(staged_file, "staged content").unwrap();
189
190 let mut index = repo.index().unwrap();
191 index.add_path(Path::new(staged_path)).unwrap();
192 index.write().unwrap();
193
194 let unstaged_path = "unstaged.txt";
196 let unstaged_full_path = repo.workdir().unwrap().join(unstaged_path);
197 let mut unstaged_file = File::create(&unstaged_full_path).unwrap();
198 writeln!(unstaged_file, "unstaged content").unwrap();
199
200 let mut paths = get_paths(&repo).unwrap();
202 paths.sort_by(|a, b| a.path.cmp(&b.path));
203 assert_eq!(paths.len(), 2);
204
205 let staged = paths.iter().find(|p| p.path == staged_path).unwrap();
206 assert!(staged.is_staged);
207
208 let unstaged = paths.iter().find(|p| p.path == unstaged_path).unwrap();
209 assert!(!unstaged.is_staged);
210 }
211}