1 #include "clar_libgit2.h"
2 #include "futils.h"
3 #include "stash_helpers.h"
4
5 static git_signature *signature;
6 static git_repository *repo;
7 static git_index *repo_index;
8
test_stash_apply__initialize(void)9 void test_stash_apply__initialize(void)
10 {
11 git_oid oid;
12
13 repo = cl_git_sandbox_init_new("stash");
14 cl_git_pass(git_repository_index(&repo_index, repo));
15 cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */
16
17 cl_git_mkfile("stash/what", "hello\n");
18 cl_git_mkfile("stash/how", "small\n");
19 cl_git_mkfile("stash/who", "world\n");
20 cl_git_mkfile("stash/where", "meh\n");
21
22 cl_git_pass(git_index_add_bypath(repo_index, "what"));
23 cl_git_pass(git_index_add_bypath(repo_index, "how"));
24 cl_git_pass(git_index_add_bypath(repo_index, "who"));
25
26 cl_repo_commit_from_index(NULL, repo, signature, 0, "Initial commit");
27
28 cl_git_rewritefile("stash/what", "goodbye\n");
29 cl_git_rewritefile("stash/who", "funky world\n");
30 cl_git_mkfile("stash/when", "tomorrow\n");
31 cl_git_mkfile("stash/why", "would anybody use stash?\n");
32 cl_git_mkfile("stash/where", "????\n");
33
34 cl_git_pass(git_index_add_bypath(repo_index, "who"));
35 cl_git_pass(git_index_add_bypath(repo_index, "why"));
36 cl_git_pass(git_index_add_bypath(repo_index, "where"));
37 cl_git_pass(git_index_write(repo_index));
38
39 cl_git_rewritefile("stash/where", "....\n");
40
41 /* Pre-stash state */
42 assert_status(repo, "what", GIT_STATUS_WT_MODIFIED);
43 assert_status(repo, "how", GIT_STATUS_CURRENT);
44 assert_status(repo, "who", GIT_STATUS_INDEX_MODIFIED);
45 assert_status(repo, "when", GIT_STATUS_WT_NEW);
46 assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
47 assert_status(repo, "where", GIT_STATUS_INDEX_NEW|GIT_STATUS_WT_MODIFIED);
48
49 cl_git_pass(git_stash_save(&oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
50
51 /* Post-stash state */
52 assert_status(repo, "what", GIT_STATUS_CURRENT);
53 assert_status(repo, "how", GIT_STATUS_CURRENT);
54 assert_status(repo, "who", GIT_STATUS_CURRENT);
55 assert_status(repo, "when", GIT_ENOTFOUND);
56 assert_status(repo, "why", GIT_ENOTFOUND);
57 assert_status(repo, "where", GIT_ENOTFOUND);
58 }
59
test_stash_apply__cleanup(void)60 void test_stash_apply__cleanup(void)
61 {
62 git_signature_free(signature);
63 signature = NULL;
64
65 git_index_free(repo_index);
66 repo_index = NULL;
67
68 cl_git_sandbox_cleanup();
69 }
70
test_stash_apply__with_default(void)71 void test_stash_apply__with_default(void)
72 {
73 git_buf where = GIT_BUF_INIT;
74
75 cl_git_pass(git_stash_apply(repo, 0, NULL));
76
77 cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
78 assert_status(repo, "what", GIT_STATUS_WT_MODIFIED);
79 assert_status(repo, "how", GIT_STATUS_CURRENT);
80 assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
81 assert_status(repo, "when", 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_dispose(&where);
89 }
90
test_stash_apply__with_existing_file(void)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
test_stash_apply__merges_new_file(void)97 void test_stash_apply__merges_new_file(void)
98 {
99 const 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);
113 }
114
test_stash_apply__with_reinstate_index(void)115 void test_stash_apply__with_reinstate_index(void)
116 {
117 git_buf where = GIT_BUF_INIT;
118 git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
119
120 opts.flags = GIT_STASH_APPLY_REINSTATE_INDEX;
121
122 cl_git_pass(git_stash_apply(repo, 0, &opts));
123
124 cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
125 assert_status(repo, "what", GIT_STATUS_WT_MODIFIED);
126 assert_status(repo, "how", GIT_STATUS_CURRENT);
127 assert_status(repo, "who", GIT_STATUS_INDEX_MODIFIED);
128 assert_status(repo, "when", GIT_STATUS_WT_NEW);
129 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_dispose(&where);
136 }
137
test_stash_apply__conflict_index_with_default(void)138 void test_stash_apply__conflict_index_with_default(void)
139 {
140 const git_index_entry *ancestor;
141 const git_index_entry *our;
142 const git_index_entry *their;
143
144 cl_git_rewritefile("stash/who", "nothing\n");
145 cl_git_pass(git_index_add_bypath(repo_index, "who"));
146 cl_git_pass(git_index_write(repo_index));
147 cl_repo_commit_from_index(NULL, repo, signature, 0, "Other commit");
148
149 cl_git_pass(git_stash_apply(repo, 0, NULL));
150
151 cl_assert_equal_i(git_index_has_conflicts(repo_index), 1);
152 assert_status(repo, "what", GIT_STATUS_INDEX_MODIFIED);
153 assert_status(repo, "how", GIT_STATUS_CURRENT);
154 cl_git_pass(git_index_conflict_get(&ancestor, &our, &their, repo_index, "who")); /* unmerged */
155 assert_status(repo, "when", GIT_STATUS_WT_NEW);
156 assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
157 }
158
test_stash_apply__conflict_index_with_reinstate_index(void)159 void test_stash_apply__conflict_index_with_reinstate_index(void)
160 {
161 git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
162
163 opts.flags = GIT_STASH_APPLY_REINSTATE_INDEX;
164
165 cl_git_rewritefile("stash/who", "nothing\n");
166 cl_git_pass(git_index_add_bypath(repo_index, "who"));
167 cl_git_pass(git_index_write(repo_index));
168 cl_repo_commit_from_index(NULL, repo, signature, 0, "Other commit");
169
170 cl_git_fail_with(git_stash_apply(repo, 0, &opts), GIT_ECONFLICT);
171
172 cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
173 assert_status(repo, "what", GIT_STATUS_CURRENT);
174 assert_status(repo, "how", GIT_STATUS_CURRENT);
175 assert_status(repo, "who", GIT_STATUS_CURRENT);
176 assert_status(repo, "when", GIT_ENOTFOUND);
177 assert_status(repo, "why", GIT_ENOTFOUND);
178 }
179
test_stash_apply__conflict_untracked_with_default(void)180 void test_stash_apply__conflict_untracked_with_default(void)
181 {
182 git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
183
184 cl_git_mkfile("stash/when", "nothing\n");
185
186 cl_git_fail_with(git_stash_apply(repo, 0, &opts), GIT_ECONFLICT);
187
188 cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
189 assert_status(repo, "what", GIT_STATUS_CURRENT);
190 assert_status(repo, "how", GIT_STATUS_CURRENT);
191 assert_status(repo, "who", GIT_STATUS_CURRENT);
192 assert_status(repo, "when", GIT_STATUS_WT_NEW);
193 assert_status(repo, "why", GIT_ENOTFOUND);
194 }
195
test_stash_apply__conflict_untracked_with_reinstate_index(void)196 void test_stash_apply__conflict_untracked_with_reinstate_index(void)
197 {
198 git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
199
200 opts.flags = GIT_STASH_APPLY_REINSTATE_INDEX;
201
202 cl_git_mkfile("stash/when", "nothing\n");
203
204 cl_git_fail_with(git_stash_apply(repo, 0, &opts), GIT_ECONFLICT);
205
206 cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
207 assert_status(repo, "what", GIT_STATUS_CURRENT);
208 assert_status(repo, "how", GIT_STATUS_CURRENT);
209 assert_status(repo, "who", GIT_STATUS_CURRENT);
210 assert_status(repo, "when", GIT_STATUS_WT_NEW);
211 assert_status(repo, "why", GIT_ENOTFOUND);
212 }
213
test_stash_apply__conflict_workdir_with_default(void)214 void test_stash_apply__conflict_workdir_with_default(void)
215 {
216 cl_git_rewritefile("stash/what", "ciao\n");
217
218 cl_git_fail_with(git_stash_apply(repo, 0, NULL), GIT_ECONFLICT);
219
220 cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
221 assert_status(repo, "what", GIT_STATUS_WT_MODIFIED);
222 assert_status(repo, "how", GIT_STATUS_CURRENT);
223 assert_status(repo, "who", GIT_STATUS_CURRENT);
224 assert_status(repo, "when", GIT_STATUS_WT_NEW);
225 assert_status(repo, "why", GIT_ENOTFOUND);
226 }
227
test_stash_apply__conflict_workdir_with_reinstate_index(void)228 void test_stash_apply__conflict_workdir_with_reinstate_index(void)
229 {
230 git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
231
232 opts.flags = GIT_STASH_APPLY_REINSTATE_INDEX;
233
234 cl_git_rewritefile("stash/what", "ciao\n");
235
236 cl_git_fail_with(git_stash_apply(repo, 0, &opts), GIT_ECONFLICT);
237
238 cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
239 assert_status(repo, "what", GIT_STATUS_WT_MODIFIED);
240 assert_status(repo, "how", GIT_STATUS_CURRENT);
241 assert_status(repo, "who", GIT_STATUS_CURRENT);
242 assert_status(repo, "when", GIT_STATUS_WT_NEW);
243 assert_status(repo, "why", GIT_ENOTFOUND);
244 }
245
test_stash_apply__conflict_commit_with_default(void)246 void test_stash_apply__conflict_commit_with_default(void)
247 {
248 const git_index_entry *ancestor;
249 const git_index_entry *our;
250 const git_index_entry *their;
251
252 cl_git_rewritefile("stash/what", "ciao\n");
253 cl_git_pass(git_index_add_bypath(repo_index, "what"));
254 cl_repo_commit_from_index(NULL, repo, signature, 0, "Other commit");
255
256 cl_git_pass(git_stash_apply(repo, 0, NULL));
257
258 cl_assert_equal_i(git_index_has_conflicts(repo_index), 1);
259 cl_git_pass(git_index_conflict_get(&ancestor, &our, &their, repo_index, "what")); /* unmerged */
260 assert_status(repo, "how", GIT_STATUS_CURRENT);
261 assert_status(repo, "who", GIT_STATUS_INDEX_MODIFIED);
262 assert_status(repo, "when", GIT_STATUS_WT_NEW);
263 assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
264 }
265
test_stash_apply__conflict_commit_with_reinstate_index(void)266 void test_stash_apply__conflict_commit_with_reinstate_index(void)
267 {
268 git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
269 const git_index_entry *ancestor;
270 const git_index_entry *our;
271 const git_index_entry *their;
272
273 opts.flags = GIT_STASH_APPLY_REINSTATE_INDEX;
274
275 cl_git_rewritefile("stash/what", "ciao\n");
276 cl_git_pass(git_index_add_bypath(repo_index, "what"));
277 cl_repo_commit_from_index(NULL, repo, signature, 0, "Other commit");
278
279 cl_git_pass(git_stash_apply(repo, 0, &opts));
280
281 cl_assert_equal_i(git_index_has_conflicts(repo_index), 1);
282 cl_git_pass(git_index_conflict_get(&ancestor, &our, &their, repo_index, "what")); /* unmerged */
283 assert_status(repo, "how", GIT_STATUS_CURRENT);
284 assert_status(repo, "who", GIT_STATUS_INDEX_MODIFIED);
285 assert_status(repo, "when", GIT_STATUS_WT_NEW);
286 assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
287 }
288
test_stash_apply__fails_with_uncommitted_changes_in_index(void)289 void test_stash_apply__fails_with_uncommitted_changes_in_index(void)
290 {
291 cl_git_rewritefile("stash/who", "nothing\n");
292 cl_git_pass(git_index_add_bypath(repo_index, "who"));
293 cl_git_pass(git_index_write(repo_index));
294
295 cl_git_fail_with(git_stash_apply(repo, 0, NULL), GIT_EUNCOMMITTED);
296
297 cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
298 assert_status(repo, "what", GIT_STATUS_CURRENT);
299 assert_status(repo, "how", GIT_STATUS_CURRENT);
300 assert_status(repo, "who", GIT_STATUS_INDEX_MODIFIED);
301 assert_status(repo, "when", GIT_ENOTFOUND);
302 assert_status(repo, "why", GIT_ENOTFOUND);
303 }
304
test_stash_apply__pop(void)305 void test_stash_apply__pop(void)
306 {
307 cl_git_pass(git_stash_pop(repo, 0, NULL));
308
309 cl_git_fail_with(git_stash_pop(repo, 0, NULL), GIT_ENOTFOUND);
310 }
311
312 struct seen_paths {
313 bool what;
314 bool how;
315 bool who;
316 bool when;
317 };
318
checkout_notify(git_checkout_notify_t why,const char * path,const git_diff_file * baseline,const git_diff_file * target,const git_diff_file * workdir,void * payload)319 int checkout_notify(
320 git_checkout_notify_t why,
321 const char *path,
322 const git_diff_file *baseline,
323 const git_diff_file *target,
324 const git_diff_file *workdir,
325 void *payload)
326 {
327 struct seen_paths *seen_paths = (struct seen_paths *)payload;
328
329 GIT_UNUSED(why);
330 GIT_UNUSED(baseline);
331 GIT_UNUSED(target);
332 GIT_UNUSED(workdir);
333
334 if (strcmp(path, "what") == 0)
335 seen_paths->what = 1;
336 else if (strcmp(path, "how") == 0)
337 seen_paths->how = 1;
338 else if (strcmp(path, "who") == 0)
339 seen_paths->who = 1;
340 else if (strcmp(path, "when") == 0)
341 seen_paths->when = 1;
342
343 return 0;
344 }
345
test_stash_apply__executes_notify_cb(void)346 void test_stash_apply__executes_notify_cb(void)
347 {
348 git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
349 struct seen_paths seen_paths = {0};
350
351 opts.checkout_options.notify_cb = checkout_notify;
352 opts.checkout_options.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
353 opts.checkout_options.notify_payload = &seen_paths;
354
355 cl_git_pass(git_stash_apply(repo, 0, &opts));
356
357 cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
358 assert_status(repo, "what", GIT_STATUS_WT_MODIFIED);
359 assert_status(repo, "how", GIT_STATUS_CURRENT);
360 assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
361 assert_status(repo, "when", GIT_STATUS_WT_NEW);
362 assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
363 assert_status(repo, "where", GIT_STATUS_INDEX_NEW);
364
365 cl_assert_equal_b(true, seen_paths.what);
366 cl_assert_equal_b(false, seen_paths.how);
367 cl_assert_equal_b(true, seen_paths.who);
368 cl_assert_equal_b(true, seen_paths.when);
369 }
370
progress_cb(git_stash_apply_progress_t progress,void * payload)371 int progress_cb(
372 git_stash_apply_progress_t progress,
373 void *payload)
374 {
375 git_stash_apply_progress_t *p = (git_stash_apply_progress_t *)payload;
376
377 cl_assert_equal_i((*p)+1, progress);
378
379 *p = progress;
380
381 return 0;
382 }
383
test_stash_apply__calls_progress_cb(void)384 void test_stash_apply__calls_progress_cb(void)
385 {
386 git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
387 git_stash_apply_progress_t progress = GIT_STASH_APPLY_PROGRESS_NONE;
388
389 opts.progress_cb = progress_cb;
390 opts.progress_payload = &progress;
391
392 cl_git_pass(git_stash_apply(repo, 0, &opts));
393 cl_assert_equal_i(progress, GIT_STASH_APPLY_PROGRESS_DONE);
394 }
395
aborting_progress_cb(git_stash_apply_progress_t progress,void * payload)396 int aborting_progress_cb(
397 git_stash_apply_progress_t progress,
398 void *payload)
399 {
400 GIT_UNUSED(payload);
401
402 if (progress == GIT_STASH_APPLY_PROGRESS_ANALYZE_MODIFIED)
403 return -44;
404
405 return 0;
406 }
407
test_stash_apply__progress_cb_can_abort(void)408 void test_stash_apply__progress_cb_can_abort(void)
409 {
410 git_stash_apply_options opts = GIT_STASH_APPLY_OPTIONS_INIT;
411
412 opts.progress_cb = aborting_progress_cb;
413
414 cl_git_fail_with(-44, git_stash_apply(repo, 0, &opts));
415 }
416
test_stash_apply__uses_reflog_like_indices_1(void)417 void test_stash_apply__uses_reflog_like_indices_1(void)
418 {
419 git_oid oid;
420
421 cl_git_mkfile("stash/untracked", "untracked\n");
422 cl_git_pass(git_stash_save(&oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
423 assert_status(repo, "untracked", GIT_ENOTFOUND);
424
425 /* stash@{1} is the oldest (first) stash we made */
426 cl_git_pass(git_stash_apply(repo, 1, NULL));
427 cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
428 assert_status(repo, "what", GIT_STATUS_WT_MODIFIED);
429 assert_status(repo, "how", GIT_STATUS_CURRENT);
430 assert_status(repo, "who", GIT_STATUS_WT_MODIFIED);
431 assert_status(repo, "when", GIT_STATUS_WT_NEW);
432 assert_status(repo, "why", GIT_STATUS_INDEX_NEW);
433 assert_status(repo, "where", GIT_STATUS_INDEX_NEW);
434 }
435
test_stash_apply__uses_reflog_like_indices_2(void)436 void test_stash_apply__uses_reflog_like_indices_2(void)
437 {
438 git_oid oid;
439
440 cl_git_mkfile("stash/untracked", "untracked\n");
441 cl_git_pass(git_stash_save(&oid, repo, signature, NULL, GIT_STASH_INCLUDE_UNTRACKED));
442 assert_status(repo, "untracked", GIT_ENOTFOUND);
443
444 /* stash@{0} is the newest stash we made immediately above */
445 cl_git_pass(git_stash_apply(repo, 0, NULL));
446
447 cl_assert_equal_i(git_index_has_conflicts(repo_index), 0);
448 assert_status(repo, "untracked", GIT_STATUS_WT_NEW);
449 }
450