@@ -194,6 +194,31 @@ static int stash_to_index(
194194 return git_index_add (index , & entry );
195195}
196196
197+ static int stash_update_index_from_paths (
198+ git_repository * repo ,
199+ git_index * index ,
200+ git_strarray * paths )
201+ {
202+ unsigned int status_flags ;
203+ size_t i ;
204+ int error = 0 ;
205+
206+ for (i = 0 ; i < paths -> count ; i ++ ) {
207+ git_status_file (& status_flags , repo , paths -> strings [i ]);
208+
209+ if (status_flags & (GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_DELETED )) {
210+ if ((error = git_index_remove (index , paths -> strings [i ], 0 )) < 0 )
211+ return error ;
212+ }
213+ else {
214+ if ((error = stash_to_index (repo , index , paths -> strings [i ])) < 0 )
215+ return error ;
216+ }
217+ }
218+
219+ return error ;
220+ }
221+
197222static int stash_update_index_from_diff (
198223 git_repository * repo ,
199224 git_index * index ,
@@ -576,6 +601,50 @@ static int ensure_there_are_changes_to_stash(git_repository *repo, uint32_t flag
576601 return error ;
577602}
578603
604+ static int has_changes_cb (const char * path , unsigned int status , void * payload ) {
605+ GIT_UNUSED (path );
606+ GIT_UNUSED (status );
607+ GIT_UNUSED (payload );
608+
609+ if (status == GIT_STATUS_CURRENT )
610+ return GIT_ENOTFOUND ;
611+
612+ return 0 ;
613+ }
614+
615+ static int ensure_there_are_changes_to_stash_paths (
616+ git_repository * repo ,
617+ uint32_t flags ,
618+ git_strarray * paths )
619+ {
620+ int error ;
621+ git_status_options opts = GIT_STATUS_OPTIONS_INIT ;
622+
623+ opts .show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR ;
624+ opts .flags = GIT_STATUS_OPT_EXCLUDE_SUBMODULES
625+ | GIT_STATUS_OPT_INCLUDE_UNMODIFIED
626+ | GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH ;
627+
628+ if (flags & GIT_STASH_INCLUDE_UNTRACKED )
629+ opts .flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
630+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS ;
631+
632+ if (flags & GIT_STASH_INCLUDE_IGNORED )
633+ opts .flags |= GIT_STATUS_OPT_INCLUDE_IGNORED |
634+ GIT_STATUS_OPT_RECURSE_IGNORED_DIRS ;
635+
636+ git_strarray_copy (& opts .pathspec , paths );
637+
638+ error = git_status_foreach_ext (repo , & opts , has_changes_cb , NULL );
639+
640+ git_strarray_dispose (& opts .pathspec );
641+
642+ if (error == GIT_ENOTFOUND )
643+ return create_error (GIT_ENOTFOUND , "one of the files does not have any changes to stash." );
644+
645+ return error ;
646+ }
647+
579648static int reset_index_and_workdir (git_repository * repo , git_commit * commit , uint32_t flags )
580649{
581650 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT ;
@@ -596,50 +665,87 @@ int git_stash_save(
596665 const char * message ,
597666 uint32_t flags )
598667{
599- git_index * index = NULL ;
668+ git_stash_save_options opts = GIT_STASH_SAVE_OPTIONS_INIT ;
669+ opts .stasher = stasher ;
670+ opts .message = message ;
671+ opts .flags = flags ;
672+ return git_stash_save_with_opts (out , repo , & opts );
673+ }
674+
675+ int git_stash_save_with_opts (
676+ git_oid * out , git_repository * repo , git_stash_save_options * opts )
677+ {
678+ git_index * index = NULL , * paths_index = NULL ;
600679 git_commit * b_commit = NULL , * i_commit = NULL , * u_commit = NULL ;
601680 git_buf msg = GIT_BUF_INIT ;
681+ git_tree * tree = NULL ;
682+ git_reference * head = NULL ;
602683 int error ;
603684
604685 GIT_ASSERT_ARG (out );
605686 GIT_ASSERT_ARG (repo );
606- GIT_ASSERT_ARG (stasher );
607687
608688 if ((error = git_repository__ensure_not_bare (repo , "stash save" )) < 0 )
609689 return error ;
610690
611691 if ((error = retrieve_base_commit_and_message (& b_commit , & msg , repo )) < 0 )
612692 goto cleanup ;
613693
614- if ((error = ensure_there_are_changes_to_stash (repo , flags )) < 0 )
694+ if (opts -> paths .count == 0 &&
695+ (error = ensure_there_are_changes_to_stash (repo , opts -> flags )) < 0 )
696+ goto cleanup ;
697+ else if (opts -> paths .count > 0 &&
698+ (error = ensure_there_are_changes_to_stash_paths (
699+ repo , opts -> flags , & opts -> paths )) < 0 )
615700 goto cleanup ;
616701
617702 if ((error = git_repository_index (& index , repo )) < 0 )
618703 goto cleanup ;
619704
620- if ((error = commit_index (& i_commit , repo , index , stasher ,
621- git_buf_cstr (& msg ), b_commit )) < 0 )
705+ if ((error = commit_index (& i_commit , repo , index , opts -> stasher ,
706+ git_buf_cstr (& msg ), b_commit )) < 0 )
622707 goto cleanup ;
623708
624- if ((flags & (GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED )) &&
625- (error = commit_untracked (& u_commit , repo , stasher ,
626- git_buf_cstr (& msg ), i_commit , flags )) < 0 )
709+ if ((opts -> flags & (GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED )) &&
710+ (error = commit_untracked (& u_commit , repo , opts -> stasher ,
711+ git_buf_cstr (& msg ), i_commit , opts -> flags )) < 0 )
627712 goto cleanup ;
628713
629- if ((error = prepare_worktree_commit_message (& msg , message )) < 0 )
714+ if ((error = prepare_worktree_commit_message (& msg , opts -> message )) < 0 )
630715 goto cleanup ;
631716
632- if ((error = commit_worktree (out , repo , stasher , git_buf_cstr (& msg ),
633- i_commit , b_commit , u_commit )) < 0 )
634- goto cleanup ;
717+ if (opts -> paths .count == 0 ) {
718+ if ((error = commit_worktree (out , repo , opts -> stasher , git_buf_cstr (& msg ),
719+ i_commit , b_commit , u_commit )) < 0 )
720+ goto cleanup ;
721+ } else {
722+ if ((error = git_index_new (& paths_index )) < 0 )
723+ goto cleanup ;
724+
725+ if ((error = retrieve_head (& head , repo )) < 0 )
726+ goto cleanup ;
727+
728+ if ((error = git_reference_peel ((git_object * * )& tree , head , GIT_OBJECT_TREE )) < 0 )
729+ goto cleanup ;
730+
731+ if ((error = git_index_read_tree (paths_index , tree )) < 0 )
732+ goto cleanup ;
733+
734+ if ((error = stash_update_index_from_paths (repo , paths_index , & opts -> paths )) < 0 )
735+ goto cleanup ;
736+
737+ if ((error = build_stash_commit_from_index (out , repo , opts -> stasher , git_buf_cstr (& msg ),
738+ i_commit , b_commit , u_commit , paths_index )) < 0 )
739+ goto cleanup ;
740+ }
635741
636742 git_buf_rtrim (& msg );
637743
638744 if ((error = update_reflog (out , repo , git_buf_cstr (& msg ))) < 0 )
639745 goto cleanup ;
640746
641- if (( error = reset_index_and_workdir ( repo , ( flags & GIT_STASH_KEEP_INDEX ) ? i_commit : b_commit ,
642- flags )) < 0 )
747+ if (!( opts -> flags & GIT_STASH_KEEP_ALL ) && ( error = reset_index_and_workdir ( repo ,
748+ ( opts -> flags & GIT_STASH_KEEP_INDEX ) ? i_commit : b_commit , opts -> flags )) < 0 )
643749 goto cleanup ;
644750
645751cleanup :
@@ -648,7 +754,10 @@ int git_stash_save(
648754 git_commit_free (i_commit );
649755 git_commit_free (b_commit );
650756 git_commit_free (u_commit );
757+ git_tree_free (tree );
758+ git_reference_free (head );
651759 git_index_free (index );
760+ git_index_free (paths_index );
652761
653762 return error ;
654763}
@@ -833,6 +942,13 @@ int git_stash_apply_options_init(git_stash_apply_options *opts, unsigned int ver
833942 return 0 ;
834943}
835944
945+ int git_stash_save_options_init (git_stash_save_options * opts , unsigned int version )
946+ {
947+ GIT_INIT_STRUCTURE_FROM_TEMPLATE (
948+ opts , version , git_stash_save_options , GIT_STASH_SAVE_OPTIONS_INIT );
949+ return 0 ;
950+ }
951+
836952#ifndef GIT_DEPRECATE_HARD
837953int git_stash_apply_init_options (git_stash_apply_options * opts , unsigned int version )
838954{
0 commit comments