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

Skip to content

Commit c2f274c

Browse files
committed
Merge pull request libgit2#3250 from ethomson/stash
Stash workdir correctly when added in the index, modified in the workdir
2 parents aacfd03 + 9017711 commit c2f274c

File tree

6 files changed

+95
-71
lines changed

6 files changed

+95
-71
lines changed

src/diff.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,25 @@ extern int git_diff_find_similar__calc_similarity(
123123
extern int git_diff__commit(
124124
git_diff **diff, git_repository *repo, const git_commit *commit, const git_diff_options *opts);
125125

126+
/* Merge two `git_diff`s according to the callback given by `cb`. */
127+
128+
typedef git_diff_delta *(*git_diff__merge_cb)(
129+
const git_diff_delta *left,
130+
const git_diff_delta *right,
131+
git_pool *pool);
132+
133+
extern int git_diff__merge(
134+
git_diff *onto, const git_diff *from, git_diff__merge_cb cb);
135+
136+
extern git_diff_delta *git_diff__merge_like_cgit(
137+
const git_diff_delta *a,
138+
const git_diff_delta *b,
139+
git_pool *pool);
140+
141+
/* Duplicate a `git_diff_delta` out of the `git_pool` */
142+
extern git_diff_delta *git_diff__delta_dup(
143+
const git_diff_delta *d, git_pool *pool);
144+
126145
/*
127146
* Sometimes a git_diff_file will have a zero size; this attempts to
128147
* fill in the size without loading the blob if possible. If that is

src/diff_tform.c

Lines changed: 24 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
#include "fileops.h"
1616
#include "config.h"
1717

18-
static git_diff_delta *diff_delta__dup(
18+
git_diff_delta *git_diff__delta_dup(
1919
const git_diff_delta *d, git_pool *pool)
2020
{
2121
git_diff_delta *delta = git__malloc(sizeof(git_diff_delta));
@@ -46,7 +46,7 @@ static git_diff_delta *diff_delta__dup(
4646
return NULL;
4747
}
4848

49-
static git_diff_delta *diff_delta__merge_like_cgit(
49+
git_diff_delta *git_diff__merge_like_cgit(
5050
const git_diff_delta *a,
5151
const git_diff_delta *b,
5252
git_pool *pool)
@@ -67,23 +67,24 @@ static git_diff_delta *diff_delta__merge_like_cgit(
6767

6868
/* If one of the diffs is a conflict, just dup it */
6969
if (b->status == GIT_DELTA_CONFLICTED)
70-
return diff_delta__dup(b, pool);
70+
return git_diff__delta_dup(b, pool);
7171
if (a->status == GIT_DELTA_CONFLICTED)
72-
return diff_delta__dup(a, pool);
72+
return git_diff__delta_dup(a, pool);
7373

7474
/* if f2 == f3 or f2 is deleted, then just dup the 'a' diff */
7575
if (b->status == GIT_DELTA_UNMODIFIED || a->status == GIT_DELTA_DELETED)
76-
return diff_delta__dup(a, pool);
76+
return git_diff__delta_dup(a, pool);
7777

7878
/* otherwise, base this diff on the 'b' diff */
79-
if ((dup = diff_delta__dup(b, pool)) == NULL)
79+
if ((dup = git_diff__delta_dup(b, pool)) == NULL)
8080
return NULL;
8181

8282
/* If 'a' status is uninteresting, then we're done */
83-
if (a->status == GIT_DELTA_UNMODIFIED)
83+
if (a->status == GIT_DELTA_UNMODIFIED ||
84+
a->status == GIT_DELTA_UNTRACKED ||
85+
a->status == GIT_DELTA_UNREADABLE)
8486
return dup;
8587

86-
assert(a->status != GIT_DELTA_UNMODIFIED);
8788
assert(b->status != GIT_DELTA_UNMODIFIED);
8889

8990
/* A cgit exception is that the diff of a file that is only in the
@@ -108,48 +109,8 @@ static git_diff_delta *diff_delta__merge_like_cgit(
108109
return dup;
109110
}
110111

111-
static git_diff_delta *diff_delta__merge_like_cgit_reversed(
112-
const git_diff_delta *a,
113-
const git_diff_delta *b,
114-
git_pool *pool)
115-
{
116-
git_diff_delta *dup;
117-
118-
/* reversed version of above logic */
119-
120-
if (a->status == GIT_DELTA_CONFLICTED)
121-
return diff_delta__dup(a, pool);
122-
if (b->status == GIT_DELTA_CONFLICTED)
123-
return diff_delta__dup(b, pool);
124-
125-
if (a->status == GIT_DELTA_UNMODIFIED)
126-
return diff_delta__dup(b, pool);
127-
128-
if ((dup = diff_delta__dup(a, pool)) == NULL)
129-
return NULL;
130-
131-
if (b->status == GIT_DELTA_UNMODIFIED || b->status == GIT_DELTA_UNTRACKED || b->status == GIT_DELTA_UNREADABLE)
132-
return dup;
133-
134-
if (dup->status == GIT_DELTA_DELETED) {
135-
if (b->status == GIT_DELTA_ADDED) {
136-
dup->status = GIT_DELTA_UNMODIFIED;
137-
dup->nfiles = 2;
138-
}
139-
} else {
140-
dup->status = b->status;
141-
dup->nfiles = b->nfiles;
142-
}
143-
144-
git_oid_cpy(&dup->old_file.id, &b->old_file.id);
145-
dup->old_file.mode = b->old_file.mode;
146-
dup->old_file.size = b->old_file.size;
147-
dup->old_file.flags = b->old_file.flags;
148-
149-
return dup;
150-
}
151-
152-
int git_diff_merge(git_diff *onto, const git_diff *from)
112+
int git_diff__merge(
113+
git_diff *onto, const git_diff *from, git_diff__merge_cb cb)
153114
{
154115
int error = 0;
155116
git_pool onto_pool;
@@ -185,23 +146,24 @@ int git_diff_merge(git_diff *onto, const git_diff *from)
185146
STRCMP_CASESELECT(ignore_case, o->old_file.path, f->old_file.path);
186147

187148
if (cmp < 0) {
188-
delta = diff_delta__dup(o, &onto_pool);
149+
delta = git_diff__delta_dup(o, &onto_pool);
189150
i++;
190151
} else if (cmp > 0) {
191-
delta = diff_delta__dup(f, &onto_pool);
152+
delta = git_diff__delta_dup(f, &onto_pool);
192153
j++;
193154
} else {
194-
delta = reversed ?
195-
diff_delta__merge_like_cgit_reversed(o, f, &onto_pool) :
196-
diff_delta__merge_like_cgit(o, f, &onto_pool);
155+
const git_diff_delta *left = reversed ? f : o;
156+
const git_diff_delta *right = reversed ? o : f;
157+
158+
delta = cb(left, right, &onto_pool);
197159
i++;
198160
j++;
199161
}
200162

201163
/* the ignore rules for the target may not match the source
202164
* or the result of a merged delta could be skippable...
203165
*/
204-
if (git_diff_delta__should_skip(&onto->opts, delta)) {
166+
if (delta && git_diff_delta__should_skip(&onto->opts, delta)) {
205167
git__free(delta);
206168
continue;
207169
}
@@ -232,6 +194,11 @@ int git_diff_merge(git_diff *onto, const git_diff *from)
232194
return error;
233195
}
234196

197+
int git_diff_merge(git_diff *onto, const git_diff *from)
198+
{
199+
return git_diff__merge(onto, from, git_diff__merge_like_cgit);
200+
}
201+
235202
int git_diff_find_similar__hashsig_for_file(
236203
void **out, const git_diff_file *f, const char *path, void *p)
237204
{
@@ -380,7 +347,7 @@ static int insert_delete_side_of_split(
380347
git_diff *diff, git_vector *onto, const git_diff_delta *delta)
381348
{
382349
/* make new record for DELETED side of split */
383-
git_diff_delta *deleted = diff_delta__dup(delta, &diff->pool);
350+
git_diff_delta *deleted = git_diff__delta_dup(delta, &diff->pool);
384351
GITERR_CHECK_ALLOC(deleted);
385352

386353
deleted->status = GIT_DELTA_DELETED;

src/stash.c

Lines changed: 26 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "signature.h"
2323
#include "iterator.h"
2424
#include "merge.h"
25+
#include "diff.h"
2526

2627
static int create_error(int error, const char *msg)
2728
{
@@ -292,24 +293,45 @@ static int commit_untracked(
292293
return error;
293294
}
294295

296+
static git_diff_delta *stash_delta_merge(
297+
const git_diff_delta *a,
298+
const git_diff_delta *b,
299+
git_pool *pool)
300+
{
301+
/* Special case for stash: if a file is deleted in the index, but exists
302+
* in the working tree, we need to stash the workdir copy for the workdir.
303+
*/
304+
if (a->status == GIT_DELTA_DELETED && b->status == GIT_DELTA_UNTRACKED) {
305+
git_diff_delta *dup = git_diff__delta_dup(b, pool);
306+
307+
if (dup)
308+
dup->status = GIT_DELTA_MODIFIED;
309+
return dup;
310+
}
311+
312+
return git_diff__merge_like_cgit(a, b, pool);
313+
}
314+
295315
static int build_workdir_tree(
296316
git_tree **tree_out,
297317
git_index *index,
298318
git_commit *b_commit)
299319
{
300320
git_repository *repo = git_index_owner(index);
301321
git_tree *b_tree = NULL;
302-
git_diff *diff = NULL;
322+
git_diff *diff = NULL, *idx_to_wd = NULL;
303323
git_diff_options opts = GIT_DIFF_OPTIONS_INIT;
304324
struct stash_update_rules data = {0};
305325
int error;
306326

307-
opts.flags = GIT_DIFF_IGNORE_SUBMODULES;
327+
opts.flags = GIT_DIFF_IGNORE_SUBMODULES | GIT_DIFF_INCLUDE_UNTRACKED;
308328

309329
if ((error = git_commit_tree(&b_tree, b_commit)) < 0)
310330
goto cleanup;
311331

312-
if ((error = git_diff_tree_to_workdir(&diff, repo, b_tree, &opts)) < 0)
332+
if ((error = git_diff_tree_to_index(&diff, repo, b_tree, index, &opts)) < 0 ||
333+
(error = git_diff_index_to_workdir(&idx_to_wd, repo, index, &opts)) < 0 ||
334+
(error = git_diff__merge(diff, idx_to_wd, stash_delta_merge)) < 0)
313335
goto cleanup;
314336

315337
data.include_changed = true;
@@ -320,6 +342,7 @@ static int build_workdir_tree(
320342
error = build_tree_from_index(tree_out, index);
321343

322344
cleanup:
345+
git_diff_free(idx_to_wd);
323346
git_diff_free(diff);
324347
git_tree_free(b_tree);
325348

tests/stash/foreach.c

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,16 +69,16 @@ void test_stash_foreach__enumerating_a_empty_repository_doesnt_fail(void)
6969
void test_stash_foreach__can_enumerate_a_repository(void)
7070
{
7171
char *oids_default[] = {
72-
"1d91c842a7cdfc25872b3a763e5c31add8816c25", NULL };
72+
"493568b7a2681187aaac8a58d3f1eab1527cba84", NULL };
7373

7474
char *oids_untracked[] = {
7575
"7f89a8b15c878809c5c54d1ff8f8c9674154017b",
76-
"1d91c842a7cdfc25872b3a763e5c31add8816c25", NULL };
76+
"493568b7a2681187aaac8a58d3f1eab1527cba84", NULL };
7777

7878
char *oids_ignored[] = {
7979
"c95599a8fef20a7e57582c6727b1a0d02e0a5828",
8080
"7f89a8b15c878809c5c54d1ff8f8c9674154017b",
81-
"1d91c842a7cdfc25872b3a763e5c31add8816c25", NULL };
81+
"493568b7a2681187aaac8a58d3f1eab1527cba84", NULL };
8282

8383
cl_git_pass(git_repository_init(&repo, REPO_NAME, 0));
8484

@@ -96,29 +96,31 @@ void test_stash_foreach__can_enumerate_a_repository(void)
9696
cl_git_pass(git_stash_foreach(repo, callback_cb, &data));
9797
cl_assert_equal_i(1, data.invokes);
9898

99-
data.oids = oids_untracked;
100-
data.invokes = 0;
101-
99+
/* ensure stash_foreach operates with INCLUDE_UNTRACKED */
102100
cl_git_pass(git_stash_save(
103101
&stash_tip_oid,
104102
repo,
105103
signature,
106104
NULL,
107105
GIT_STASH_INCLUDE_UNTRACKED));
108106

107+
data.oids = oids_untracked;
108+
data.invokes = 0;
109+
109110
cl_git_pass(git_stash_foreach(repo, callback_cb, &data));
110111
cl_assert_equal_i(2, data.invokes);
111112

112-
data.oids = oids_ignored;
113-
data.invokes = 0;
114-
113+
/* ensure stash_foreach operates with INCLUDE_IGNORED */
115114
cl_git_pass(git_stash_save(
116115
&stash_tip_oid,
117116
repo,
118117
signature,
119118
NULL,
120119
GIT_STASH_INCLUDE_IGNORED));
121120

121+
data.oids = oids_ignored;
122+
data.invokes = 0;
123+
122124
cl_git_pass(git_stash_foreach(repo, callback_cb, &data));
123125
cl_assert_equal_i(3, data.invokes);
124126
}

tests/stash/save.c

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,12 +100,18 @@ fatal: Path 'when' exists on disk, but not in 'stash^2'.
100100
assert_blob_oid("refs/stash:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
101101
assert_blob_oid("refs/stash:who", "a0400d4954659306a976567af43125a0b1aa8595"); /* funky world */
102102
assert_blob_oid("refs/stash:when", NULL);
103+
assert_blob_oid("refs/stash:why", "88c2533e21f098b89c91a431d8075cbdbe422a51"); /* would anybody use stash? */
104+
assert_blob_oid("refs/stash:where", "e3d6434ec12eb76af8dfa843a64ba6ab91014a0b"); /* .... */
105+
assert_blob_oid("refs/stash:.gitignore", "ac4d88de61733173d9959e4b77c69b9f17a00980");
103106
assert_blob_oid("refs/stash:just.ignore", NULL);
104107

105108
assert_blob_oid("refs/stash^2:what", "dd7e1c6f0fefe118f0b63d9f10908c460aa317a6"); /* goodbye */
106109
assert_blob_oid("refs/stash^2:how", "e6d64adb2c7f3eb8feb493b556cc8070dca379a3"); /* not so small and */
107110
assert_blob_oid("refs/stash^2:who", "cc628ccd10742baea8241c5924df992b5c019f71"); /* world */
108111
assert_blob_oid("refs/stash^2:when", NULL);
112+
assert_blob_oid("refs/stash^2:why", "88c2533e21f098b89c91a431d8075cbdbe422a51"); /* would anybody use stash? */
113+
assert_blob_oid("refs/stash^2:where", "e08f7fbb9a42a0c5367cf8b349f1f08c3d56bd72"); /* ???? */
114+
assert_blob_oid("refs/stash^2:.gitignore", "ac4d88de61733173d9959e4b77c69b9f17a00980");
109115
assert_blob_oid("refs/stash^2:just.ignore", NULL);
110116

111117
assert_blob_oid("refs/stash^3", NULL);
@@ -243,11 +249,13 @@ void test_stash_save__cannot_stash_when_there_are_no_local_change(void)
243249
cl_git_pass(git_repository_index(&index, repo));
244250

245251
/*
246-
* 'what' and 'who' are being committed.
247-
* 'when' remain untracked.
252+
* 'what', 'where' and 'who' are being committed.
253+
* 'when' remains untracked.
248254
*/
249255
cl_git_pass(git_index_add_bypath(index, "what"));
256+
cl_git_pass(git_index_add_bypath(index, "where"));
250257
cl_git_pass(git_index_add_bypath(index, "who"));
258+
251259
cl_repo_commit_from_index(NULL, repo, signature, 0, "Initial commit");
252260
git_index_free(index);
253261

tests/stash/stash_helpers.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,12 +26,17 @@ void setup_stash(git_repository *repo, git_signature *signature)
2626
cl_git_rewritefile("stash/what", "goodbye\n"); /* dd7e1c6f0fefe118f0b63d9f10908c460aa317a6 */
2727
cl_git_rewritefile("stash/how", "not so small and\n"); /* e6d64adb2c7f3eb8feb493b556cc8070dca379a3 */
2828
cl_git_rewritefile("stash/who", "funky world\n"); /* a0400d4954659306a976567af43125a0b1aa8595 */
29+
cl_git_mkfile("stash/why", "would anybody use stash?\n"); /* 88c2533e21f098b89c91a431d8075cbde422a51 */
30+
cl_git_mkfile("stash/where", "????\n"); /* e08f7fbb9a42a0c5367cf8b349f1f08c3d56bd72 */
2931

3032
cl_git_pass(git_index_add_bypath(index, "what"));
3133
cl_git_pass(git_index_add_bypath(index, "how"));
34+
cl_git_pass(git_index_add_bypath(index, "why"));
35+
cl_git_pass(git_index_add_bypath(index, "where"));
3236
cl_git_pass(git_index_write(index));
3337

3438
cl_git_rewritefile("stash/what", "see you later\n"); /* bc99dc98b3eba0e9157e94769cd4d49cb49de449 */
39+
cl_git_mkfile("stash/where", "....\n"); /* e3d6434ec12eb76af8dfa843a64ba6ab91014a0b */
3540

3641
git_index_free(index);
3742
}

0 commit comments

Comments
 (0)