1 #include "clar_libgit2.h"
2 #include "checkout_helpers.h"
3 
4 #include "git2/checkout.h"
5 #include "repository.h"
6 #include "buffer.h"
7 #include "futils.h"
8 
9 static git_repository *g_repo;
10 static git_checkout_options g_opts;
11 static git_object *g_object;
12 
assert_status_entrycount(git_repository * repo,size_t count)13 static void assert_status_entrycount(git_repository *repo, size_t count)
14 {
15 	git_status_list *status;
16 
17 	cl_git_pass(git_status_list_new(&status, repo, NULL));
18 	cl_assert_equal_i(count, git_status_list_entrycount(status));
19 
20 	git_status_list_free(status);
21 }
22 
test_checkout_tree__initialize(void)23 void test_checkout_tree__initialize(void)
24 {
25 	g_repo = cl_git_sandbox_init("testrepo");
26 
27 	GIT_INIT_STRUCTURE(&g_opts, GIT_CHECKOUT_OPTIONS_VERSION);
28 	g_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
29 }
30 
test_checkout_tree__cleanup(void)31 void test_checkout_tree__cleanup(void)
32 {
33 	git_object_free(g_object);
34 	g_object = NULL;
35 
36 	cl_git_sandbox_cleanup();
37 
38 	if (git_path_isdir("alternative"))
39 		git_futils_rmdir_r("alternative", NULL, GIT_RMDIR_REMOVE_FILES);
40 }
41 
test_checkout_tree__cannot_checkout_a_non_treeish(void)42 void test_checkout_tree__cannot_checkout_a_non_treeish(void)
43 {
44 	/* blob */
45 	cl_git_pass(git_revparse_single(&g_object, g_repo, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd"));
46 	cl_git_fail(git_checkout_tree(g_repo, g_object, NULL));
47 }
48 
test_checkout_tree__can_checkout_a_subdirectory_from_a_commit(void)49 void test_checkout_tree__can_checkout_a_subdirectory_from_a_commit(void)
50 {
51 	char *entries[] = { "ab/de/" };
52 
53 	g_opts.paths.strings = entries;
54 	g_opts.paths.count = 1;
55 
56 	cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees"));
57 
58 	cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
59 
60 	cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
61 
62 	cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/2.txt"));
63 	cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/fgh/1.txt"));
64 }
65 
test_checkout_tree__can_checkout_and_remove_directory(void)66 void test_checkout_tree__can_checkout_and_remove_directory(void)
67 {
68 	cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
69 
70 	/* Checkout brach "subtrees" and update HEAD, so that HEAD matches the
71 	 * current working tree
72 	 */
73 	cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees"));
74 	cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
75 
76 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees"));
77 
78 	cl_assert_equal_i(true, git_path_isdir("./testrepo/ab/"));
79 	cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/2.txt"));
80 	cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/fgh/1.txt"));
81 
82 	git_object_free(g_object);
83 	g_object = NULL;
84 
85 	/* Checkout brach "master" and update HEAD, so that HEAD matches the
86 	 * current working tree
87 	 */
88 	cl_git_pass(git_revparse_single(&g_object, g_repo, "master"));
89 	cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
90 
91 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master"));
92 
93 	/* This directory should no longer exist */
94 	cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
95 }
96 
test_checkout_tree__can_checkout_a_subdirectory_from_a_subtree(void)97 void test_checkout_tree__can_checkout_a_subdirectory_from_a_subtree(void)
98 {
99 	char *entries[] = { "de/" };
100 
101 	g_opts.paths.strings = entries;
102 	g_opts.paths.count = 1;
103 
104 	cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees:ab"));
105 
106 	cl_assert_equal_i(false, git_path_isdir("./testrepo/de/"));
107 
108 	cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
109 
110 	cl_assert_equal_i(true, git_path_isfile("./testrepo/de/2.txt"));
111 	cl_assert_equal_i(true, git_path_isfile("./testrepo/de/fgh/1.txt"));
112 }
113 
progress(const char * path,size_t cur,size_t tot,void * payload)114 static void progress(const char *path, size_t cur, size_t tot, void *payload)
115 {
116 	bool *was_called = (bool*)payload;
117 	GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
118 	*was_called = true;
119 }
120 
test_checkout_tree__calls_progress_callback(void)121 void test_checkout_tree__calls_progress_callback(void)
122 {
123 	bool was_called = 0;
124 
125 	g_opts.progress_cb = progress;
126 	g_opts.progress_payload = &was_called;
127 
128 	cl_git_pass(git_revparse_single(&g_object, g_repo, "master"));
129 
130 	cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
131 
132 	cl_assert_equal_i(was_called, true);
133 }
134 
test_checkout_tree__doesnt_write_unrequested_files_to_worktree(void)135 void test_checkout_tree__doesnt_write_unrequested_files_to_worktree(void)
136 {
137 	git_oid master_oid;
138 	git_oid chomped_oid;
139 	git_commit* p_master_commit;
140 	git_commit* p_chomped_commit;
141 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
142 
143 	git_oid_fromstr(&master_oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
144 	git_oid_fromstr(&chomped_oid, "e90810b8df3e80c413d903f631643c716887138d");
145 	cl_git_pass(git_commit_lookup(&p_master_commit, g_repo, &master_oid));
146 	cl_git_pass(git_commit_lookup(&p_chomped_commit, g_repo, &chomped_oid));
147 
148 	/* GIT_CHECKOUT_NONE should not add any file to the working tree from the
149 	 * index as it is supposed to be a dry run.
150 	 */
151 	opts.checkout_strategy = GIT_CHECKOUT_NONE;
152 	git_checkout_tree(g_repo, (git_object*)p_chomped_commit, &opts);
153 	cl_assert_equal_i(false, git_path_isfile("testrepo/readme.txt"));
154 
155 	git_commit_free(p_master_commit);
156 	git_commit_free(p_chomped_commit);
157 }
158 
test_checkout_tree__can_switch_branches(void)159 void test_checkout_tree__can_switch_branches(void)
160 {
161 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
162 	git_oid oid;
163 	git_object *obj = NULL;
164 
165 	assert_on_branch(g_repo, "master");
166 
167 	/* do first checkout with FORCE because we don't know if testrepo
168 	 * base data is clean for a checkout or not
169 	 */
170 	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
171 
172 	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
173 	cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
174 
175 	cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
176 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir"));
177 
178 	cl_assert(git_path_isfile("testrepo/README"));
179 	cl_assert(git_path_isfile("testrepo/branch_file.txt"));
180 	cl_assert(git_path_isfile("testrepo/new.txt"));
181 	cl_assert(git_path_isfile("testrepo/a/b.txt"));
182 
183 	cl_assert(!git_path_isdir("testrepo/ab"));
184 
185 	assert_on_branch(g_repo, "dir");
186 
187 	git_object_free(obj);
188 
189 	/* do second checkout safe because we should be clean after first */
190 	opts.checkout_strategy = GIT_CHECKOUT_SAFE;
191 
192 	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/subtrees"));
193 	cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
194 
195 	cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
196 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees"));
197 
198 	cl_assert(git_path_isfile("testrepo/README"));
199 	cl_assert(git_path_isfile("testrepo/branch_file.txt"));
200 	cl_assert(git_path_isfile("testrepo/new.txt"));
201 	cl_assert(git_path_isfile("testrepo/ab/4.txt"));
202 	cl_assert(git_path_isfile("testrepo/ab/c/3.txt"));
203 	cl_assert(git_path_isfile("testrepo/ab/de/2.txt"));
204 	cl_assert(git_path_isfile("testrepo/ab/de/fgh/1.txt"));
205 
206 	cl_assert(!git_path_isdir("testrepo/a"));
207 
208 	assert_on_branch(g_repo, "subtrees");
209 
210 	git_object_free(obj);
211 }
212 
test_checkout_tree__can_remove_untracked(void)213 void test_checkout_tree__can_remove_untracked(void)
214 {
215 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
216 
217 	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_REMOVE_UNTRACKED;
218 
219 	cl_git_mkfile("testrepo/untracked_file", "as you wish");
220 	cl_assert(git_path_isfile("testrepo/untracked_file"));
221 
222 	cl_git_pass(git_checkout_head(g_repo, &opts));
223 
224 	cl_assert(!git_path_isfile("testrepo/untracked_file"));
225 }
226 
test_checkout_tree__can_remove_ignored(void)227 void test_checkout_tree__can_remove_ignored(void)
228 {
229 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
230 	int ignored = 0;
231 
232 	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_REMOVE_IGNORED;
233 
234 	cl_git_mkfile("testrepo/ignored_file", "as you wish");
235 
236 	cl_git_pass(git_ignore_add_rule(g_repo, "ignored_file\n"));
237 
238 	cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "ignored_file"));
239 	cl_assert_equal_i(1, ignored);
240 
241 	cl_assert(git_path_isfile("testrepo/ignored_file"));
242 
243 	cl_git_pass(git_checkout_head(g_repo, &opts));
244 
245 	cl_assert(!git_path_isfile("testrepo/ignored_file"));
246 }
247 
checkout_tree_with_blob_ignored_in_workdir(int strategy,bool isdir)248 static int checkout_tree_with_blob_ignored_in_workdir(int strategy, bool isdir)
249 {
250 	git_oid oid;
251 	git_object *obj = NULL;
252 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
253 	int ignored = 0, error;
254 
255 	assert_on_branch(g_repo, "master");
256 
257 	/* do first checkout with FORCE because we don't know if testrepo
258 	 * base data is clean for a checkout or not
259 	 */
260 	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
261 
262 	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
263 	cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
264 
265 	cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
266 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir"));
267 
268 	cl_assert(git_path_isfile("testrepo/README"));
269 	cl_assert(git_path_isfile("testrepo/branch_file.txt"));
270 	cl_assert(git_path_isfile("testrepo/new.txt"));
271 	cl_assert(git_path_isfile("testrepo/a/b.txt"));
272 
273 	cl_assert(!git_path_isdir("testrepo/ab"));
274 
275 	assert_on_branch(g_repo, "dir");
276 
277 	git_object_free(obj);
278 
279 	opts.checkout_strategy = strategy;
280 
281 	if (isdir) {
282 		cl_must_pass(p_mkdir("testrepo/ab", 0777));
283 		cl_must_pass(p_mkdir("testrepo/ab/4.txt", 0777));
284 
285 		cl_git_mkfile("testrepo/ab/4.txt/file1.txt", "as you wish");
286 		cl_git_mkfile("testrepo/ab/4.txt/file2.txt", "foo bar foo");
287 		cl_git_mkfile("testrepo/ab/4.txt/file3.txt", "inky blinky pinky clyde");
288 
289 		cl_assert(git_path_isdir("testrepo/ab/4.txt"));
290 	} else {
291 		cl_must_pass(p_mkdir("testrepo/ab", 0777));
292 		cl_git_mkfile("testrepo/ab/4.txt", "as you wish");
293 
294 		cl_assert(git_path_isfile("testrepo/ab/4.txt"));
295 	}
296 
297 	cl_git_pass(git_ignore_add_rule(g_repo, "ab/4.txt\n"));
298 
299 	cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "ab/4.txt"));
300 	cl_assert_equal_i(1, ignored);
301 
302 	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/subtrees"));
303 	cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
304 
305 	error = git_checkout_tree(g_repo, obj, &opts);
306 
307 	git_object_free(obj);
308 
309 	return error;
310 }
311 
test_checkout_tree__conflict_on_ignored_when_not_overwriting(void)312 void test_checkout_tree__conflict_on_ignored_when_not_overwriting(void)
313 {
314 	int error;
315 
316 	cl_git_fail(error = checkout_tree_with_blob_ignored_in_workdir(
317 		GIT_CHECKOUT_SAFE | GIT_CHECKOUT_DONT_OVERWRITE_IGNORED, false));
318 
319 	cl_assert_equal_i(GIT_ECONFLICT, error);
320 }
321 
test_checkout_tree__can_overwrite_ignored_by_default(void)322 void test_checkout_tree__can_overwrite_ignored_by_default(void)
323 {
324 	cl_git_pass(checkout_tree_with_blob_ignored_in_workdir(GIT_CHECKOUT_SAFE, false));
325 
326 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees"));
327 
328 	cl_assert(git_path_isfile("testrepo/ab/4.txt"));
329 
330 	assert_on_branch(g_repo, "subtrees");
331 }
332 
test_checkout_tree__conflict_on_ignored_folder_when_not_overwriting(void)333 void test_checkout_tree__conflict_on_ignored_folder_when_not_overwriting(void)
334 {
335 	int error;
336 
337 	cl_git_fail(error = checkout_tree_with_blob_ignored_in_workdir(
338 		GIT_CHECKOUT_SAFE | GIT_CHECKOUT_DONT_OVERWRITE_IGNORED, true));
339 
340 	cl_assert_equal_i(GIT_ECONFLICT, error);
341 }
342 
test_checkout_tree__can_overwrite_ignored_folder_by_default(void)343 void test_checkout_tree__can_overwrite_ignored_folder_by_default(void)
344 {
345 	cl_git_pass(checkout_tree_with_blob_ignored_in_workdir(GIT_CHECKOUT_SAFE, true));
346 
347 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees"));
348 
349 	cl_assert(git_path_isfile("testrepo/ab/4.txt"));
350 
351 	assert_on_branch(g_repo, "subtrees");
352 
353 }
354 
test_checkout_tree__can_update_only(void)355 void test_checkout_tree__can_update_only(void)
356 {
357 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
358 	git_oid oid;
359 	git_object *obj = NULL;
360 
361 	/* first let's get things into a known state - by checkout out the HEAD */
362 
363 	assert_on_branch(g_repo, "master");
364 
365 	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
366 	cl_git_pass(git_checkout_head(g_repo, &opts));
367 
368 	cl_assert(!git_path_isdir("testrepo/a"));
369 
370 	check_file_contents_nocr("testrepo/branch_file.txt", "hi\nbye!\n");
371 
372 	/* now checkout branch but with update only */
373 
374 	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
375 
376 	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
377 	cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
378 
379 	cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
380 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir"));
381 
382 	assert_on_branch(g_repo, "dir");
383 
384 	/* this normally would have been created (which was tested separately in
385 	 * the test_checkout_tree__can_switch_branches test), but with
386 	 * UPDATE_ONLY it will not have been created.
387 	 */
388 	cl_assert(!git_path_isdir("testrepo/a"));
389 
390 	/* but this file still should have been updated */
391 	check_file_contents_nocr("testrepo/branch_file.txt", "hi\n");
392 
393 	git_object_free(obj);
394 }
395 
test_checkout_tree__can_checkout_with_pattern(void)396 void test_checkout_tree__can_checkout_with_pattern(void)
397 {
398 	char *entries[] = { "[l-z]*.txt" };
399 
400 	/* reset to beginning of history (i.e. just a README file) */
401 
402 	g_opts.checkout_strategy =
403 		GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
404 
405 	cl_git_pass(git_revparse_single(&g_object, g_repo, "8496071c1b46c854b31185ea97743be6a8774479"));
406 
407 	cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
408 	cl_git_pass(
409 		git_repository_set_head_detached(g_repo, git_object_id(g_object)));
410 
411 	git_object_free(g_object);
412 	g_object = NULL;
413 
414 	cl_assert(git_path_exists("testrepo/README"));
415 	cl_assert(!git_path_exists("testrepo/branch_file.txt"));
416 	cl_assert(!git_path_exists("testrepo/link_to_new.txt"));
417 	cl_assert(!git_path_exists("testrepo/new.txt"));
418 
419 	/* now to a narrow patterned checkout */
420 
421 	g_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
422 	g_opts.paths.strings = entries;
423 	g_opts.paths.count = 1;
424 
425 	cl_git_pass(git_revparse_single(&g_object, g_repo, "refs/heads/master"));
426 
427 	cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
428 
429 	cl_assert(git_path_exists("testrepo/README"));
430 	cl_assert(!git_path_exists("testrepo/branch_file.txt"));
431 	cl_assert(git_path_exists("testrepo/link_to_new.txt"));
432 	cl_assert(git_path_exists("testrepo/new.txt"));
433 }
434 
test_checkout_tree__pathlist_checkout_ignores_non_matches(void)435 void test_checkout_tree__pathlist_checkout_ignores_non_matches(void)
436 {
437 	char *entries[] = { "branch_file.txt", "link_to_new.txt" };
438 
439 	/* reset to beginning of history (i.e. just a README file) */
440 
441 	g_opts.checkout_strategy =
442 		GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
443 
444 	cl_git_pass(git_revparse_single(&g_object, g_repo, "refs/heads/master"));
445 
446 	cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
447 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master"));
448 
449 	cl_assert(git_path_exists("testrepo/README"));
450 	cl_assert(git_path_exists("testrepo/branch_file.txt"));
451 	cl_assert(git_path_exists("testrepo/link_to_new.txt"));
452 	cl_assert(git_path_exists("testrepo/new.txt"));
453 
454 	git_object_free(g_object);
455 	cl_git_pass(git_revparse_single(&g_object, g_repo, "8496071c1b46c854b31185ea97743be6a8774479"));
456 
457 	g_opts.checkout_strategy =
458 		GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
459 	g_opts.paths.strings = entries;
460 	g_opts.paths.count = 2;
461 
462 	cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
463 
464 	cl_assert(git_path_exists("testrepo/README"));
465 	cl_assert(!git_path_exists("testrepo/branch_file.txt"));
466 	cl_assert(!git_path_exists("testrepo/link_to_new.txt"));
467 	cl_assert(git_path_exists("testrepo/new.txt"));
468 }
469 
test_checkout_tree__can_disable_pattern_match(void)470 void test_checkout_tree__can_disable_pattern_match(void)
471 {
472 	char *entries[] = { "b*.txt" };
473 
474 	/* reset to beginning of history (i.e. just a README file) */
475 
476 	g_opts.checkout_strategy =
477 		GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
478 
479 	cl_git_pass(git_revparse_single(&g_object, g_repo, "8496071c1b46c854b31185ea97743be6a8774479"));
480 
481 	cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
482 	cl_git_pass(
483 		git_repository_set_head_detached(g_repo, git_object_id(g_object)));
484 
485 	git_object_free(g_object);
486 	g_object = NULL;
487 
488 	cl_assert(!git_path_isfile("testrepo/branch_file.txt"));
489 
490 	/* now to a narrow patterned checkout, but disable pattern */
491 
492 	g_opts.checkout_strategy =
493 		GIT_CHECKOUT_SAFE |
494 		GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
495 	g_opts.paths.strings = entries;
496 	g_opts.paths.count = 1;
497 
498 	cl_git_pass(git_revparse_single(&g_object, g_repo, "refs/heads/master"));
499 
500 	cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
501 
502 	cl_assert(!git_path_isfile("testrepo/branch_file.txt"));
503 
504 	/* let's try that again, but allow the pattern match */
505 
506 	g_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
507 
508 	cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
509 
510 	cl_assert(git_path_isfile("testrepo/branch_file.txt"));
511 }
512 
assert_conflict(const char * entry_path,const char * new_content,const char * parent_sha,const char * commit_sha)513 void assert_conflict(
514 	const char *entry_path,
515 	const char *new_content,
516 	const char *parent_sha,
517 	const char *commit_sha)
518 {
519 	git_index *index;
520 	git_object *hack_tree;
521 	git_reference *branch, *head;
522 	git_buf file_path = GIT_BUF_INIT;
523 
524 	cl_git_pass(git_repository_index(&index, g_repo));
525 
526 	/* Create a branch pointing at the parent */
527 	cl_git_pass(git_revparse_single(&g_object, g_repo, parent_sha));
528 	cl_git_pass(git_branch_create(&branch, g_repo,
529 		"potential_conflict", (git_commit *)g_object, 0));
530 
531 	/* Make HEAD point to this branch */
532 	cl_git_pass(git_reference_symbolic_create(
533 		&head, g_repo, "HEAD", git_reference_name(branch), 1, NULL));
534 	git_reference_free(head);
535 	git_reference_free(branch);
536 
537 	/* Checkout the parent */
538 	g_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
539 	cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
540 
541 	/* Hack-ishy workaound to ensure *all* the index entries
542 	 * match the content of the tree
543 	 */
544 	cl_git_pass(git_object_peel(&hack_tree, g_object, GIT_OBJECT_TREE));
545 	cl_git_pass(git_index_read_tree(index, (git_tree *)hack_tree));
546 	cl_git_pass(git_index_write(index));
547 	git_object_free(hack_tree);
548 	git_object_free(g_object);
549 	g_object = NULL;
550 
551 	/* Create a conflicting file */
552 	cl_git_pass(git_buf_joinpath(&file_path, "./testrepo", entry_path));
553 	cl_git_mkfile(git_buf_cstr(&file_path), new_content);
554 	git_buf_dispose(&file_path);
555 
556 	/* Trying to checkout the original commit */
557 	cl_git_pass(git_revparse_single(&g_object, g_repo, commit_sha));
558 
559 	g_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
560 	cl_assert_equal_i(
561 		GIT_ECONFLICT, git_checkout_tree(g_repo, g_object, &g_opts));
562 
563 	/* Stage the conflicting change */
564 	cl_git_pass(git_index_add_bypath(index, entry_path));
565 	cl_git_pass(git_index_write(index));
566 	git_index_free(index);
567 
568 	cl_assert_equal_i(
569 		GIT_ECONFLICT, git_checkout_tree(g_repo, g_object, &g_opts));
570 }
571 
test_checkout_tree__checking_out_a_conflicting_type_change_returns_ECONFLICT(void)572 void test_checkout_tree__checking_out_a_conflicting_type_change_returns_ECONFLICT(void)
573 {
574 	/*
575 	 * 099faba adds a symlink named 'link_to_new.txt'
576 	 * a65fedf is the parent of 099faba
577 	 */
578 
579 	assert_conflict("link_to_new.txt", "old.txt", "a65fedf", "099faba");
580 }
581 
test_checkout_tree__checking_out_a_conflicting_type_change_returns_ECONFLICT_2(void)582 void test_checkout_tree__checking_out_a_conflicting_type_change_returns_ECONFLICT_2(void)
583 {
584 	/*
585 	 * cf80f8d adds a directory named 'a/'
586 	 * a4a7dce is the parent of cf80f8d
587 	 */
588 
589 	assert_conflict("a", "hello\n", "a4a7dce", "cf80f8d");
590 }
591 
test_checkout_tree__checking_out_a_conflicting_content_change_returns_ECONFLICT(void)592 void test_checkout_tree__checking_out_a_conflicting_content_change_returns_ECONFLICT(void)
593 {
594 	/*
595 	 * c47800c adds a symlink named 'branch_file.txt'
596 	 * 5b5b025 is the parent of 763d71a
597 	 */
598 
599 	assert_conflict("branch_file.txt", "hello\n", "5b5b025", "c47800c");
600 }
601 
test_checkout_tree__donot_update_deleted_file_by_default(void)602 void test_checkout_tree__donot_update_deleted_file_by_default(void)
603 {
604 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
605 	git_oid old_id, new_id;
606 	git_commit *old_commit = NULL, *new_commit = NULL;
607 	git_index *index = NULL;
608 	checkout_counts ct;
609 
610 	opts.checkout_strategy = GIT_CHECKOUT_SAFE;
611 
612 	memset(&ct, 0, sizeof(ct));
613 	opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
614 	opts.notify_cb = checkout_count_callback;
615 	opts.notify_payload = &ct;
616 
617 	cl_git_pass(git_repository_index(&index, g_repo));
618 
619 	cl_git_pass(git_oid_fromstr(&old_id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
620 	cl_git_pass(git_commit_lookup(&old_commit, g_repo, &old_id));
621 	cl_git_pass(git_reset(g_repo, (git_object *)old_commit, GIT_RESET_HARD, NULL));
622 
623 	cl_git_pass(p_unlink("testrepo/branch_file.txt"));
624 	cl_git_pass(git_index_remove_bypath(index ,"branch_file.txt"));
625 	cl_git_pass(git_index_write(index));
626 
627 	cl_assert(!git_path_exists("testrepo/branch_file.txt"));
628 
629 	cl_git_pass(git_oid_fromstr(&new_id, "099fabac3a9ea935598528c27f866e34089c2eff"));
630 	cl_git_pass(git_commit_lookup(&new_commit, g_repo, &new_id));
631 
632 
633 	cl_git_fail(git_checkout_tree(g_repo, (git_object *)new_commit, &opts));
634 
635 	cl_assert_equal_i(1, ct.n_conflicts);
636 	cl_assert_equal_i(1, ct.n_updates);
637 
638 	git_commit_free(old_commit);
639 	git_commit_free(new_commit);
640 	git_index_free(index);
641 }
642 
643 struct checkout_cancel_at {
644 	const char *filename;
645 	int error;
646 	int count;
647 };
648 
checkout_cancel_cb(git_checkout_notify_t why,const char * path,const git_diff_file * b,const git_diff_file * t,const git_diff_file * w,void * payload)649 static int checkout_cancel_cb(
650 	git_checkout_notify_t why,
651 	const char *path,
652 	const git_diff_file *b,
653 	const git_diff_file *t,
654 	const git_diff_file *w,
655 	void *payload)
656 {
657 	struct checkout_cancel_at *ca = payload;
658 
659 	GIT_UNUSED(why); GIT_UNUSED(b); GIT_UNUSED(t); GIT_UNUSED(w);
660 
661 	ca->count++;
662 
663 	if (!strcmp(path, ca->filename))
664 		return ca->error;
665 
666 	return 0;
667 }
668 
test_checkout_tree__can_cancel_checkout_from_notify(void)669 void test_checkout_tree__can_cancel_checkout_from_notify(void)
670 {
671 	struct checkout_cancel_at ca;
672 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
673 	git_oid oid;
674 	git_object *obj = NULL;
675 
676 	assert_on_branch(g_repo, "master");
677 
678 	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
679 	cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
680 
681 	ca.filename = "new.txt";
682 	ca.error = -5555;
683 	ca.count = 0;
684 
685 	opts.notify_flags = GIT_CHECKOUT_NOTIFY_UPDATED;
686 	opts.notify_cb = checkout_cancel_cb;
687 	opts.notify_payload = &ca;
688 	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
689 
690 	cl_assert(!git_path_exists("testrepo/new.txt"));
691 
692 	cl_git_fail_with(git_checkout_tree(g_repo, obj, &opts), -5555);
693 
694 	cl_assert(!git_path_exists("testrepo/new.txt"));
695 
696 	/* on case-insensitive FS = a/b.txt, branch_file.txt, new.txt */
697 	/* on case-sensitive FS   = README, then above */
698 
699 	if (git_path_exists("testrepo/.git/CoNfIg")) /* case insensitive */
700 		cl_assert_equal_i(3, ca.count);
701 	else
702 		cl_assert_equal_i(4, ca.count);
703 
704 	/* and again with a different stopping point and return code */
705 	ca.filename = "README";
706 	ca.error = 123;
707 	ca.count = 0;
708 
709 	cl_git_fail_with(git_checkout_tree(g_repo, obj, &opts), 123);
710 
711 	cl_assert(!git_path_exists("testrepo/new.txt"));
712 
713 	if (git_path_exists("testrepo/.git/CoNfIg")) /* case insensitive */
714 		cl_assert_equal_i(4, ca.count);
715 	else
716 		cl_assert_equal_i(1, ca.count);
717 
718 	git_object_free(obj);
719 }
720 
test_checkout_tree__can_checkout_with_last_workdir_item_missing(void)721 void test_checkout_tree__can_checkout_with_last_workdir_item_missing(void)
722 {
723 	git_index *index = NULL;
724 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
725 	git_oid tree_id, commit_id;
726 	git_tree *tree = NULL;
727 	git_commit *commit = NULL;
728 
729 	git_repository_index(&index, g_repo);
730 
731 	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
732 
733 	cl_git_pass(git_reference_name_to_id(&commit_id, g_repo, "refs/heads/master"));
734 	cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id));
735 
736 	cl_git_pass(git_checkout_tree(g_repo, (git_object *)commit, &opts));
737 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master"));
738 
739 	cl_git_pass(p_mkdir("./testrepo/this-is-dir", 0777));
740 	cl_git_mkfile("./testrepo/this-is-dir/contained_file", "content\n");
741 
742 	cl_git_pass(git_index_add_bypath(index, "this-is-dir/contained_file"));
743 	cl_git_pass(git_index_write(index));
744 
745 	cl_git_pass(git_index_write_tree(&tree_id, index));
746 	cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
747 
748 	cl_git_pass(p_unlink("./testrepo/this-is-dir/contained_file"));
749 
750 	opts.checkout_strategy = GIT_CHECKOUT_SAFE;
751 
752 	opts.checkout_strategy = 1;
753 	git_checkout_tree(g_repo, (git_object *)tree, &opts);
754 
755 	git_tree_free(tree);
756 	git_commit_free(commit);
757 	git_index_free(index);
758 }
759 
test_checkout_tree__issue_1397(void)760 void test_checkout_tree__issue_1397(void)
761 {
762 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
763 	const char *partial_oid = "8a7ef04";
764 	git_object *tree = NULL;
765 
766 	test_checkout_tree__cleanup(); /* cleanup default checkout */
767 
768 	g_repo = cl_git_sandbox_init("issue_1397");
769 
770 	cl_repo_set_bool(g_repo, "core.autocrlf", true);
771 
772 	cl_git_pass(git_revparse_single(&tree, g_repo, partial_oid));
773 
774 	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
775 
776 	cl_git_pass(git_checkout_tree(g_repo, tree, &opts));
777 
778 	check_file_contents("./issue_1397/crlf_file.txt", "first line\r\nsecond line\r\nboth with crlf");
779 
780 	git_object_free(tree);
781 }
782 
test_checkout_tree__can_write_to_empty_dirs(void)783 void test_checkout_tree__can_write_to_empty_dirs(void)
784 {
785 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
786 	git_oid oid;
787 	git_object *obj = NULL;
788 
789 	assert_on_branch(g_repo, "master");
790 
791 	cl_git_pass(p_mkdir("testrepo/a", 0777));
792 
793 	/* do first checkout with FORCE because we don't know if testrepo
794 	 * base data is clean for a checkout or not
795 	 */
796 	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
797 
798 	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
799 	cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
800 
801 	cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
802 
803 	cl_assert(git_path_isfile("testrepo/a/b.txt"));
804 
805 	git_object_free(obj);
806 }
807 
test_checkout_tree__fails_when_dir_in_use(void)808 void test_checkout_tree__fails_when_dir_in_use(void)
809 {
810 #ifdef GIT_WIN32
811 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
812 	git_oid oid;
813 	git_object *obj = NULL;
814 
815 	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
816 
817 	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
818 	cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
819 
820 	cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
821 
822 	cl_assert(git_path_isfile("testrepo/a/b.txt"));
823 
824 	git_object_free(obj);
825 
826 	cl_git_pass(p_chdir("testrepo/a"));
827 
828 	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/master"));
829 	cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
830 
831 	cl_git_fail(git_checkout_tree(g_repo, obj, &opts));
832 
833 	cl_git_pass(p_chdir("../.."));
834 
835 	cl_assert(git_path_is_empty_dir("testrepo/a"));
836 
837 	git_object_free(obj);
838 #endif
839 }
840 
test_checkout_tree__can_continue_when_dir_in_use(void)841 void test_checkout_tree__can_continue_when_dir_in_use(void)
842 {
843 #ifdef GIT_WIN32
844 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
845 	git_oid oid;
846 	git_object *obj = NULL;
847 
848 	opts.checkout_strategy = GIT_CHECKOUT_FORCE |
849 		GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES;
850 
851 	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
852 	cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
853 
854 	cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
855 
856 	cl_assert(git_path_isfile("testrepo/a/b.txt"));
857 
858 	git_object_free(obj);
859 
860 	cl_git_pass(p_chdir("testrepo/a"));
861 
862 	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/master"));
863 	cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
864 
865 	cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
866 
867 	cl_git_pass(p_chdir("../.."));
868 
869 	cl_assert(git_path_is_empty_dir("testrepo/a"));
870 
871 	git_object_free(obj);
872 #endif
873 }
874 
test_checkout_tree__target_directory_from_bare(void)875 void test_checkout_tree__target_directory_from_bare(void)
876 {
877 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
878 	git_oid oid;
879 	checkout_counts cts;
880 	memset(&cts, 0, sizeof(cts));
881 
882 	test_checkout_tree__cleanup(); /* cleanup default checkout */
883 
884 	g_repo = cl_git_sandbox_init("testrepo.git");
885 	cl_assert(git_repository_is_bare(g_repo));
886 
887 	opts.checkout_strategy = GIT_CHECKOUT_SAFE |
888 		GIT_CHECKOUT_RECREATE_MISSING;
889 
890 	opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
891 	opts.notify_cb = checkout_count_callback;
892 	opts.notify_payload = &cts;
893 
894 	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
895 	cl_git_pass(git_object_lookup(&g_object, g_repo, &oid, GIT_OBJECT_ANY));
896 
897 	cl_git_fail(git_checkout_tree(g_repo, g_object, &opts));
898 
899 	opts.target_directory = "alternative";
900 	cl_assert(!git_path_isdir("alternative"));
901 
902 	cl_git_pass(git_checkout_tree(g_repo, g_object, &opts));
903 
904 	cl_assert_equal_i(0, cts.n_untracked);
905 	cl_assert_equal_i(0, cts.n_ignored);
906 	cl_assert_equal_i(3, cts.n_updates);
907 
908 	check_file_contents_nocr("./alternative/README", "hey there\n");
909 	check_file_contents_nocr("./alternative/branch_file.txt", "hi\nbye!\n");
910 	check_file_contents_nocr("./alternative/new.txt", "my new file\n");
911 
912 	cl_git_pass(git_futils_rmdir_r(
913 		"alternative", NULL, GIT_RMDIR_REMOVE_FILES));
914 }
915 
test_checkout_tree__extremely_long_file_name(void)916 void test_checkout_tree__extremely_long_file_name(void)
917 {
918 	/* A utf-8 string with 83 characters, but 249 bytes. */
919 	const char *longname = "\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97\xe5\x8f\x97";
920 	char path[1024] = {0};
921 
922 	g_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
923 	cl_git_pass(git_revparse_single(&g_object, g_repo, "long-file-name"));
924 	cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
925 
926 	sprintf(path, "testrepo/%s.txt", longname);
927 	cl_assert(git_path_exists(path));
928 
929 	git_object_free(g_object);
930 	cl_git_pass(git_revparse_single(&g_object, g_repo, "master"));
931 	cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
932 	cl_assert(!git_path_exists(path));
933 }
934 
create_conflict(const char * path)935 static void create_conflict(const char *path)
936 {
937 	git_index *index;
938 	git_index_entry entry;
939 
940 	cl_git_pass(git_repository_index(&index, g_repo));
941 
942 	memset(&entry, 0x0, sizeof(git_index_entry));
943 	entry.mode = 0100644;
944 	GIT_INDEX_ENTRY_STAGE_SET(&entry, 1);
945 	git_oid_fromstr(&entry.id, "d427e0b2e138501a3d15cc376077a3631e15bd46");
946 	entry.path = path;
947 	cl_git_pass(git_index_add(index, &entry));
948 
949 	GIT_INDEX_ENTRY_STAGE_SET(&entry, 2);
950 	git_oid_fromstr(&entry.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf");
951 	cl_git_pass(git_index_add(index, &entry));
952 
953 	GIT_INDEX_ENTRY_STAGE_SET(&entry, 3);
954 	git_oid_fromstr(&entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863");
955 	cl_git_pass(git_index_add(index, &entry));
956 
957 	cl_git_pass(git_index_write(index));
958 	git_index_free(index);
959 }
960 
test_checkout_tree__fails_when_conflicts_exist_in_index(void)961 void test_checkout_tree__fails_when_conflicts_exist_in_index(void)
962 {
963 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
964 	git_oid oid;
965 	git_object *obj = NULL;
966 
967 	opts.checkout_strategy = GIT_CHECKOUT_SAFE;
968 
969 	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
970 	cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
971 
972 	create_conflict("conflicts.txt");
973 
974 	cl_git_fail(git_checkout_tree(g_repo, obj, &opts));
975 
976 	git_object_free(obj);
977 }
978 
test_checkout_tree__filemode_preserved_in_index(void)979 void test_checkout_tree__filemode_preserved_in_index(void)
980 {
981 	git_oid executable_oid;
982 	git_commit *commit;
983 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
984 	git_index *index;
985 	const git_index_entry *entry;
986 
987 	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
988 
989 	cl_git_pass(git_repository_index(&index, g_repo));
990 
991 	/* test a freshly added executable */
992 	cl_git_pass(git_oid_fromstr(&executable_oid, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6"));
993 	cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
994 
995 	cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
996 	cl_assert(entry = git_index_get_bypath(index, "executable.txt", 0));
997 	cl_assert(GIT_PERMS_IS_EXEC(entry->mode));
998 
999 	git_commit_free(commit);
1000 
1001 
1002 	/* Now start with a commit which has a text file */
1003 	cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9"));
1004 	cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
1005 
1006 	cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
1007 	cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0));
1008 	cl_assert(!GIT_PERMS_IS_EXEC(entry->mode));
1009 
1010 	git_commit_free(commit);
1011 
1012 
1013 	/* And then check out to a commit which converts the text file to an executable */
1014 	cl_git_pass(git_oid_fromstr(&executable_oid, "144344043ba4d4a405da03de3844aa829ae8be0e"));
1015 	cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
1016 
1017 	cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
1018 	cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0));
1019 	cl_assert(GIT_PERMS_IS_EXEC(entry->mode));
1020 
1021 	git_commit_free(commit);
1022 
1023 
1024 	/* Finally, check out the text file again and check that the exec bit is cleared */
1025 	cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9"));
1026 	cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
1027 
1028 	cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
1029 	cl_assert(entry = git_index_get_bypath(index, "a/b.txt", 0));
1030 	cl_assert(!GIT_PERMS_IS_EXEC(entry->mode));
1031 
1032 	git_commit_free(commit);
1033 
1034 
1035 	git_index_free(index);
1036 }
1037 
read_filemode(const char * path)1038 mode_t read_filemode(const char *path)
1039 {
1040 	git_buf fullpath = GIT_BUF_INIT;
1041 	struct stat st;
1042 	mode_t result;
1043 
1044 	git_buf_joinpath(&fullpath, "testrepo", path);
1045 	cl_must_pass(p_stat(fullpath.ptr, &st));
1046 
1047 	result = GIT_PERMS_IS_EXEC(st.st_mode) ?
1048 		GIT_FILEMODE_BLOB_EXECUTABLE : GIT_FILEMODE_BLOB;
1049 
1050 	git_buf_dispose(&fullpath);
1051 
1052 	return result;
1053 }
1054 
test_checkout_tree__filemode_preserved_in_workdir(void)1055 void test_checkout_tree__filemode_preserved_in_workdir(void)
1056 {
1057 #ifndef GIT_WIN32
1058 	git_oid executable_oid;
1059 	git_commit *commit;
1060 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
1061 
1062 	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
1063 
1064 	/* test a freshly added executable */
1065 	cl_git_pass(git_oid_fromstr(&executable_oid, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6"));
1066 	cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
1067 
1068 	cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
1069 	cl_assert(GIT_PERMS_IS_EXEC(read_filemode("executable.txt")));
1070 
1071 	git_commit_free(commit);
1072 
1073 
1074 	/* Now start with a commit which has a text file */
1075 	cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9"));
1076 	cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
1077 
1078 	cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
1079 	cl_assert(!GIT_PERMS_IS_EXEC(read_filemode("a/b.txt")));
1080 
1081 	git_commit_free(commit);
1082 
1083 
1084 	/* And then check out to a commit which converts the text file to an executable */
1085 	cl_git_pass(git_oid_fromstr(&executable_oid, "144344043ba4d4a405da03de3844aa829ae8be0e"));
1086 	cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
1087 
1088 	cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
1089 	cl_assert(GIT_PERMS_IS_EXEC(read_filemode("a/b.txt")));
1090 
1091 	git_commit_free(commit);
1092 
1093 
1094 	/* Finally, check out the text file again and check that the exec bit is cleared */
1095 	cl_git_pass(git_oid_fromstr(&executable_oid, "cf80f8de9f1185bf3a05f993f6121880dd0cfbc9"));
1096 	cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
1097 
1098 	cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
1099 	cl_assert(!GIT_PERMS_IS_EXEC(read_filemode("a/b.txt")));
1100 
1101 	git_commit_free(commit);
1102 #else
1103 	cl_skip();
1104 #endif
1105 }
1106 
test_checkout_tree__removes_conflicts(void)1107 void test_checkout_tree__removes_conflicts(void)
1108 {
1109 	git_oid commit_id;
1110 	git_commit *commit;
1111 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
1112 	git_index *index;
1113 
1114 	cl_git_pass(git_oid_fromstr(&commit_id, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6"));
1115 	cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id));
1116 
1117 	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
1118 
1119 	cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
1120 
1121 	cl_git_pass(git_repository_index(&index, g_repo));
1122 	cl_git_pass(git_index_remove(index, "executable.txt", 0));
1123 
1124 	create_conflict("executable.txt");
1125 	cl_git_mkfile("testrepo/executable.txt", "This is the conflict file.\n");
1126 
1127 	create_conflict("other.txt");
1128 	cl_git_mkfile("testrepo/other.txt", "This is another conflict file.\n");
1129 
1130 	cl_git_pass(git_index_write(index));
1131 
1132 	cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
1133 
1134 	cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 1));
1135 	cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 2));
1136 	cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 3));
1137 
1138 	cl_assert_equal_p(NULL, git_index_get_bypath(index, "other.txt", 1));
1139 	cl_assert_equal_p(NULL, git_index_get_bypath(index, "other.txt", 2));
1140 	cl_assert_equal_p(NULL, git_index_get_bypath(index, "other.txt", 3));
1141 
1142 	cl_assert(!git_path_exists("testrepo/other.txt"));
1143 
1144 	git_commit_free(commit);
1145 	git_index_free(index);
1146 }
1147 
1148 
test_checkout_tree__removes_conflicts_only_by_pathscope(void)1149 void test_checkout_tree__removes_conflicts_only_by_pathscope(void)
1150 {
1151 	git_oid commit_id;
1152 	git_commit *commit;
1153 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
1154 	git_index *index;
1155 	const char *path = "executable.txt";
1156 
1157 	cl_git_pass(git_oid_fromstr(&commit_id, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6"));
1158 	cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id));
1159 
1160 	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
1161 	opts.paths.count = 1;
1162 	opts.paths.strings = (char **)&path;
1163 
1164 	cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
1165 
1166 	cl_git_pass(git_repository_index(&index, g_repo));
1167 	cl_git_pass(git_index_remove(index, "executable.txt", 0));
1168 
1169 	create_conflict("executable.txt");
1170 	cl_git_mkfile("testrepo/executable.txt", "This is the conflict file.\n");
1171 
1172 	create_conflict("other.txt");
1173 	cl_git_mkfile("testrepo/other.txt", "This is another conflict file.\n");
1174 
1175 	cl_git_pass(git_index_write(index));
1176 
1177 	cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
1178 
1179 	cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 1));
1180 	cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 2));
1181 	cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 3));
1182 
1183 	cl_assert(git_index_get_bypath(index, "other.txt", 1) != NULL);
1184 	cl_assert(git_index_get_bypath(index, "other.txt", 2) != NULL);
1185 	cl_assert(git_index_get_bypath(index, "other.txt", 3) != NULL);
1186 
1187 	cl_assert(git_path_exists("testrepo/other.txt"));
1188 
1189 	git_commit_free(commit);
1190 	git_index_free(index);
1191 }
1192 
test_checkout_tree__case_changing_rename(void)1193 void test_checkout_tree__case_changing_rename(void)
1194 {
1195 	git_index *index;
1196 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
1197 	git_oid master_id, dir_commit_id, tree_id, commit_id;
1198 	git_commit *master_commit, *dir_commit;
1199 	git_tree *tree;
1200 	git_signature *signature;
1201 	const git_index_entry *index_entry;
1202 	bool case_sensitive;
1203 
1204 	assert_on_branch(g_repo, "master");
1205 
1206 	cl_git_pass(git_repository_index(&index, g_repo));
1207 
1208 	/* Switch branches and perform a case-changing rename */
1209 
1210 	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
1211 
1212 	cl_git_pass(git_reference_name_to_id(&dir_commit_id, g_repo, "refs/heads/dir"));
1213 	cl_git_pass(git_commit_lookup(&dir_commit, g_repo, &dir_commit_id));
1214 
1215 	cl_git_pass(git_checkout_tree(g_repo, (git_object *)dir_commit, &opts));
1216 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir"));
1217 
1218 	cl_assert(git_path_isfile("testrepo/README"));
1219 	case_sensitive = !git_path_isfile("testrepo/readme");
1220 
1221 	cl_assert(index_entry = git_index_get_bypath(index, "README", 0));
1222 	cl_assert_equal_s("README", index_entry->path);
1223 
1224 	cl_git_pass(git_index_remove_bypath(index, "README"));
1225 	cl_git_pass(p_rename("testrepo/README", "testrepo/__readme__"));
1226 	cl_git_pass(p_rename("testrepo/__readme__", "testrepo/readme"));
1227 	cl_git_append2file("testrepo/readme", "An addendum...");
1228 	cl_git_pass(git_index_add_bypath(index, "readme"));
1229 
1230 	cl_git_pass(git_index_write(index));
1231 
1232 	cl_git_pass(git_index_write_tree(&tree_id, index));
1233 	cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
1234 
1235 	cl_git_pass(git_signature_new(&signature, "Renamer", "rename@contoso.com", time(NULL), 0));
1236 
1237 	cl_git_pass(git_commit_create(&commit_id, g_repo, "refs/heads/dir", signature, signature, NULL, "case-changing rename", tree, 1, (const git_commit **)&dir_commit));
1238 
1239 	cl_assert(git_path_isfile("testrepo/readme"));
1240 	if (case_sensitive)
1241 		cl_assert(!git_path_isfile("testrepo/README"));
1242 
1243 	cl_assert(index_entry = git_index_get_bypath(index, "readme", 0));
1244 	cl_assert_equal_s("readme", index_entry->path);
1245 
1246 	/* Switching back to master should rename readme -> README */
1247 	opts.checkout_strategy = GIT_CHECKOUT_SAFE;
1248 
1249 	cl_git_pass(git_reference_name_to_id(&master_id, g_repo, "refs/heads/master"));
1250 	cl_git_pass(git_commit_lookup(&master_commit, g_repo, &master_id));
1251 
1252 	cl_git_pass(git_checkout_tree(g_repo, (git_object *)master_commit, &opts));
1253 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master"));
1254 
1255 	assert_on_branch(g_repo, "master");
1256 
1257 	cl_assert(git_path_isfile("testrepo/README"));
1258 	if (case_sensitive)
1259 		cl_assert(!git_path_isfile("testrepo/readme"));
1260 
1261 	cl_assert(index_entry = git_index_get_bypath(index, "README", 0));
1262 	cl_assert_equal_s("README", index_entry->path);
1263 
1264 	git_index_free(index);
1265 	git_signature_free(signature);
1266 	git_tree_free(tree);
1267 	git_commit_free(dir_commit);
1268 	git_commit_free(master_commit);
1269 }
1270 
perfdata_cb(const git_checkout_perfdata * in,void * payload)1271 void perfdata_cb(const git_checkout_perfdata *in, void *payload)
1272 {
1273 	memcpy(payload, in, sizeof(git_checkout_perfdata));
1274 }
1275 
test_checkout_tree__can_collect_perfdata(void)1276 void test_checkout_tree__can_collect_perfdata(void)
1277 {
1278 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
1279 	git_oid oid;
1280 	git_object *obj = NULL;
1281 	git_checkout_perfdata perfdata = {0};
1282 
1283 	opts.perfdata_cb = perfdata_cb;
1284 	opts.perfdata_payload = &perfdata;
1285 
1286 	assert_on_branch(g_repo, "master");
1287 	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
1288 
1289 	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
1290 	cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
1291 
1292 	cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
1293 
1294 	cl_assert(perfdata.mkdir_calls > 0);
1295 	cl_assert(perfdata.stat_calls > 0);
1296 
1297 	git_object_free(obj);
1298 }
1299 
update_attr_callback(const char * path,size_t completed_steps,size_t total_steps,void * payload)1300 void update_attr_callback(
1301 	const char *path,
1302 	size_t completed_steps,
1303 	size_t total_steps,
1304 	void *payload)
1305 {
1306 	GIT_UNUSED(completed_steps);
1307 	GIT_UNUSED(total_steps);
1308 	GIT_UNUSED(payload);
1309 
1310 	if (path && strcmp(path, "ident1.txt") == 0)
1311 		cl_git_write2file("testrepo/.gitattributes",
1312 			"*.txt ident\n", 12, O_RDWR|O_CREAT, 0666);
1313 }
1314 
test_checkout_tree__caches_attributes_during_checkout(void)1315 void test_checkout_tree__caches_attributes_during_checkout(void)
1316 {
1317 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
1318 	git_oid oid;
1319 	git_object *obj = NULL;
1320 	git_buf ident1 = GIT_BUF_INIT, ident2 = GIT_BUF_INIT;
1321 	char *ident_paths[] = { "ident1.txt", "ident2.txt" };
1322 
1323 	opts.progress_cb = update_attr_callback;
1324 
1325 	assert_on_branch(g_repo, "master");
1326 	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
1327 	opts.paths.strings = ident_paths;
1328 	opts.paths.count = 2;
1329 
1330 	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/ident"));
1331 	cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
1332 
1333 	cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
1334 
1335 	cl_git_pass(git_futils_readbuffer(&ident1, "testrepo/ident1.txt"));
1336 	cl_git_pass(git_futils_readbuffer(&ident2, "testrepo/ident2.txt"));
1337 
1338 	cl_assert_equal_strn(ident1.ptr, "# $Id$", 6);
1339 	cl_assert_equal_strn(ident2.ptr, "# $Id$", 6);
1340 
1341 	cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
1342 
1343 	cl_git_pass(git_futils_readbuffer(&ident1, "testrepo/ident1.txt"));
1344 	cl_git_pass(git_futils_readbuffer(&ident2, "testrepo/ident2.txt"));
1345 
1346 	cl_assert_equal_strn(ident1.ptr, "# $Id: ", 7);
1347 	cl_assert_equal_strn(ident2.ptr, "# $Id: ", 7);
1348 
1349 	git_buf_dispose(&ident1);
1350 	git_buf_dispose(&ident2);
1351 	git_object_free(obj);
1352 }
1353 
test_checkout_tree__can_not_update_index(void)1354 void test_checkout_tree__can_not_update_index(void)
1355 {
1356 	git_oid oid;
1357 	git_object *head;
1358 	unsigned int status;
1359 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
1360 	git_index *index;
1361 
1362 	opts.checkout_strategy |=
1363 		GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DONT_UPDATE_INDEX;
1364 
1365 	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
1366 	cl_git_pass(git_object_lookup(&head, g_repo, &oid, GIT_OBJECT_ANY));
1367 
1368 	cl_git_pass(git_reset(g_repo, head, GIT_RESET_HARD, &g_opts));
1369 
1370 	cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
1371 
1372 	cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees"));
1373 
1374 	cl_git_pass(git_checkout_tree(g_repo, g_object, &opts));
1375 
1376 	cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/2.txt"));
1377 	cl_git_pass(git_status_file(&status, g_repo, "ab/de/2.txt"));
1378 	cl_assert_equal_i(GIT_STATUS_WT_NEW, status);
1379 
1380 	cl_git_pass(git_repository_index(&index, g_repo));
1381 	cl_git_pass(git_index_write(index));
1382 
1383 	cl_git_pass(git_status_file(&status, g_repo, "ab/de/2.txt"));
1384 	cl_assert_equal_i(GIT_STATUS_WT_NEW, status);
1385 
1386 	git_object_free(head);
1387 	git_index_free(index);
1388 }
1389 
test_checkout_tree__can_update_but_not_write_index(void)1390 void test_checkout_tree__can_update_but_not_write_index(void)
1391 {
1392 	git_oid oid;
1393 	git_object *head;
1394 	unsigned int status;
1395 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
1396 	git_index *index;
1397 	git_repository *other;
1398 
1399 	opts.checkout_strategy |=
1400 		GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DONT_WRITE_INDEX;
1401 
1402 	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
1403 	cl_git_pass(git_object_lookup(&head, g_repo, &oid, GIT_OBJECT_ANY));
1404 
1405 	cl_git_pass(git_reset(g_repo, head, GIT_RESET_HARD, &g_opts));
1406 
1407 	cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
1408 
1409 	cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees"));
1410 
1411 	cl_git_pass(git_checkout_tree(g_repo, g_object, &opts));
1412 
1413 	cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/2.txt"));
1414 	cl_git_pass(git_status_file(&status, g_repo, "ab/de/2.txt"));
1415 	cl_assert_equal_i(GIT_STATUS_INDEX_NEW, status);
1416 
1417 	cl_git_pass(git_repository_open(&other, "testrepo"));
1418 	cl_git_pass(git_status_file(&status, other, "ab/de/2.txt"));
1419 	cl_assert_equal_i(GIT_STATUS_WT_NEW, status);
1420 	git_repository_free(other);
1421 
1422 	cl_git_pass(git_repository_index(&index, g_repo));
1423 	cl_git_pass(git_index_write(index));
1424 
1425 	cl_git_pass(git_repository_open(&other, "testrepo"));
1426 	cl_git_pass(git_status_file(&status, other, "ab/de/2.txt"));
1427 	cl_assert_equal_i(GIT_STATUS_INDEX_NEW, status);
1428 	git_repository_free(other);
1429 
1430 	git_object_free(head);
1431 	git_index_free(index);
1432 }
1433 
1434 /* Emulate checking out in a repo created by clone --no-checkout,
1435  * which would not have written an index. */
test_checkout_tree__safe_proceeds_if_no_index(void)1436 void test_checkout_tree__safe_proceeds_if_no_index(void)
1437 {
1438 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
1439 	git_oid oid;
1440 	git_object *obj = NULL;
1441 
1442 	assert_on_branch(g_repo, "master");
1443 	cl_must_pass(p_unlink("testrepo/.git/index"));
1444 
1445 	/* do second checkout safe because we should be clean after first */
1446 	opts.checkout_strategy = GIT_CHECKOUT_SAFE;
1447 
1448 	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/subtrees"));
1449 	cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
1450 
1451 	cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
1452 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees"));
1453 
1454 	cl_assert(git_path_isfile("testrepo/README"));
1455 	cl_assert(git_path_isfile("testrepo/branch_file.txt"));
1456 	cl_assert(git_path_isfile("testrepo/new.txt"));
1457 	cl_assert(git_path_isfile("testrepo/ab/4.txt"));
1458 	cl_assert(git_path_isfile("testrepo/ab/c/3.txt"));
1459 	cl_assert(git_path_isfile("testrepo/ab/de/2.txt"));
1460 	cl_assert(git_path_isfile("testrepo/ab/de/fgh/1.txt"));
1461 
1462 	cl_assert(!git_path_isdir("testrepo/a"));
1463 
1464 	assert_on_branch(g_repo, "subtrees");
1465 
1466 	git_object_free(obj);
1467 }
1468 
checkout_conflict_count_cb(git_checkout_notify_t why,const char * path,const git_diff_file * b,const git_diff_file * t,const git_diff_file * w,void * payload)1469 static int checkout_conflict_count_cb(
1470 	git_checkout_notify_t why,
1471 	const char *path,
1472 	const git_diff_file *b,
1473 	const git_diff_file *t,
1474 	const git_diff_file *w,
1475 	void *payload)
1476 {
1477 	size_t *n = payload;
1478 
1479 	GIT_UNUSED(why);
1480 	GIT_UNUSED(path);
1481 	GIT_UNUSED(b);
1482 	GIT_UNUSED(t);
1483 	GIT_UNUSED(w);
1484 
1485 	(*n)++;
1486 
1487 	return 0;
1488 }
1489 
1490 /* A repo that has a HEAD (even a properly born HEAD that peels to
1491  * a commit) but no index should be treated as if it's an empty baseline
1492  */
test_checkout_tree__baseline_is_empty_when_no_index(void)1493 void test_checkout_tree__baseline_is_empty_when_no_index(void)
1494 {
1495 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
1496 	git_reference *head;
1497 	git_object *obj;
1498 	size_t conflicts = 0;
1499 
1500 	assert_on_branch(g_repo, "master");
1501 
1502 	cl_git_pass(git_repository_head(&head, g_repo));
1503 	cl_git_pass(git_reference_peel(&obj, head, GIT_OBJECT_COMMIT));
1504 
1505 	cl_git_pass(git_reset(g_repo, obj, GIT_RESET_HARD, NULL));
1506 
1507 	cl_must_pass(p_unlink("testrepo/.git/index"));
1508 
1509 	/* for a safe checkout, we should have checkout conflicts with
1510 	 * the existing untracked files.
1511 	 */
1512 	opts.checkout_strategy &= ~GIT_CHECKOUT_FORCE;
1513 	opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
1514 	opts.notify_cb = checkout_conflict_count_cb;
1515 	opts.notify_payload = &conflicts;
1516 
1517 	cl_git_fail_with(GIT_ECONFLICT, git_checkout_tree(g_repo, obj, &opts));
1518 	cl_assert_equal_i(4, conflicts);
1519 
1520 	/* but force should succeed and update the index */
1521 	opts.checkout_strategy |= GIT_CHECKOUT_FORCE;
1522 	cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
1523 
1524 	assert_status_entrycount(g_repo, 0);
1525 
1526 	git_object_free(obj);
1527 	git_reference_free(head);
1528 }
1529 
test_checkout_tree__mode_change_is_force_updated(void)1530 void test_checkout_tree__mode_change_is_force_updated(void)
1531 {
1532 	git_index *index;
1533 	git_reference *head;
1534 	git_object *obj;
1535 
1536 	if (!cl_is_chmod_supported())
1537 		clar__skip();
1538 
1539 	assert_on_branch(g_repo, "master");
1540 	cl_git_pass(git_repository_index(&index, g_repo));
1541 	cl_git_pass(git_repository_head(&head, g_repo));
1542 	cl_git_pass(git_reference_peel(&obj, head, GIT_OBJECT_COMMIT));
1543 
1544 	cl_git_pass(git_reset(g_repo, obj, GIT_RESET_HARD, NULL));
1545 	assert_status_entrycount(g_repo, 0);
1546 
1547 	/* update the mode on-disk */
1548 	cl_must_pass(p_chmod("testrepo/README", 0755));
1549 
1550 	assert_status_entrycount(g_repo, 1);
1551 	cl_git_pass(git_checkout_tree(g_repo, obj, &g_opts));
1552 	assert_status_entrycount(g_repo, 0);
1553 
1554 	/* update the mode on-disk and in the index */
1555 	cl_must_pass(p_chmod("testrepo/README", 0755));
1556 	cl_must_pass(git_index_add_bypath(index, "README"));
1557 
1558 	cl_git_pass(git_index_write(index));
1559 	assert_status_entrycount(g_repo, 1);
1560 
1561 	cl_git_pass(git_checkout_tree(g_repo, obj, &g_opts));
1562 	cl_git_pass(git_index_write(index));
1563 
1564 	assert_status_entrycount(g_repo, 0);
1565 
1566 	git_object_free(obj);
1567 	git_reference_free(head);
1568 	git_index_free(index);
1569 }
1570 
test_checkout_tree__nullopts(void)1571 void test_checkout_tree__nullopts(void)
1572 {
1573 	cl_git_pass(git_checkout_tree(g_repo, NULL, NULL));
1574 }
1575 
modify_index_ondisk(void)1576 static void modify_index_ondisk(void)
1577 {
1578 	git_repository *other_repo;
1579 	git_index *other_index;
1580 	git_index_entry entry = {{0}};
1581 
1582 	cl_git_pass(git_repository_open(&other_repo, git_repository_workdir(g_repo)));
1583 	cl_git_pass(git_repository_index(&other_index, other_repo));
1584 
1585 	cl_git_pass(git_oid_fromstr(&entry.id, "1385f264afb75a56a5bec74243be9b367ba4ca08"));
1586 	entry.mode = 0100644;
1587 	entry.path = "README";
1588 
1589 	cl_git_pass(git_index_add(other_index, &entry));
1590 	cl_git_pass(git_index_write(other_index));
1591 
1592 	git_index_free(other_index);
1593 	git_repository_free(other_repo);
1594 }
1595 
modify_index_and_checkout_tree(git_checkout_options * opts)1596 static void modify_index_and_checkout_tree(git_checkout_options *opts)
1597 {
1598 	git_index *index;
1599 	git_reference *head;
1600 	git_object *obj;
1601 
1602 	/* External changes to the index are maintained by default */
1603 	cl_git_pass(git_repository_index(&index, g_repo));
1604 	cl_git_pass(git_repository_head(&head, g_repo));
1605 	cl_git_pass(git_reference_peel(&obj, head, GIT_OBJECT_COMMIT));
1606 
1607 	cl_git_pass(git_reset(g_repo, obj, GIT_RESET_HARD, NULL));
1608 	assert_status_entrycount(g_repo, 0);
1609 
1610 	modify_index_ondisk();
1611 
1612 	/* The file in the index remains modified */
1613 	cl_git_pass(git_checkout_tree(g_repo, obj, opts));
1614 
1615 	git_object_free(obj);
1616 	git_reference_free(head);
1617 	git_index_free(index);
1618 }
1619 
test_checkout_tree__retains_external_index_changes(void)1620 void test_checkout_tree__retains_external_index_changes(void)
1621 {
1622 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
1623 
1624 	opts.checkout_strategy = GIT_CHECKOUT_SAFE;
1625 
1626 	modify_index_and_checkout_tree(&opts);
1627 	assert_status_entrycount(g_repo, 1);
1628 }
1629 
test_checkout_tree__no_index_refresh(void)1630 void test_checkout_tree__no_index_refresh(void)
1631 {
1632 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
1633 
1634 	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_NO_REFRESH;
1635 
1636 	modify_index_and_checkout_tree(&opts);
1637 	assert_status_entrycount(g_repo, 0);
1638 }
1639 
test_checkout_tree__dry_run(void)1640 void test_checkout_tree__dry_run(void)
1641 {
1642 	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
1643 	git_oid oid;
1644 	git_object *obj = NULL;
1645 	checkout_counts ct;
1646 
1647 	/* first let's get things into a known state - by checkout out the HEAD */
1648 
1649 	assert_on_branch(g_repo, "master");
1650 
1651 	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
1652 	cl_git_pass(git_checkout_head(g_repo, &opts));
1653 
1654 	cl_assert(!git_path_isdir("testrepo/a"));
1655 
1656 	check_file_contents_nocr("testrepo/branch_file.txt", "hi\nbye!\n");
1657 
1658 	/* now checkout branch but with dry run enabled */
1659 
1660 	memset(&ct, 0, sizeof(ct));
1661 	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_DRY_RUN;
1662 	opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
1663 	opts.notify_cb = checkout_count_callback;
1664 	opts.notify_payload = &ct;
1665 
1666 	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
1667 	cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJECT_ANY));
1668 
1669 	cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
1670 	cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir"));
1671 
1672 	assert_on_branch(g_repo, "dir");
1673 
1674 	/* these normally would have been created and updated, but with
1675 	 * DRY_RUN they will be unchanged.
1676 	 */
1677 	cl_assert(!git_path_isdir("testrepo/a"));
1678 	check_file_contents_nocr("testrepo/branch_file.txt", "hi\nbye!\n");
1679 
1680 	/* check that notify callback was invoked */
1681 	cl_assert_equal_i(ct.n_updates, 2);
1682 
1683 	git_object_free(obj);
1684 }
1685