]> git.proxmox.com Git - cargo.git/blob - vendor/git2/src/stash.rs
New upstream version 0.47.0
[cargo.git] / vendor / git2 / src / stash.rs
1 use crate::build::CheckoutBuilder;
2 use crate::util::Binding;
3 use crate::{panic, raw, Oid, StashApplyProgress};
4 use libc::{c_char, c_int, c_void, size_t};
5 use std::ffi::CStr;
6 use std::mem;
7
8 /// Stash application progress notification function.
9 ///
10 /// Return `true` to continue processing, or `false` to
11 /// abort the stash application.
12 pub type StashApplyProgressCb<'a> = dyn FnMut(StashApplyProgress) -> bool + 'a;
13
14 /// This is a callback function you can provide to iterate over all the
15 /// stashed states that will be invoked per entry.
16 pub type StashCb<'a> = dyn FnMut(usize, &str, &Oid) -> bool + 'a;
17
18 #[allow(unused)]
19 /// Stash application options structure
20 pub struct StashApplyOptions<'cb> {
21 progress: Option<Box<StashApplyProgressCb<'cb>>>,
22 checkout_options: Option<CheckoutBuilder<'cb>>,
23 raw_opts: raw::git_stash_apply_options,
24 }
25
26 impl<'cb> Default for StashApplyOptions<'cb> {
27 fn default() -> Self {
28 Self::new()
29 }
30 }
31
32 impl<'cb> StashApplyOptions<'cb> {
33 /// Creates a default set of merge options.
34 pub fn new() -> StashApplyOptions<'cb> {
35 let mut opts = StashApplyOptions {
36 progress: None,
37 checkout_options: None,
38 raw_opts: unsafe { mem::zeroed() },
39 };
40 assert_eq!(
41 unsafe { raw::git_stash_apply_init_options(&mut opts.raw_opts, 1) },
42 0
43 );
44 opts
45 }
46
47 /// Set stash application flag to GIT_STASH_APPLY_REINSTATE_INDEX
48 pub fn reinstantiate_index(&mut self) -> &mut StashApplyOptions<'cb> {
49 self.raw_opts.flags = raw::GIT_STASH_APPLY_REINSTATE_INDEX as u32;
50 self
51 }
52
53 /// Options to use when writing files to the working directory
54 pub fn checkout_options(&mut self, opts: CheckoutBuilder<'cb>) -> &mut StashApplyOptions<'cb> {
55 self.checkout_options = Some(opts);
56 self
57 }
58
59 /// Optional callback to notify the consumer of application progress.
60 ///
61 /// Return `true` to continue processing, or `false` to
62 /// abort the stash application.
63 pub fn progress_cb<C>(&mut self, callback: C) -> &mut StashApplyOptions<'cb>
64 where
65 C: FnMut(StashApplyProgress) -> bool + 'cb,
66 {
67 self.progress = Some(Box::new(callback) as Box<StashApplyProgressCb<'cb>>);
68 self.raw_opts.progress_cb = Some(stash_apply_progress_cb);
69 self.raw_opts.progress_payload = self as *mut _ as *mut _;
70 self
71 }
72
73 /// Pointer to a raw git_stash_apply_options
74 pub fn raw(&mut self) -> &raw::git_stash_apply_options {
75 unsafe {
76 if let Some(opts) = self.checkout_options.as_mut() {
77 opts.configure(&mut self.raw_opts.checkout_options);
78 }
79 }
80 &self.raw_opts
81 }
82 }
83
84 #[allow(unused)]
85 pub struct StashCbData<'a> {
86 pub callback: &'a mut StashCb<'a>,
87 }
88
89 #[allow(unused)]
90 pub extern "C" fn stash_cb(
91 index: size_t,
92 message: *const c_char,
93 stash_id: *const raw::git_oid,
94 payload: *mut c_void,
95 ) -> c_int {
96 panic::wrap(|| unsafe {
97 let mut data = &mut *(payload as *mut StashCbData<'_>);
98 let res = {
99 let mut callback = &mut data.callback;
100 callback(
101 index,
102 CStr::from_ptr(message).to_str().unwrap(),
103 &Binding::from_raw(stash_id),
104 )
105 };
106
107 if res {
108 0
109 } else {
110 1
111 }
112 })
113 .unwrap_or(1)
114 }
115
116 fn convert_progress(progress: raw::git_stash_apply_progress_t) -> StashApplyProgress {
117 match progress {
118 raw::GIT_STASH_APPLY_PROGRESS_NONE => StashApplyProgress::None,
119 raw::GIT_STASH_APPLY_PROGRESS_LOADING_STASH => StashApplyProgress::LoadingStash,
120 raw::GIT_STASH_APPLY_PROGRESS_ANALYZE_INDEX => StashApplyProgress::AnalyzeIndex,
121 raw::GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED => StashApplyProgress::AnalyzeModified,
122 raw::GIT_STASH_APPLY_PROGRESS_ANALYZE_UNTRACKED => StashApplyProgress::AnalyzeUntracked,
123 raw::GIT_STASH_APPLY_PROGRESS_CHECKOUT_UNTRACKED => StashApplyProgress::CheckoutUntracked,
124 raw::GIT_STASH_APPLY_PROGRESS_CHECKOUT_MODIFIED => StashApplyProgress::CheckoutModified,
125 raw::GIT_STASH_APPLY_PROGRESS_DONE => StashApplyProgress::Done,
126
127 _ => StashApplyProgress::None,
128 }
129 }
130
131 #[allow(unused)]
132 extern "C" fn stash_apply_progress_cb(
133 progress: raw::git_stash_apply_progress_t,
134 payload: *mut c_void,
135 ) -> c_int {
136 panic::wrap(|| unsafe {
137 let mut options = &mut *(payload as *mut StashApplyOptions<'_>);
138 let res = {
139 let mut callback = options.progress.as_mut().unwrap();
140 callback(convert_progress(progress))
141 };
142
143 if res {
144 0
145 } else {
146 -1
147 }
148 })
149 .unwrap_or(-1)
150 }
151
152 #[cfg(test)]
153 mod tests {
154 use crate::stash::StashApplyOptions;
155 use crate::test::repo_init;
156 use crate::{Repository, StashFlags, Status};
157 use std::fs;
158 use std::io::Write;
159 use std::path::Path;
160
161 fn make_stash<C>(next: C)
162 where
163 C: FnOnce(&mut Repository),
164 {
165 let (_td, mut repo) = repo_init();
166 let signature = repo.signature().unwrap();
167
168 let p = Path::new(repo.workdir().unwrap()).join("file_b.txt");
169 println!("using path {:?}", p);
170 fs::File::create(&p)
171 .unwrap()
172 .write("data".as_bytes())
173 .unwrap();
174
175 let rel_p = Path::new("file_b.txt");
176 assert!(repo.status_file(&rel_p).unwrap() == Status::WT_NEW);
177
178 repo.stash_save(&signature, "msg1", Some(StashFlags::INCLUDE_UNTRACKED))
179 .unwrap();
180
181 assert!(repo.status_file(&rel_p).is_err());
182
183 let mut count = 0;
184 repo.stash_foreach(|index, name, _oid| {
185 count += 1;
186 assert!(index == 0);
187 assert!(name == "On master: msg1");
188 true
189 })
190 .unwrap();
191
192 assert!(count == 1);
193 next(&mut repo);
194 }
195
196 fn count_stash(repo: &mut Repository) -> usize {
197 let mut count = 0;
198 repo.stash_foreach(|_, _, _| {
199 count += 1;
200 true
201 })
202 .unwrap();
203 count
204 }
205
206 #[test]
207 fn smoke_stash_save_drop() {
208 make_stash(|repo| {
209 repo.stash_drop(0).unwrap();
210 assert!(count_stash(repo) == 0)
211 })
212 }
213
214 #[test]
215 fn smoke_stash_save_pop() {
216 make_stash(|repo| {
217 repo.stash_pop(0, None).unwrap();
218 assert!(count_stash(repo) == 0)
219 })
220 }
221
222 #[test]
223 fn smoke_stash_save_apply() {
224 make_stash(|repo| {
225 let mut options = StashApplyOptions::new();
226 options.progress_cb(|progress| {
227 println!("{:?}", progress);
228 true
229 });
230
231 repo.stash_apply(0, Some(&mut options)).unwrap();
232 assert!(count_stash(repo) == 1)
233 })
234 }
235
236 #[test]
237 fn test_stash_save2_msg_none() {
238 let (_td, mut repo) = repo_init();
239 let signature = repo.signature().unwrap();
240
241 let p = Path::new(repo.workdir().unwrap()).join("file_b.txt");
242
243 fs::File::create(&p)
244 .unwrap()
245 .write("data".as_bytes())
246 .unwrap();
247
248 repo.stash_save2(&signature, None, Some(StashFlags::INCLUDE_UNTRACKED))
249 .unwrap();
250
251 let mut stash_name = String::new();
252 repo.stash_foreach(|index, name, _oid| {
253 assert_eq!(index, 0);
254 stash_name = name.to_string();
255 true
256 })
257 .unwrap();
258
259 assert!(stash_name.starts_with("WIP on master:"));
260 }
261 }