11use crate :: build:: CheckoutBuilder ;
2- use crate :: util:: Binding ;
3- use crate :: { panic, raw, Oid , StashApplyProgress } ;
2+ use crate :: util:: { self , Binding } ;
3+ use crate :: { panic, raw, IntoCString , Oid , Signature , StashApplyProgress , StashFlags } ;
44use libc:: { c_char, c_int, c_void, size_t} ;
5- use std:: ffi:: CStr ;
5+ use std:: ffi:: { c_uint , CStr , CString } ;
66use std:: mem;
77
8+ #[ allow( unused) ]
9+ /// Stash application options structure
10+ pub struct StashSaveOptions < ' a > {
11+ message : Option < CString > ,
12+ flags : Option < StashFlags > ,
13+ stasher : Signature < ' a > ,
14+ pathspec : Vec < CString > ,
15+ pathspec_ptrs : Vec < * const c_char > ,
16+ raw_opts : raw:: git_stash_save_options ,
17+ }
18+
19+ impl < ' a > StashSaveOptions < ' a > {
20+ /// Creates a default
21+ pub fn new ( stasher : Signature < ' a > ) -> Self {
22+ let mut opts = Self {
23+ message : None ,
24+ flags : None ,
25+ stasher,
26+ pathspec : Vec :: new ( ) ,
27+ pathspec_ptrs : Vec :: new ( ) ,
28+ raw_opts : unsafe { mem:: zeroed ( ) } ,
29+ } ;
30+ assert_eq ! (
31+ unsafe {
32+ raw:: git_stash_save_options_init(
33+ & mut opts. raw_opts,
34+ raw:: GIT_STASH_SAVE_OPTIONS_VERSION ,
35+ )
36+ } ,
37+ 0
38+ ) ;
39+ opts
40+ }
41+
42+ /// Customize optional `flags` field
43+ pub fn flags ( & mut self , flags : Option < StashFlags > ) -> & mut Self {
44+ self . flags = flags;
45+ self
46+ }
47+
48+ /// Add to the array of paths patterns to build the stash.
49+ pub fn pathspec < T : IntoCString > ( & mut self , pathspec : T ) -> & mut Self {
50+ let s = util:: cstring_to_repo_path ( pathspec) . unwrap ( ) ;
51+ self . pathspec_ptrs . push ( s. as_ptr ( ) ) ;
52+ self . pathspec . push ( s) ;
53+ self
54+ }
55+
56+ /// Acquire a pointer to the underlying raw options.
57+ ///
58+ /// This function is unsafe as the pointer is only valid so long as this
59+ /// structure is not moved, modified, or used elsewhere.
60+ pub unsafe fn raw ( & mut self ) -> * const raw:: git_stash_save_options {
61+ self . raw_opts . flags = self . flags . unwrap_or_else ( StashFlags :: empty) . bits as c_uint ;
62+ self . raw_opts . message = crate :: call:: convert ( & self . message ) ;
63+ self . raw_opts . paths . count = self . pathspec_ptrs . len ( ) as size_t ;
64+ self . raw_opts . paths . strings = self . pathspec_ptrs . as_ptr ( ) as * mut _ ;
65+ self . raw_opts . stasher = self . stasher . raw ( ) ;
66+
67+ & self . raw_opts as * const _
68+ }
69+ }
70+
871/// Stash application progress notification function.
972///
1073/// Return `true` to continue processing, or `false` to
@@ -151,12 +214,11 @@ extern "C" fn stash_apply_progress_cb(
151214
152215#[ cfg( test) ]
153216mod tests {
154- use crate :: stash:: StashApplyOptions ;
217+ use crate :: stash:: { StashApplyOptions , StashSaveOptions } ;
155218 use crate :: test:: repo_init;
156- use crate :: { Repository , StashFlags , Status } ;
219+ use crate :: { IndexAddOption , Repository , StashFlags , Status } ;
157220 use std:: fs;
158- use std:: io:: Write ;
159- use std:: path:: Path ;
221+ use std:: path:: { Path , PathBuf } ;
160222
161223 fn make_stash < C > ( next : C )
162224 where
@@ -167,10 +229,8 @@ mod tests {
167229
168230 let p = Path :: new ( repo. workdir ( ) . unwrap ( ) ) . join ( "file_b.txt" ) ;
169231 println ! ( "using path {:?}" , p) ;
170- fs:: File :: create ( & p)
171- . unwrap ( )
172- . write ( "data" . as_bytes ( ) )
173- . unwrap ( ) ;
232+
233+ fs:: write ( & p, "data" . as_bytes ( ) ) . unwrap ( ) ;
174234
175235 let rel_p = Path :: new ( "file_b.txt" ) ;
176236 assert ! ( repo. status_file( & rel_p) . unwrap( ) == Status :: WT_NEW ) ;
@@ -240,10 +300,7 @@ mod tests {
240300
241301 let p = Path :: new ( repo. workdir ( ) . unwrap ( ) ) . join ( "file_b.txt" ) ;
242302
243- fs:: File :: create ( & p)
244- . unwrap ( )
245- . write ( "data" . as_bytes ( ) )
246- . unwrap ( ) ;
303+ fs:: write ( & p, "data" . as_bytes ( ) ) . unwrap ( ) ;
247304
248305 repo. stash_save2 ( & signature, None , Some ( StashFlags :: INCLUDE_UNTRACKED ) )
249306 . unwrap ( ) ;
@@ -258,4 +315,37 @@ mod tests {
258315
259316 assert ! ( stash_name. starts_with( "WIP on main:" ) ) ;
260317 }
318+
319+ fn create_file ( r : & Repository , name : & str , data : & str ) -> PathBuf {
320+ let p = Path :: new ( r. workdir ( ) . unwrap ( ) ) . join ( name) ;
321+ fs:: write ( & p, data) . unwrap ( ) ;
322+ p
323+ }
324+
325+ #[ test]
326+ fn test_stash_save_ext ( ) {
327+ let ( _td, mut repo) = repo_init ( ) ;
328+ let signature = repo. signature ( ) . unwrap ( ) ;
329+
330+ create_file ( & repo, "file_a" , "foo" ) ;
331+ create_file ( & repo, "file_b" , "foo" ) ;
332+
333+ let mut index = repo. index ( ) . unwrap ( ) ;
334+ index
335+ . add_all ( [ "*" ] . iter ( ) , IndexAddOption :: DEFAULT , None )
336+ . unwrap ( ) ;
337+ index. write ( ) . unwrap ( ) ;
338+
339+ assert_eq ! ( repo. statuses( None ) . unwrap( ) . len( ) , 2 ) ;
340+
341+ let mut opt = StashSaveOptions :: new ( signature) ;
342+ opt. pathspec ( "file_a" ) ;
343+ repo. stash_save_ext ( Some ( & mut opt) ) . unwrap ( ) ;
344+
345+ assert_eq ! ( repo. statuses( None ) . unwrap( ) . len( ) , 0 ) ;
346+
347+ repo. stash_pop ( 0 , None ) . unwrap ( ) ;
348+
349+ assert_eq ! ( repo. statuses( None ) . unwrap( ) . len( ) , 1 ) ;
350+ }
261351}
0 commit comments