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