Skip to content

Commit 1ca0449

Browse files
author
Stephan Dilly
committed
Add folder actions unstage/stage/reset
1 parent d22571c commit 1ca0449

File tree

11 files changed

+273
-131
lines changed

11 files changed

+273
-131
lines changed

asyncgit/src/sync/diff.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ fn new_file_content(path: &Path) -> String {
238238
mod tests {
239239
use super::get_diff;
240240
use crate::sync::{
241-
stage_add,
241+
stage_add_file,
242242
status::{get_status, StatusType},
243243
tests::{repo_init, repo_init_empty},
244244
};
@@ -288,7 +288,7 @@ mod tests {
288288
.write_all(b"test\nfoo")
289289
.unwrap();
290290

291-
assert_eq!(stage_add(repo_path, file_path), true);
291+
assert_eq!(stage_add_file(repo_path, file_path), true);
292292

293293
let diff = get_diff(
294294
repo_path,
@@ -347,7 +347,7 @@ mod tests {
347347
assert_eq!(res.len(), 1);
348348
assert_eq!(res[0].path, "bar.txt");
349349

350-
let res = stage_add(repo_path, Path::new("bar.txt"));
350+
let res = stage_add_file(repo_path, Path::new("bar.txt"));
351351
assert_eq!(res, true);
352352
assert_eq!(get_status(repo_path, StatusType::Stage).len(), 1);
353353
assert_eq!(

asyncgit/src/sync/mod.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,12 @@ pub mod utils;
99

1010
pub use hooks::{hooks_commit_msg, hooks_post_commit, HookResult};
1111
pub use hunks::{stage_hunk, unstage_hunk};
12-
pub use reset::{reset_stage, reset_workdir};
13-
pub use utils::{commit, stage_add, stage_addremoved};
12+
pub use reset::{
13+
reset_stage, reset_workdir_file, reset_workdir_folder,
14+
};
15+
pub use utils::{
16+
commit, stage_add_all, stage_add_file, stage_addremoved,
17+
};
1418

1519
#[cfg(test)]
1620
mod tests {

asyncgit/src/sync/reset.rs

Lines changed: 81 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,14 @@ pub fn reset_stage(repo_path: &str, path: &Path) -> bool {
2626
}
2727

2828
///
29-
pub fn reset_workdir(repo_path: &str, path: &Path) -> bool {
30-
scope_time!("reset_workdir");
29+
pub fn reset_workdir_file(repo_path: &str, path: &str) -> bool {
30+
scope_time!("reset_workdir_file");
3131

3232
let repo = repo(repo_path);
3333

3434
// Note: early out for removing untracked files, due to bug in checkout_head code:
3535
// see https://github.com/libgit2/libgit2/issues/5089
36-
if let Ok(status) = repo.status_file(&path) {
36+
if let Ok(status) = repo.status_file(Path::new(path)) {
3737
let removed_file_wd = if status == Status::WT_NEW
3838
|| (status == Status::WT_MODIFIED | Status::INDEX_NEW)
3939
{
@@ -51,25 +51,44 @@ pub fn reset_workdir(repo_path: &str, path: &Path) -> bool {
5151
.update_index(true) // windows: needs this to be true WTF?!
5252
.allow_conflicts(true)
5353
.force()
54-
.path(&path);
54+
.path(path);
5555

5656
repo.checkout_index(None, Some(&mut checkout_opts)).is_ok()
5757
} else {
5858
false
5959
}
6060
}
6161

62+
///
63+
pub fn reset_workdir_folder(repo_path: &str, path: &str) -> bool {
64+
scope_time!("reset_workdir_folder");
65+
66+
let repo = repo(repo_path);
67+
68+
let mut checkout_opts = CheckoutBuilder::new();
69+
checkout_opts
70+
.update_index(true) // windows: needs this to be true WTF?!
71+
.allow_conflicts(true)
72+
.remove_untracked(true)
73+
.force()
74+
.path(path);
75+
76+
repo.checkout_index(None, Some(&mut checkout_opts)).is_ok()
77+
}
78+
6279
#[cfg(test)]
6380
mod tests {
64-
use super::{reset_stage, reset_workdir};
81+
use super::{
82+
reset_stage, reset_workdir_file, reset_workdir_folder,
83+
};
6584
use crate::sync::{
6685
status::{get_status, StatusType},
6786
tests::{debug_cmd_print, repo_init, repo_init_empty},
68-
utils::stage_add,
87+
utils::{commit, stage_add_all, stage_add_file},
6988
};
7089
use std::{
7190
fs::{self, File},
72-
io::Write,
91+
io::{Error, Write},
7392
path::Path,
7493
};
7594

@@ -119,7 +138,7 @@ mod tests {
119138

120139
debug_cmd_print(repo_path, "git status");
121140

122-
stage_add(repo_path, Path::new("bar.txt"));
141+
stage_add_file(repo_path, Path::new("bar.txt"));
123142

124143
debug_cmd_print(repo_path, "git status");
125144

@@ -139,7 +158,7 @@ mod tests {
139158
1
140159
);
141160

142-
let res = reset_workdir(repo_path, Path::new("bar.txt"));
161+
let res = reset_workdir_file(repo_path, "bar.txt");
143162
assert_eq!(res, true);
144163

145164
debug_cmd_print(repo_path, "git status");
@@ -172,7 +191,7 @@ mod tests {
172191
1
173192
);
174193

175-
let res = reset_workdir(repo_path, Path::new("foo/bar.txt"));
194+
let res = reset_workdir_file(repo_path, "foo/bar.txt");
176195
assert_eq!(res, true);
177196

178197
debug_cmd_print(repo_path, "git status");
@@ -183,6 +202,56 @@ mod tests {
183202
);
184203
}
185204

205+
#[test]
206+
fn test_reset_folder() -> Result<(), Error> {
207+
let (_td, repo) = repo_init();
208+
let root = repo.path().parent().unwrap();
209+
let repo_path = root.as_os_str().to_str().unwrap();
210+
211+
{
212+
fs::create_dir(&root.join("foo"))?;
213+
File::create(&root.join("foo/file1.txt"))?
214+
.write_all(b"file1")?;
215+
File::create(&root.join("foo/file2.txt"))?
216+
.write_all(b"file1")?;
217+
File::create(&root.join("file3.txt"))?
218+
.write_all(b"file3")?;
219+
}
220+
221+
assert!(stage_add_all(repo_path, "*"));
222+
commit(repo_path, "msg");
223+
224+
{
225+
File::create(&root.join("foo/file1.txt"))?
226+
.write_all(b"file1\nadded line")?;
227+
fs::remove_file(&root.join("foo/file2.txt"))?;
228+
File::create(&root.join("foo/file4.txt"))?
229+
.write_all(b"file4")?;
230+
File::create(&root.join("foo/file5.txt"))?
231+
.write_all(b"file5")?;
232+
File::create(&root.join("file3.txt"))?
233+
.write_all(b"file3\nadded line")?;
234+
}
235+
236+
stage_add_file(repo_path, Path::new("foo/file5.txt"));
237+
238+
assert_eq!(
239+
get_status(repo_path, StatusType::WorkingDir).len(),
240+
4
241+
);
242+
assert_eq!(get_status(repo_path, StatusType::Stage).len(), 1);
243+
244+
assert!(reset_workdir_folder(repo_path, "foo"));
245+
246+
assert_eq!(
247+
get_status(repo_path, StatusType::WorkingDir).len(),
248+
1
249+
);
250+
assert_eq!(get_status(repo_path, StatusType::Stage).len(), 1);
251+
252+
Ok(())
253+
}
254+
186255
#[test]
187256
fn test_reset_untracked_in_subdir_and_index() {
188257
let (_td, repo) = repo_init();
@@ -219,7 +288,7 @@ mod tests {
219288
1
220289
);
221290

222-
let res = reset_workdir(repo_path, Path::new(file));
291+
let res = reset_workdir_file(repo_path, file);
223292
assert_eq!(res, true);
224293

225294
debug_cmd_print(repo_path, "git status");
@@ -243,7 +312,7 @@ mod tests {
243312
.write_all(b"test\nfoo")
244313
.unwrap();
245314

246-
assert_eq!(stage_add(repo_path, file_path), true);
315+
assert_eq!(stage_add_file(repo_path, file_path), true);
247316

248317
assert_eq!(reset_stage(repo_path, file_path), true);
249318
}

asyncgit/src/sync/utils.rs

Lines changed: 57 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//! sync git api (various methods)
22
3-
use git2::{Repository, RepositoryOpenFlags};
3+
use git2::{IndexAddOption, Repository, RepositoryOpenFlags};
44
use scopetime::scope_time;
55
use std::path::Path;
66

@@ -63,8 +63,8 @@ pub fn commit(repo_path: &str, msg: &str) {
6363
}
6464

6565
/// add a file diff from workingdir to stage (will not add removed files see `stage_addremoved`)
66-
pub fn stage_add(repo_path: &str, path: &Path) -> bool {
67-
scope_time!("stage_add");
66+
pub fn stage_add_file(repo_path: &str, path: &Path) -> bool {
67+
scope_time!("stage_add_file");
6868

6969
let repo = repo(repo_path);
7070

@@ -78,6 +78,25 @@ pub fn stage_add(repo_path: &str, path: &Path) -> bool {
7878
false
7979
}
8080

81+
/// like `stage_add_file` but uses a pattern to match/glob multiple files/folders
82+
pub fn stage_add_all(repo_path: &str, pattern: &str) -> bool {
83+
scope_time!("stage_add_all");
84+
85+
let repo = repo(repo_path);
86+
87+
let mut index = repo.index().unwrap();
88+
89+
if index
90+
.add_all(vec![pattern], IndexAddOption::DEFAULT, None)
91+
.is_ok()
92+
{
93+
index.write().unwrap();
94+
return true;
95+
}
96+
97+
false
98+
}
99+
81100
/// stage a removed file
82101
pub fn stage_addremoved(repo_path: &str, path: &Path) -> bool {
83102
scope_time!("stage_addremoved");
@@ -98,13 +117,12 @@ pub fn stage_addremoved(repo_path: &str, path: &Path) -> bool {
98117
mod tests {
99118
use super::*;
100119
use crate::sync::{
101-
stage_add,
102120
status::{get_status, StatusType},
103121
tests::{repo_init, repo_init_empty},
104122
};
105123
use std::{
106-
fs::{remove_file, File},
107-
io::Write,
124+
fs::{self, remove_file, File},
125+
io::{Error, Write},
108126
path::Path,
109127
};
110128

@@ -126,7 +144,7 @@ mod tests {
126144

127145
assert_eq!(status_count(StatusType::WorkingDir), 1);
128146

129-
assert_eq!(stage_add(repo_path, file_path), true);
147+
assert_eq!(stage_add_file(repo_path, file_path), true);
130148

131149
assert_eq!(status_count(StatusType::WorkingDir), 0);
132150
assert_eq!(status_count(StatusType::Stage), 1);
@@ -149,7 +167,7 @@ mod tests {
149167
.write_all(b"test\nfoo")
150168
.unwrap();
151169

152-
assert_eq!(stage_add(repo_path, file_path), true);
170+
assert_eq!(stage_add_file(repo_path, file_path), true);
153171

154172
commit(repo_path, "commit msg");
155173
}
@@ -161,7 +179,7 @@ mod tests {
161179
let root = repo.path().parent().unwrap();
162180
let repo_path = root.as_os_str().to_str().unwrap();
163181

164-
assert_eq!(stage_add(repo_path, file_path), false);
182+
assert_eq!(stage_add_file(repo_path, file_path), false);
165183
}
166184

167185
#[test]
@@ -187,12 +205,40 @@ mod tests {
187205

188206
assert_eq!(status_count(StatusType::WorkingDir), 2);
189207

190-
assert_eq!(stage_add(repo_path, file_path), true);
208+
assert_eq!(stage_add_file(repo_path, file_path), true);
191209

192210
assert_eq!(status_count(StatusType::WorkingDir), 1);
193211
assert_eq!(status_count(StatusType::Stage), 1);
194212
}
195213

214+
#[test]
215+
fn test_staging_folder() -> Result<(), Error> {
216+
let (_td, repo) = repo_init();
217+
let root = repo.path().parent().unwrap();
218+
let repo_path = root.as_os_str().to_str().unwrap();
219+
220+
let status_count = |s: StatusType| -> usize {
221+
get_status(repo_path, s).len()
222+
};
223+
224+
fs::create_dir_all(&root.join("a/d"))?;
225+
File::create(&root.join(Path::new("a/d/f1.txt")))?
226+
.write_all(b"foo")?;
227+
File::create(&root.join(Path::new("a/d/f2.txt")))?
228+
.write_all(b"foo")?;
229+
File::create(&root.join(Path::new("a/f3.txt")))?
230+
.write_all(b"foo")?;
231+
232+
assert_eq!(status_count(StatusType::WorkingDir), 3);
233+
234+
assert_eq!(stage_add_all(repo_path, "a/d"), true);
235+
236+
assert_eq!(status_count(StatusType::WorkingDir), 1);
237+
assert_eq!(status_count(StatusType::Stage), 2);
238+
239+
Ok(())
240+
}
241+
196242
#[test]
197243
fn test_staging_deleted_file() {
198244
let file_path = Path::new("file1.txt");
@@ -211,7 +257,7 @@ mod tests {
211257
.write_all(b"test file1 content")
212258
.unwrap();
213259

214-
assert_eq!(stage_add(repo_path, file_path), true);
260+
assert_eq!(stage_add_file(repo_path, file_path), true);
215261

216262
commit(repo_path, "commit msg");
217263

0 commit comments

Comments
 (0)