Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit b7f5cb8

Browse files
committed
stash: stage new files when unstashing them
Files that were new (staged additions) in the stash tree should be staged when unstashing, even when not applying the index.
1 parent 8960dc1 commit b7f5cb8

File tree

2 files changed

+128
-2
lines changed

2 files changed

+128
-2
lines changed

src/stash.c

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,31 @@ static int retrieve_stash_trees(
671671
return error;
672672
}
673673

674+
static int merge_indexes(
675+
git_index **out,
676+
git_repository *repo,
677+
git_tree *ancestor_tree,
678+
git_index *ours_index,
679+
git_index *theirs_index)
680+
{
681+
git_iterator *ancestor = NULL, *ours = NULL, *theirs = NULL;
682+
const git_iterator_flag_t flags = GIT_ITERATOR_DONT_IGNORE_CASE;
683+
int error;
684+
685+
if ((error = git_iterator_for_tree(&ancestor, ancestor_tree, flags, NULL, NULL)) < 0 ||
686+
(error = git_iterator_for_index(&ours, ours_index, flags, NULL, NULL)) < 0 ||
687+
(error = git_iterator_for_index(&theirs, theirs_index, flags, NULL, NULL)) < 0)
688+
goto done;
689+
690+
error = git_merge__iterators(out, repo, ancestor, ours, theirs, NULL);
691+
692+
done:
693+
git_iterator_free(ancestor);
694+
git_iterator_free(ours);
695+
git_iterator_free(theirs);
696+
return error;
697+
}
698+
674699
static int merge_index_and_tree(
675700
git_index **out,
676701
git_repository *repo,
@@ -756,6 +781,47 @@ static int ensure_clean_index(git_repository *repo, git_index *index)
756781
return error;
757782
}
758783

784+
static int stage_new_file(const git_index_entry **entries, void *data)
785+
{
786+
git_index *index = data;
787+
788+
if(entries[0] == NULL)
789+
return git_index_add(index, entries[1]);
790+
else
791+
return git_index_add(index, entries[0]);
792+
}
793+
794+
static int stage_new_files(
795+
git_index **out,
796+
git_repository *repo,
797+
git_tree *parent_tree,
798+
git_tree *tree)
799+
{
800+
git_iterator *iterators[2] = { NULL, NULL };
801+
git_index *index = NULL;
802+
int error;
803+
804+
if ((error = git_index_new(&index)) < 0 ||
805+
(error = git_iterator_for_tree(&iterators[0], parent_tree,
806+
GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0 ||
807+
(error = git_iterator_for_tree(&iterators[1], tree,
808+
GIT_ITERATOR_DONT_IGNORE_CASE, NULL, NULL)) < 0)
809+
goto done;
810+
811+
error = git_iterator_walk(iterators, 2, stage_new_file, index);
812+
813+
done:
814+
if (error < 0)
815+
git_index_free(index);
816+
else
817+
*out = index;
818+
819+
git_iterator_free(iterators[0]);
820+
git_iterator_free(iterators[1]);
821+
822+
return error;
823+
}
824+
759825
int git_stash_apply(
760826
git_repository *repo,
761827
size_t index,
@@ -769,6 +835,7 @@ int git_stash_apply(
769835
git_tree *index_tree = NULL;
770836
git_tree *index_parent_tree = NULL;
771837
git_tree *untracked_tree = NULL;
838+
git_index *stash_adds = NULL;
772839
git_index *repo_index = NULL;
773840
git_index *unstashed_index = NULL;
774841
git_index *modified_index = NULL;
@@ -813,6 +880,16 @@ int git_stash_apply(
813880
error = GIT_ECONFLICT;
814881
goto cleanup;
815882
}
883+
884+
/* Otherwise, stage any new files in the stash tree. (Note: their
885+
* previously unstaged contents are staged, not the previously staged.)
886+
*/
887+
} else if ((opts.flags & GIT_STASH_APPLY_REINSTATE_INDEX) == 0) {
888+
if ((error = stage_new_files(
889+
&stash_adds, repo, stash_parent_tree, stash_tree)) < 0 ||
890+
(error = merge_indexes(
891+
&unstashed_index, repo, stash_parent_tree, repo_index, stash_adds)) < 0)
892+
goto cleanup;
816893
}
817894

818895
NOTIFY_PROGRESS(opts, GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED);
@@ -874,6 +951,7 @@ int git_stash_apply(
874951
git_index_free(untracked_index);
875952
git_index_free(modified_index);
876953
git_index_free(unstashed_index);
954+
git_index_free(stash_adds);
877955
git_index_free(repo_index);
878956
git_tree_free(untracked_tree);
879957
git_tree_free(index_parent_tree);

tests/stash/apply.c

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ void test_stash_apply__initialize(void)
1717
cl_git_mkfile("stash/what", "hello\n");
1818
cl_git_mkfile("stash/how", "small\n");
1919
cl_git_mkfile("stash/who", "world\n");
20+
cl_git_mkfile("stash/where", "meh\n");
2021

2122
cl_git_pass(git_index_add_bypath(repo_index, "what"));
2223
cl_git_pass(git_index_add_bypath(repo_index, "how"));
@@ -28,16 +29,22 @@ void test_stash_apply__initialize(void)
2829
cl_git_rewritefile("stash/who", "funky world\n");
2930
cl_git_mkfile("stash/when", "tomorrow\n");
3031
cl_git_mkfile("stash/why", "would anybody use stash?\n");
32+
cl_git_mkfile("stash/where", "????\n");
3133

3234
cl_git_pass(git_index_add_bypath(repo_index, "who"));
3335
cl_git_pass(git_index_add_bypath(repo_index, "why"));
36+
cl_git_pass(git_index_add_bypath(repo_index, "where"));
37+
git_index_write(repo_index);
38+
39+
cl_git_rewritefile("stash/where", "....\n");
3440

3541
/* Pre-stash state */
3642
assert_status(repo, "what", GIT_STATUS_WT_MODIFIED);
3743
assert_status(repo, "how", GIT_STATUS_CURRENT);
3844
assert_status(repo, "who", GIT_STATUS_INDEX_MODIFIED);
3945
assert_status(repo, "when", GIT_STATUS_WT_NEW);
4046
assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
47+
assert_status(repo, "where", GIT_STATUS_INDEX_NEW|GIT_STATUS_WT_MODIFIED);
4148

4249
cl_git_pass(git_stash_save(&oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
4350

@@ -47,6 +54,7 @@ void test_stash_apply__initialize(void)
4754
assert_status(repo, "who", GIT_STATUS_CURRENT);
4855
assert_status(repo, "when", GIT_ENOTFOUND);
4956
assert_status(repo, "why", GIT_ENOTFOUND);
57+
assert_status(repo, "where", GIT_ENOTFOUND);
5058
}
5159

5260
void test_stash_apply__cleanup(void)
@@ -62,18 +70,51 @@ void test_stash_apply__cleanup(void)
6270

6371
void test_stash_apply__with_default(void)
6472
{
73+
git_buf where = GIT_BUF_INIT;
74+
6575
cl_git_pass(git_stash_apply(repo, 0, NULL));
6676

6777
cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
6878
assert_status(repo, "what", GIT_STATUS_WT_MODIFIED);
6979
assert_status(repo, "how", GIT_STATUS_CURRENT);
7080
assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
7181
assert_status(repo, "when", GIT_STATUS_WT_NEW);
72-
assert_status(repo, "why", GIT_STATUS_WT_NEW);
82+
assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
83+
assert_status(repo, "where", GIT_STATUS_INDEX_NEW);
84+
85+
cl_git_pass(git_futils_readbuffer(&where, "stash/where"));
86+
cl_assert_equal_s("....\n", where.ptr);
87+
88+
git_buf_free(&where);
89+
}
90+
91+
void test_stash_apply__with_existing_file(void)
92+
{
93+
cl_git_mkfile("stash/where", "oops!\n");
94+
cl_git_fail(git_stash_apply(repo, 0, NULL));
95+
}
96+
97+
void test_stash_apply__merges_new_file(void)
98+
{
99+
git_index_entry *ancestor, *our, *their;
100+
101+
cl_git_mkfile("stash/where", "committed before stash\n");
102+
cl_git_pass(git_index_add_bypath(repo_index, "where"));
103+
cl_repo_commit_from_index(NULL, repo, signature, 0, "Other commit");
104+
105+
cl_git_pass(git_stash_apply(repo, 0, NULL));
106+
107+
cl_assert_equal_i(1, git_index_has_conflicts(repo_index));
108+
assert_status(repo, "what", GIT_STATUS_INDEX_MODIFIED);
109+
cl_git_pass(git_index_conflict_get(&ancestor, &our, &their, repo_index, "where")); /* unmerged */
110+
assert_status(repo, "who", GIT_STATUS_INDEX_MODIFIED);
111+
assert_status(repo, "when", GIT_STATUS_WT_NEW);
112+
assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
73113
}
74114

75115
void test_stash_apply__with_reinstate_index(void)
76116
{
117+
git_buf where = GIT_BUF_INIT;
77118
git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
78119

79120
opts.flags = GIT_STASH_APPLY_REINSTATE_INDEX;
@@ -86,6 +127,12 @@ void test_stash_apply__with_reinstate_index(void)
86127
assert_status(repo, "who", GIT_STATUS_INDEX_MODIFIED);
87128
assert_status(repo, "when", GIT_STATUS_WT_NEW);
88129
assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
130+
assert_status(repo, "where", GIT_STATUS_INDEX_NEW | GIT_STATUS_WT_MODIFIED);
131+
132+
cl_git_pass(git_futils_readbuffer(&where, "stash/where"));
133+
cl_assert_equal_s("....\n", where.ptr);
134+
135+
git_buf_free(&where);
89136
}
90137

91138
void test_stash_apply__conflict_index_with_default(void)
@@ -312,7 +359,8 @@ void test_stash_apply__executes_notify_cb(void)
312359
assert_status(repo, "how", GIT_STATUS_CURRENT);
313360
assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
314361
assert_status(repo, "when", GIT_STATUS_WT_NEW);
315-
assert_status(repo, "why", GIT_STATUS_WT_NEW);
362+
assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
363+
assert_status(repo, "where", GIT_STATUS_INDEX_NEW);
316364

317365
cl_assert_equal_b(true, seen_paths.what);
318366
cl_assert_equal_b(false, seen_paths.how);

0 commit comments

Comments
 (0)