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