gnostr_asyncgit/sync/
status.rs1use std::path::Path;
4
5use git2::{Delta, Status, StatusOptions, StatusShow};
6use scopetime::scope_time;
7
8use super::{RepoPath, ShowUntrackedFilesConfig};
9use crate::{
10 error::{Error, Result},
11 sync::{config::untracked_files_config_repo, repository::repo},
12};
13
14#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
16pub enum StatusItemType {
17 New,
19 Modified,
21 Deleted,
23 Renamed,
25 Typechange,
27 Conflicted,
29}
30
31impl From<Status> for StatusItemType {
32 fn from(s: Status) -> Self {
33 if s.is_index_new() || s.is_wt_new() {
34 Self::New
35 } else if s.is_index_deleted() || s.is_wt_deleted() {
36 Self::Deleted
37 } else if s.is_index_renamed() || s.is_wt_renamed() {
38 Self::Renamed
39 } else if s.is_index_typechange() || s.is_wt_typechange() {
40 Self::Typechange
41 } else if s.is_conflicted() {
42 Self::Conflicted
43 } else {
44 Self::Modified
45 }
46 }
47}
48
49impl From<Delta> for StatusItemType {
50 fn from(d: Delta) -> Self {
51 match d {
52 Delta::Added => Self::New,
53 Delta::Deleted => Self::Deleted,
54 Delta::Renamed => Self::Renamed,
55 Delta::Typechange => Self::Typechange,
56 _ => Self::Modified,
57 }
58 }
59}
60
61#[derive(Clone, Hash, PartialEq, Eq, Debug)]
63pub struct StatusItem {
64 pub path: String,
66 pub status: StatusItemType,
68}
69
70#[derive(Copy, Clone, Hash, PartialEq, Eq, Debug)]
72pub enum StatusType {
73 WorkingDir,
75 Stage,
77 Both,
79}
80
81impl Default for StatusType {
82 fn default() -> Self {
83 Self::WorkingDir
84 }
85}
86
87impl From<StatusType> for StatusShow {
88 fn from(s: StatusType) -> Self {
89 match s {
90 StatusType::WorkingDir => Self::Workdir,
91 StatusType::Stage => Self::Index,
92 StatusType::Both => Self::IndexAndWorkdir,
93 }
94 }
95}
96
97pub fn is_workdir_clean(
99 repo_path: &RepoPath,
100 show_untracked: Option<ShowUntrackedFilesConfig>,
101) -> Result<bool> {
102 let repo = repo(repo_path)?;
103
104 if repo.is_bare() && !repo.is_worktree() {
105 return Ok(true);
106 }
107
108 let show_untracked = if let Some(config) = show_untracked {
109 config
110 } else {
111 untracked_files_config_repo(&repo)?
112 };
113
114 let mut options = StatusOptions::default();
115 options
116 .show(StatusShow::Workdir)
117 .update_index(true)
118 .include_untracked(show_untracked.include_untracked())
119 .renames_head_to_index(true)
120 .recurse_untracked_dirs(
121 show_untracked.recurse_untracked_dirs(),
122 );
123
124 let statuses = repo.statuses(Some(&mut options))?;
125
126 Ok(statuses.is_empty())
127}
128
129pub fn get_status(
131 repo_path: &RepoPath,
132 status_type: StatusType,
133 show_untracked: Option<ShowUntrackedFilesConfig>,
134) -> Result<Vec<StatusItem>> {
135 scope_time!("get_status");
136
137 let repo = repo(repo_path)?;
138
139 if repo.is_bare() && !repo.is_worktree() {
140 return Ok(Vec::new());
141 }
142
143 let show_untracked = if let Some(config) = show_untracked {
144 config
145 } else {
146 untracked_files_config_repo(&repo)?
147 };
148
149 let mut options = StatusOptions::default();
150 options
151 .show(status_type.into())
152 .update_index(true)
153 .include_untracked(show_untracked.include_untracked())
154 .renames_head_to_index(true)
155 .recurse_untracked_dirs(
156 show_untracked.recurse_untracked_dirs(),
157 );
158
159 let statuses = repo.statuses(Some(&mut options))?;
160
161 let mut res = Vec::with_capacity(statuses.len());
162
163 for e in statuses.iter() {
164 let status: Status = e.status();
165
166 let path = match e.head_to_index() {
167 Some(diff) => diff
168 .new_file()
169 .path()
170 .and_then(Path::to_str)
171 .map(String::from)
172 .ok_or_else(|| {
173 Error::Generic(
174 "failed to get path to diff's new file."
175 .to_string(),
176 )
177 })?,
178 None => e.path().map(String::from).ok_or_else(|| {
179 Error::Generic(
180 "failed to get the path to indexed file."
181 .to_string(),
182 )
183 })?,
184 };
185
186 res.push(StatusItem {
187 path,
188 status: StatusItemType::from(status),
189 });
190 }
191
192 res.sort_by(|a, b| {
193 Path::new(a.path.as_str()).cmp(Path::new(b.path.as_str()))
194 });
195
196 Ok(res)
197}