@@ -193,6 +193,30 @@ static int stash_to_index(
193193 return git_index_add (index , & entry );
194194}
195195
196+ static int stash_update_index_from_paths (
197+ git_repository * repo ,
198+ git_index * index ,
199+ git_strarray * paths )
200+ {
201+ unsigned int status_flags ;
202+ size_t i , error = 0 ;
203+
204+ for (i = 0 ; i < paths -> count ; i ++ ) {
205+ git_status_file (& status_flags , repo , paths -> strings [i ]);
206+
207+ if (status_flags & (GIT_STATUS_WT_DELETED | GIT_STATUS_INDEX_DELETED )) {
208+ if ((error = git_index_remove (index , paths -> strings [i ], 0 )) < 0 )
209+ return error ;
210+ }
211+ else {
212+ if ((error = stash_to_index (repo , index , paths -> strings [i ])) < 0 )
213+ return error ;
214+ }
215+ }
216+
217+ return error ;
218+ }
219+
196220static int stash_update_index_from_diff (
197221 git_repository * repo ,
198222 git_index * index ,
@@ -388,24 +412,80 @@ static int build_workdir_tree(
388412 return error ;
389413}
390414
391- static int commit_worktree (
415+ static int build_stash_commit_from_tree (
392416 git_oid * w_commit_oid ,
393417 git_repository * repo ,
394418 const git_signature * stasher ,
395419 const char * message ,
396420 git_commit * i_commit ,
397421 git_commit * b_commit ,
398- git_commit * u_commit )
422+ git_commit * u_commit ,
423+ git_tree * tree )
399424{
400425 const git_commit * parents [] = { NULL , NULL , NULL };
401- git_index * i_index = NULL , * r_index = NULL ;
402- git_tree * w_tree = NULL ;
403- int error = 0 , ignorecase ;
404426
405427 parents [0 ] = b_commit ;
406428 parents [1 ] = i_commit ;
407429 parents [2 ] = u_commit ;
408430
431+ return git_commit_create (
432+ w_commit_oid ,
433+ repo ,
434+ NULL ,
435+ stasher ,
436+ stasher ,
437+ NULL ,
438+ message ,
439+ tree ,
440+ u_commit ? 3 : 2 ,
441+ parents );
442+ }
443+
444+ static int build_stash_commit_from_index (
445+ git_oid * w_commit_oid ,
446+ git_repository * repo ,
447+ const git_signature * stasher ,
448+ const char * message ,
449+ git_commit * i_commit ,
450+ git_commit * b_commit ,
451+ git_commit * u_commit ,
452+ git_index * index )
453+ {
454+ git_tree * tree ;
455+ int error ;
456+
457+ if ((error = build_tree_from_index (& tree , repo , index )) < 0 )
458+ goto cleanup ;
459+
460+ error = build_stash_commit_from_tree (
461+ w_commit_oid ,
462+ repo ,
463+ stasher ,
464+ message ,
465+ i_commit ,
466+ b_commit ,
467+ u_commit ,
468+ tree
469+ );
470+
471+ cleanup :
472+ git_tree_free (tree );
473+ return error ;
474+ }
475+
476+ static int commit_worktree (
477+ git_oid * w_commit_oid ,
478+ git_repository * repo ,
479+ const git_signature * stasher ,
480+ const char * message ,
481+ git_commit * i_commit ,
482+ git_commit * b_commit ,
483+ git_commit * u_commit )
484+ {
485+ git_index * i_index = NULL , * r_index = NULL ;
486+ git_tree * w_tree = NULL ;
487+ int error = 0 , ignorecase ;
488+
409489 if ((error = git_repository_index (& r_index , repo ) < 0 ) ||
410490 (error = git_index_new (& i_index )) < 0 ||
411491 (error = git_index__fill (i_index , & r_index -> entries ) < 0 ) ||
@@ -417,17 +497,16 @@ static int commit_worktree(
417497 if ((error = build_workdir_tree (& w_tree , repo , i_index , b_commit )) < 0 )
418498 goto cleanup ;
419499
420- error = git_commit_create (
500+ error = build_stash_commit_from_tree (
421501 w_commit_oid ,
422502 repo ,
423- NULL ,
424- stasher ,
425503 stasher ,
426- NULL ,
427504 message ,
428- w_tree ,
429- u_commit ? 3 : 2 ,
430- parents );
505+ i_commit ,
506+ b_commit ,
507+ u_commit ,
508+ w_tree
509+ );
431510
432511cleanup :
433512 git_tree_free (w_tree );
@@ -520,6 +599,50 @@ static int ensure_there_are_changes_to_stash(git_repository *repo, uint32_t flag
520599 return error ;
521600}
522601
602+ static int has_changes_cb (const char * path , unsigned int status , void * payload ) {
603+ GIT_UNUSED (path );
604+ GIT_UNUSED (status );
605+ GIT_UNUSED (payload );
606+
607+ if (status == GIT_STATUS_CURRENT )
608+ return GIT_ENOTFOUND ;
609+
610+ return 0 ;
611+ }
612+
613+ static int ensure_there_are_changes_to_stash_paths (
614+ git_repository * repo ,
615+ uint32_t flags ,
616+ git_strarray * paths )
617+ {
618+ int error ;
619+ git_status_options opts = GIT_STATUS_OPTIONS_INIT ;
620+
621+ opts .show = GIT_STATUS_SHOW_INDEX_AND_WORKDIR ;
622+ opts .flags = GIT_STATUS_OPT_EXCLUDE_SUBMODULES
623+ | GIT_STATUS_OPT_INCLUDE_UNMODIFIED
624+ | GIT_STATUS_OPT_DISABLE_PATHSPEC_MATCH ;
625+
626+ if (flags & GIT_STASH_INCLUDE_UNTRACKED )
627+ opts .flags |= GIT_STATUS_OPT_INCLUDE_UNTRACKED |
628+ GIT_STATUS_OPT_RECURSE_UNTRACKED_DIRS ;
629+
630+ if (flags & GIT_STASH_INCLUDE_IGNORED )
631+ opts .flags |= GIT_STATUS_OPT_INCLUDE_IGNORED |
632+ GIT_STATUS_OPT_RECURSE_IGNORED_DIRS ;
633+
634+ git_strarray_copy (& opts .pathspec , paths );
635+
636+ error = git_status_foreach_ext (repo , & opts , has_changes_cb , NULL );
637+
638+ git_strarray_dispose (& opts .pathspec );
639+
640+ if (error == GIT_ENOTFOUND )
641+ return create_error (GIT_ENOTFOUND , "one of the files does not have any changes to stash." );
642+
643+ return error ;
644+ }
645+
523646static int reset_index_and_workdir (git_repository * repo , git_commit * commit , uint32_t flags )
524647{
525648 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT ;
@@ -540,50 +663,87 @@ int git_stash_save(
540663 const char * message ,
541664 uint32_t flags )
542665{
543- git_index * index = NULL ;
666+ git_stash_save_options opts = GIT_STASH_SAVE_OPTIONS_INIT ;
667+ opts .stasher = stasher ;
668+ opts .message = message ;
669+ opts .flags = flags ;
670+ return git_stash_save_with_opts (out , repo , & opts );
671+ }
672+
673+ int git_stash_save_with_opts (
674+ git_oid * out , git_repository * repo , git_stash_save_options * opts )
675+ {
676+ git_index * index = NULL , * paths_index = NULL ;
544677 git_commit * b_commit = NULL , * i_commit = NULL , * u_commit = NULL ;
545678 git_str msg = GIT_STR_INIT ;
679+ git_tree * tree = NULL ;
680+ git_reference * head = NULL ;
546681 int error ;
547682
548683 GIT_ASSERT_ARG (out );
549684 GIT_ASSERT_ARG (repo );
550- GIT_ASSERT_ARG (stasher );
551685
552686 if ((error = git_repository__ensure_not_bare (repo , "stash save" )) < 0 )
553687 return error ;
554688
555689 if ((error = retrieve_base_commit_and_message (& b_commit , & msg , repo )) < 0 )
556690 goto cleanup ;
557691
558- if ((error = ensure_there_are_changes_to_stash (repo , flags )) < 0 )
692+ if (opts -> paths .count == 0 &&
693+ (error = ensure_there_are_changes_to_stash (repo , opts -> flags )) < 0 )
694+ goto cleanup ;
695+ else if (opts -> paths .count > 0 &&
696+ (error = ensure_there_are_changes_to_stash_paths (
697+ repo , opts -> flags , & opts -> paths )) < 0 )
559698 goto cleanup ;
560699
561700 if ((error = git_repository_index (& index , repo )) < 0 )
562701 goto cleanup ;
563702
564- if ((error = commit_index (& i_commit , repo , index , stasher ,
703+ if ((error = commit_index (& i_commit , repo , index , opts -> stasher ,
565704 git_str_cstr (& msg ), b_commit )) < 0 )
566705 goto cleanup ;
567706
568- if ((flags & (GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED )) &&
569- (error = commit_untracked (& u_commit , repo , stasher ,
570- git_str_cstr (& msg ), i_commit , flags )) < 0 )
707+ if ((opts -> flags & (GIT_STASH_INCLUDE_UNTRACKED | GIT_STASH_INCLUDE_IGNORED )) &&
708+ (error = commit_untracked (& u_commit , repo , opts -> stasher ,
709+ git_str_cstr (& msg ), i_commit , opts -> flags )) < 0 )
571710 goto cleanup ;
572711
573- if ((error = prepare_worktree_commit_message (& msg , message )) < 0 )
712+ if ((error = prepare_worktree_commit_message (& msg , opts -> message )) < 0 )
574713 goto cleanup ;
575714
576- if ((error = commit_worktree (out , repo , stasher , git_str_cstr (& msg ),
577- i_commit , b_commit , u_commit )) < 0 )
578- goto cleanup ;
715+ if (opts -> paths .count == 0 ) {
716+ if ((error = commit_worktree (out , repo , opts -> stasher , git_str_cstr (& msg ),
717+ i_commit , b_commit , u_commit )) < 0 )
718+ goto cleanup ;
719+ } else {
720+ if ((error = git_index_new (& paths_index )) < 0 )
721+ goto cleanup ;
722+
723+ if ((error = retrieve_head (& head , repo )) < 0 )
724+ goto cleanup ;
725+
726+ if ((error = git_reference_peel ((git_object * * )& tree , head , GIT_OBJECT_TREE )) < 0 )
727+ goto cleanup ;
728+
729+ if ((error = git_index_read_tree (paths_index , tree )) < 0 )
730+ goto cleanup ;
731+
732+ if ((error = stash_update_index_from_paths (repo , paths_index , & opts -> paths )) < 0 )
733+ goto cleanup ;
734+
735+ if ((error = build_stash_commit_from_index (out , repo , opts -> stasher , git_str_cstr (& msg ),
736+ i_commit , b_commit , u_commit , paths_index )) < 0 )
737+ goto cleanup ;
738+ }
579739
580740 git_str_rtrim (& msg );
581741
582742 if ((error = update_reflog (out , repo , git_str_cstr (& msg ))) < 0 )
583743 goto cleanup ;
584744
585- if (( error = reset_index_and_workdir ( repo , ( flags & GIT_STASH_KEEP_INDEX ) ? i_commit : b_commit ,
586- flags )) < 0 )
745+ if (!( opts -> flags & GIT_STASH_KEEP_ALL ) && ( error = reset_index_and_workdir ( repo ,
746+ ( opts -> flags & GIT_STASH_KEEP_INDEX ) ? i_commit : b_commit , opts -> flags )) < 0 )
587747 goto cleanup ;
588748
589749cleanup :
@@ -592,7 +752,10 @@ int git_stash_save(
592752 git_commit_free (i_commit );
593753 git_commit_free (b_commit );
594754 git_commit_free (u_commit );
755+ git_tree_free (tree );
756+ git_reference_free (head );
595757 git_index_free (index );
758+ git_index_free (paths_index );
596759
597760 return error ;
598761}
@@ -777,6 +940,13 @@ int git_stash_apply_options_init(git_stash_apply_options *opts, unsigned int ver
777940 return 0 ;
778941}
779942
943+ int git_stash_save_options_init (git_stash_save_options * opts , unsigned int version )
944+ {
945+ GIT_INIT_STRUCTURE_FROM_TEMPLATE (
946+ opts , version , git_stash_save_options , GIT_STASH_SAVE_OPTIONS_INIT );
947+ return 0 ;
948+ }
949+
780950#ifndef GIT_DEPRECATE_HARD
781951int git_stash_apply_init_options (git_stash_apply_options * opts , unsigned int version )
782952{
0 commit comments