]> git.proxmox.com Git - libgit2.git/blob - tests/checkout/tree.c
checkout: introduce GIT_CHECKOUT_DONT_WRITE_INDEX
[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 "buffer.h"
7 #include "fileops.h"
8
9 static git_repository *g_repo;
10 static git_checkout_options g_opts;
11 static git_object *g_object;
12
13 void test_checkout_tree__initialize(void)
14 {
15 g_repo = cl_git_sandbox_init("testrepo");
16
17 GIT_INIT_STRUCTURE(&g_opts, GIT_CHECKOUT_OPTIONS_VERSION);
18 g_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
19 }
20
21 void test_checkout_tree__cleanup(void)
22 {
23 git_object_free(g_object);
24 g_object = NULL;
25
26 cl_git_sandbox_cleanup();
27
28 if (git_path_isdir("alternative"))
29 git_futils_rmdir_r("alternative", NULL, GIT_RMDIR_REMOVE_FILES);
30 }
31
32 void test_checkout_tree__cannot_checkout_a_non_treeish(void)
33 {
34 /* blob */
35 cl_git_pass(git_revparse_single(&g_object, g_repo, "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd"));
36 cl_git_fail(git_checkout_tree(g_repo, g_object, NULL));
37 }
38
39 void test_checkout_tree__can_checkout_a_subdirectory_from_a_commit(void)
40 {
41 char *entries[] = { "ab/de/" };
42
43 g_opts.paths.strings = entries;
44 g_opts.paths.count = 1;
45
46 cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees"));
47
48 cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
49
50 cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
51
52 cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/2.txt"));
53 cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/fgh/1.txt"));
54 }
55
56 void test_checkout_tree__can_checkout_and_remove_directory(void)
57 {
58 cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
59
60 /* Checkout brach "subtrees" and update HEAD, so that HEAD matches the
61 * current working tree
62 */
63 cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees"));
64 cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
65
66 cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees", NULL, NULL));
67
68 cl_assert_equal_i(true, git_path_isdir("./testrepo/ab/"));
69 cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/2.txt"));
70 cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/fgh/1.txt"));
71
72 git_object_free(g_object);
73 g_object = NULL;
74
75 /* Checkout brach "master" and update HEAD, so that HEAD matches the
76 * current working tree
77 */
78 cl_git_pass(git_revparse_single(&g_object, g_repo, "master"));
79 cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
80
81 cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master", NULL, NULL));
82
83 /* This directory should no longer exist */
84 cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
85 }
86
87 void test_checkout_tree__can_checkout_a_subdirectory_from_a_subtree(void)
88 {
89 char *entries[] = { "de/" };
90
91 g_opts.paths.strings = entries;
92 g_opts.paths.count = 1;
93
94 cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees:ab"));
95
96 cl_assert_equal_i(false, git_path_isdir("./testrepo/de/"));
97
98 cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
99
100 cl_assert_equal_i(true, git_path_isfile("./testrepo/de/2.txt"));
101 cl_assert_equal_i(true, git_path_isfile("./testrepo/de/fgh/1.txt"));
102 }
103
104 static void progress(const char *path, size_t cur, size_t tot, void *payload)
105 {
106 bool *was_called = (bool*)payload;
107 GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
108 *was_called = true;
109 }
110
111 void test_checkout_tree__calls_progress_callback(void)
112 {
113 bool was_called = 0;
114
115 g_opts.progress_cb = progress;
116 g_opts.progress_payload = &was_called;
117
118 cl_git_pass(git_revparse_single(&g_object, g_repo, "master"));
119
120 cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
121
122 cl_assert_equal_i(was_called, true);
123 }
124
125 void test_checkout_tree__doesnt_write_unrequested_files_to_worktree(void)
126 {
127 git_oid master_oid;
128 git_oid chomped_oid;
129 git_commit* p_master_commit;
130 git_commit* p_chomped_commit;
131 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
132
133 git_oid_fromstr(&master_oid, "a65fedf39aefe402d3bb6e24df4d4f5fe4547750");
134 git_oid_fromstr(&chomped_oid, "e90810b8df3e80c413d903f631643c716887138d");
135 cl_git_pass(git_commit_lookup(&p_master_commit, g_repo, &master_oid));
136 cl_git_pass(git_commit_lookup(&p_chomped_commit, g_repo, &chomped_oid));
137
138 /* GIT_CHECKOUT_NONE should not add any file to the working tree from the
139 * index as it is supposed to be a dry run.
140 */
141 opts.checkout_strategy = GIT_CHECKOUT_NONE;
142 git_checkout_tree(g_repo, (git_object*)p_chomped_commit, &opts);
143 cl_assert_equal_i(false, git_path_isfile("testrepo/readme.txt"));
144
145 git_commit_free(p_master_commit);
146 git_commit_free(p_chomped_commit);
147 }
148
149 void test_checkout_tree__can_switch_branches(void)
150 {
151 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
152 git_oid oid;
153 git_object *obj = NULL;
154
155 assert_on_branch(g_repo, "master");
156
157 /* do first checkout with FORCE because we don't know if testrepo
158 * base data is clean for a checkout or not
159 */
160 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
161
162 cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
163 cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
164
165 cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
166 cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir", NULL, NULL));
167
168 cl_assert(git_path_isfile("testrepo/README"));
169 cl_assert(git_path_isfile("testrepo/branch_file.txt"));
170 cl_assert(git_path_isfile("testrepo/new.txt"));
171 cl_assert(git_path_isfile("testrepo/a/b.txt"));
172
173 cl_assert(!git_path_isdir("testrepo/ab"));
174
175 assert_on_branch(g_repo, "dir");
176
177 git_object_free(obj);
178
179 /* do second checkout safe because we should be clean after first */
180 opts.checkout_strategy = GIT_CHECKOUT_SAFE;
181
182 cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/subtrees"));
183 cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
184
185 cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
186 cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees", NULL, NULL));
187
188 cl_assert(git_path_isfile("testrepo/README"));
189 cl_assert(git_path_isfile("testrepo/branch_file.txt"));
190 cl_assert(git_path_isfile("testrepo/new.txt"));
191 cl_assert(git_path_isfile("testrepo/ab/4.txt"));
192 cl_assert(git_path_isfile("testrepo/ab/c/3.txt"));
193 cl_assert(git_path_isfile("testrepo/ab/de/2.txt"));
194 cl_assert(git_path_isfile("testrepo/ab/de/fgh/1.txt"));
195
196 cl_assert(!git_path_isdir("testrepo/a"));
197
198 assert_on_branch(g_repo, "subtrees");
199
200 git_object_free(obj);
201 }
202
203 void test_checkout_tree__can_remove_untracked(void)
204 {
205 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
206
207 opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_REMOVE_UNTRACKED;
208
209 cl_git_mkfile("testrepo/untracked_file", "as you wish");
210 cl_assert(git_path_isfile("testrepo/untracked_file"));
211
212 cl_git_pass(git_checkout_head(g_repo, &opts));
213
214 cl_assert(!git_path_isfile("testrepo/untracked_file"));
215 }
216
217 void test_checkout_tree__can_remove_ignored(void)
218 {
219 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
220 int ignored = 0;
221
222 opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_REMOVE_IGNORED;
223
224 cl_git_mkfile("testrepo/ignored_file", "as you wish");
225
226 cl_git_pass(git_ignore_add_rule(g_repo, "ignored_file\n"));
227
228 cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "ignored_file"));
229 cl_assert_equal_i(1, ignored);
230
231 cl_assert(git_path_isfile("testrepo/ignored_file"));
232
233 cl_git_pass(git_checkout_head(g_repo, &opts));
234
235 cl_assert(!git_path_isfile("testrepo/ignored_file"));
236 }
237
238 static int checkout_tree_with_blob_ignored_in_workdir(int strategy, bool isdir)
239 {
240 git_oid oid;
241 git_object *obj = NULL;
242 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
243 int ignored = 0, error;
244
245 assert_on_branch(g_repo, "master");
246
247 /* do first checkout with FORCE because we don't know if testrepo
248 * base data is clean for a checkout or not
249 */
250 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
251
252 cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
253 cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
254
255 cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
256 cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir", NULL, NULL));
257
258 cl_assert(git_path_isfile("testrepo/README"));
259 cl_assert(git_path_isfile("testrepo/branch_file.txt"));
260 cl_assert(git_path_isfile("testrepo/new.txt"));
261 cl_assert(git_path_isfile("testrepo/a/b.txt"));
262
263 cl_assert(!git_path_isdir("testrepo/ab"));
264
265 assert_on_branch(g_repo, "dir");
266
267 git_object_free(obj);
268
269 opts.checkout_strategy = strategy;
270
271 if (isdir) {
272 cl_must_pass(p_mkdir("testrepo/ab", 0777));
273 cl_must_pass(p_mkdir("testrepo/ab/4.txt", 0777));
274
275 cl_git_mkfile("testrepo/ab/4.txt/file1.txt", "as you wish");
276 cl_git_mkfile("testrepo/ab/4.txt/file2.txt", "foo bar foo");
277 cl_git_mkfile("testrepo/ab/4.txt/file3.txt", "inky blinky pinky clyde");
278
279 cl_assert(git_path_isdir("testrepo/ab/4.txt"));
280 } else {
281 cl_must_pass(p_mkdir("testrepo/ab", 0777));
282 cl_git_mkfile("testrepo/ab/4.txt", "as you wish");
283
284 cl_assert(git_path_isfile("testrepo/ab/4.txt"));
285 }
286
287 cl_git_pass(git_ignore_add_rule(g_repo, "ab/4.txt\n"));
288
289 cl_git_pass(git_ignore_path_is_ignored(&ignored, g_repo, "ab/4.txt"));
290 cl_assert_equal_i(1, ignored);
291
292 cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/subtrees"));
293 cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
294
295 error = git_checkout_tree(g_repo, obj, &opts);
296
297 git_object_free(obj);
298
299 return error;
300 }
301
302 void test_checkout_tree__conflict_on_ignored_when_not_overwriting(void)
303 {
304 int error;
305
306 cl_git_fail(error = checkout_tree_with_blob_ignored_in_workdir(
307 GIT_CHECKOUT_SAFE | GIT_CHECKOUT_DONT_OVERWRITE_IGNORED, false));
308
309 cl_assert_equal_i(GIT_EMERGECONFLICT, error);
310 }
311
312 void test_checkout_tree__can_overwrite_ignored_by_default(void)
313 {
314 cl_git_pass(checkout_tree_with_blob_ignored_in_workdir(GIT_CHECKOUT_SAFE, false));
315
316 cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees", NULL, NULL));
317
318 cl_assert(git_path_isfile("testrepo/ab/4.txt"));
319
320 assert_on_branch(g_repo, "subtrees");
321 }
322
323 void test_checkout_tree__conflict_on_ignored_folder_when_not_overwriting(void)
324 {
325 int error;
326
327 cl_git_fail(error = checkout_tree_with_blob_ignored_in_workdir(
328 GIT_CHECKOUT_SAFE | GIT_CHECKOUT_DONT_OVERWRITE_IGNORED, true));
329
330 cl_assert_equal_i(GIT_EMERGECONFLICT, error);
331 }
332
333 void test_checkout_tree__can_overwrite_ignored_folder_by_default(void)
334 {
335 cl_git_pass(checkout_tree_with_blob_ignored_in_workdir(GIT_CHECKOUT_SAFE, true));
336
337 cl_git_pass(git_repository_set_head(g_repo, "refs/heads/subtrees", NULL, NULL));
338
339 cl_assert(git_path_isfile("testrepo/ab/4.txt"));
340
341 assert_on_branch(g_repo, "subtrees");
342
343 }
344
345 void test_checkout_tree__can_update_only(void)
346 {
347 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
348 git_oid oid;
349 git_object *obj = NULL;
350
351 /* first let's get things into a known state - by checkout out the HEAD */
352
353 assert_on_branch(g_repo, "master");
354
355 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
356 cl_git_pass(git_checkout_head(g_repo, &opts));
357
358 cl_assert(!git_path_isdir("testrepo/a"));
359
360 check_file_contents_nocr("testrepo/branch_file.txt", "hi\nbye!\n");
361
362 /* now checkout branch but with update only */
363
364 opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_UPDATE_ONLY;
365
366 cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
367 cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
368
369 cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
370 cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir", NULL, NULL));
371
372 assert_on_branch(g_repo, "dir");
373
374 /* this normally would have been created (which was tested separately in
375 * the test_checkout_tree__can_switch_branches test), but with
376 * UPDATE_ONLY it will not have been created.
377 */
378 cl_assert(!git_path_isdir("testrepo/a"));
379
380 /* but this file still should have been updated */
381 check_file_contents_nocr("testrepo/branch_file.txt", "hi\n");
382
383 git_object_free(obj);
384 }
385
386 void test_checkout_tree__can_checkout_with_pattern(void)
387 {
388 char *entries[] = { "[l-z]*.txt" };
389
390 /* reset to beginning of history (i.e. just a README file) */
391
392 g_opts.checkout_strategy =
393 GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
394
395 cl_git_pass(git_revparse_single(&g_object, g_repo, "8496071c1b46c854b31185ea97743be6a8774479"));
396
397 cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
398 cl_git_pass(
399 git_repository_set_head_detached(g_repo, git_object_id(g_object), NULL, NULL));
400
401 git_object_free(g_object);
402 g_object = NULL;
403
404 cl_assert(git_path_exists("testrepo/README"));
405 cl_assert(!git_path_exists("testrepo/branch_file.txt"));
406 cl_assert(!git_path_exists("testrepo/link_to_new.txt"));
407 cl_assert(!git_path_exists("testrepo/new.txt"));
408
409 /* now to a narrow patterned checkout */
410
411 g_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
412 g_opts.paths.strings = entries;
413 g_opts.paths.count = 1;
414
415 cl_git_pass(git_revparse_single(&g_object, g_repo, "refs/heads/master"));
416
417 cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
418
419 cl_assert(git_path_exists("testrepo/README"));
420 cl_assert(!git_path_exists("testrepo/branch_file.txt"));
421 cl_assert(git_path_exists("testrepo/link_to_new.txt"));
422 cl_assert(git_path_exists("testrepo/new.txt"));
423 }
424
425 void test_checkout_tree__can_disable_pattern_match(void)
426 {
427 char *entries[] = { "b*.txt" };
428
429 /* reset to beginning of history (i.e. just a README file) */
430
431 g_opts.checkout_strategy =
432 GIT_CHECKOUT_FORCE | GIT_CHECKOUT_REMOVE_UNTRACKED;
433
434 cl_git_pass(git_revparse_single(&g_object, g_repo, "8496071c1b46c854b31185ea97743be6a8774479"));
435
436 cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
437 cl_git_pass(
438 git_repository_set_head_detached(g_repo, git_object_id(g_object), NULL, NULL));
439
440 git_object_free(g_object);
441 g_object = NULL;
442
443 cl_assert(!git_path_isfile("testrepo/branch_file.txt"));
444
445 /* now to a narrow patterned checkout, but disable pattern */
446
447 g_opts.checkout_strategy =
448 GIT_CHECKOUT_SAFE_CREATE | GIT_CHECKOUT_DISABLE_PATHSPEC_MATCH;
449 g_opts.paths.strings = entries;
450 g_opts.paths.count = 1;
451
452 cl_git_pass(git_revparse_single(&g_object, g_repo, "refs/heads/master"));
453
454 cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
455
456 cl_assert(!git_path_isfile("testrepo/branch_file.txt"));
457
458 /* let's try that again, but allow the pattern match */
459
460 g_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
461
462 cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
463
464 cl_assert(git_path_isfile("testrepo/branch_file.txt"));
465 }
466
467 void assert_conflict(
468 const char *entry_path,
469 const char *new_content,
470 const char *parent_sha,
471 const char *commit_sha)
472 {
473 git_index *index;
474 git_object *hack_tree;
475 git_reference *branch, *head;
476 git_buf file_path = GIT_BUF_INIT;
477
478 cl_git_pass(git_repository_index(&index, g_repo));
479
480 /* Create a branch pointing at the parent */
481 cl_git_pass(git_revparse_single(&g_object, g_repo, parent_sha));
482 cl_git_pass(git_branch_create(&branch, g_repo,
483 "potential_conflict", (git_commit *)g_object, 0, NULL, NULL));
484
485 /* Make HEAD point to this branch */
486 cl_git_pass(git_reference_symbolic_create(
487 &head, g_repo, "HEAD", git_reference_name(branch), 1, NULL, NULL));
488 git_reference_free(head);
489 git_reference_free(branch);
490
491 /* Checkout the parent */
492 g_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
493 cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
494
495 /* Hack-ishy workaound to ensure *all* the index entries
496 * match the content of the tree
497 */
498 cl_git_pass(git_object_peel(&hack_tree, g_object, GIT_OBJ_TREE));
499 cl_git_pass(git_index_read_tree(index, (git_tree *)hack_tree));
500 git_object_free(hack_tree);
501 git_object_free(g_object);
502 g_object = NULL;
503
504 /* Create a conflicting file */
505 cl_git_pass(git_buf_joinpath(&file_path, "./testrepo", entry_path));
506 cl_git_mkfile(git_buf_cstr(&file_path), new_content);
507 git_buf_free(&file_path);
508
509 /* Trying to checkout the original commit */
510 cl_git_pass(git_revparse_single(&g_object, g_repo, commit_sha));
511
512 g_opts.checkout_strategy = GIT_CHECKOUT_SAFE;
513 cl_assert_equal_i(
514 GIT_EMERGECONFLICT, git_checkout_tree(g_repo, g_object, &g_opts));
515
516 /* Stage the conflicting change */
517 cl_git_pass(git_index_add_bypath(index, entry_path));
518 cl_git_pass(git_index_write(index));
519 git_index_free(index);
520
521 cl_assert_equal_i(
522 GIT_EMERGECONFLICT, git_checkout_tree(g_repo, g_object, &g_opts));
523 }
524
525 void test_checkout_tree__checking_out_a_conflicting_type_change_returns_EMERGECONFLICT(void)
526 {
527 /*
528 * 099faba adds a symlink named 'link_to_new.txt'
529 * a65fedf is the parent of 099faba
530 */
531
532 assert_conflict("link_to_new.txt", "old.txt", "a65fedf", "099faba");
533 }
534
535 void test_checkout_tree__checking_out_a_conflicting_type_change_returns_EMERGECONFLICT_2(void)
536 {
537 /*
538 * cf80f8d adds a directory named 'a/'
539 * a4a7dce is the parent of cf80f8d
540 */
541
542 assert_conflict("a", "hello\n", "a4a7dce", "cf80f8d");
543 }
544
545 void test_checkout_tree__checking_out_a_conflicting_content_change_returns_EMERGECONFLICT(void)
546 {
547 /*
548 * c47800c adds a symlink named 'branch_file.txt'
549 * 5b5b025 is the parent of 763d71a
550 */
551
552 assert_conflict("branch_file.txt", "hello\n", "5b5b025", "c47800c");
553 }
554
555 void test_checkout_tree__donot_update_deleted_file_by_default(void)
556 {
557 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
558 git_oid old_id, new_id;
559 git_commit *old_commit = NULL, *new_commit = NULL;
560 git_index *index = NULL;
561 checkout_counts ct;
562
563 opts.checkout_strategy = GIT_CHECKOUT_SAFE;
564
565 memset(&ct, 0, sizeof(ct));
566 opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
567 opts.notify_cb = checkout_count_callback;
568 opts.notify_payload = &ct;
569
570 cl_git_pass(git_repository_index(&index, g_repo));
571
572 cl_git_pass(git_oid_fromstr(&old_id, "be3563ae3f795b2b4353bcce3a527ad0a4f7f644"));
573 cl_git_pass(git_commit_lookup(&old_commit, g_repo, &old_id));
574 cl_git_pass(git_reset(g_repo, (git_object *)old_commit, GIT_RESET_HARD, NULL, NULL, NULL));
575
576 cl_git_pass(p_unlink("testrepo/branch_file.txt"));
577 cl_git_pass(git_index_remove_bypath(index ,"branch_file.txt"));
578 cl_git_pass(git_index_write(index));
579
580 cl_assert(!git_path_exists("testrepo/branch_file.txt"));
581
582 cl_git_pass(git_oid_fromstr(&new_id, "099fabac3a9ea935598528c27f866e34089c2eff"));
583 cl_git_pass(git_commit_lookup(&new_commit, g_repo, &new_id));
584
585
586 cl_git_fail(git_checkout_tree(g_repo, (git_object *)new_commit, &opts));
587
588 cl_assert_equal_i(1, ct.n_conflicts);
589 cl_assert_equal_i(1, ct.n_updates);
590
591 git_commit_free(old_commit);
592 git_commit_free(new_commit);
593 git_index_free(index);
594 }
595
596 struct checkout_cancel_at {
597 const char *filename;
598 int error;
599 int count;
600 };
601
602 static int checkout_cancel_cb(
603 git_checkout_notify_t why,
604 const char *path,
605 const git_diff_file *b,
606 const git_diff_file *t,
607 const git_diff_file *w,
608 void *payload)
609 {
610 struct checkout_cancel_at *ca = payload;
611
612 GIT_UNUSED(why); GIT_UNUSED(b); GIT_UNUSED(t); GIT_UNUSED(w);
613
614 ca->count++;
615
616 if (!strcmp(path, ca->filename))
617 return ca->error;
618
619 return 0;
620 }
621
622 void test_checkout_tree__can_cancel_checkout_from_notify(void)
623 {
624 struct checkout_cancel_at ca;
625 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
626 git_oid oid;
627 git_object *obj = NULL;
628
629 assert_on_branch(g_repo, "master");
630
631 cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
632 cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
633
634 ca.filename = "new.txt";
635 ca.error = -5555;
636 ca.count = 0;
637
638 opts.notify_flags = GIT_CHECKOUT_NOTIFY_UPDATED;
639 opts.notify_cb = checkout_cancel_cb;
640 opts.notify_payload = &ca;
641 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
642
643 cl_assert(!git_path_exists("testrepo/new.txt"));
644
645 cl_git_fail_with(git_checkout_tree(g_repo, obj, &opts), -5555);
646
647 cl_assert(!git_path_exists("testrepo/new.txt"));
648 cl_assert_equal_i(4, ca.count);
649
650 /* and again with a different stopping point and return code */
651 ca.filename = "README";
652 ca.error = 123;
653 ca.count = 0;
654
655 cl_git_fail_with(git_checkout_tree(g_repo, obj, &opts), 123);
656
657 cl_assert(!git_path_exists("testrepo/new.txt"));
658 cl_assert_equal_i(1, ca.count);
659
660 git_object_free(obj);
661 }
662
663 void test_checkout_tree__can_checkout_with_last_workdir_item_missing(void)
664 {
665 git_index *index = NULL;
666 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
667 git_oid tree_id, commit_id;
668 git_tree *tree = NULL;
669 git_commit *commit = NULL;
670
671 git_repository_index(&index, g_repo);
672
673 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
674
675 cl_git_pass(git_reference_name_to_id(&commit_id, g_repo, "refs/heads/master"));
676 cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id));
677
678 cl_git_pass(git_checkout_tree(g_repo, (git_object *)commit, &opts));
679 cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master", NULL, NULL));
680
681 cl_git_pass(p_mkdir("./testrepo/this-is-dir", 0777));
682 cl_git_mkfile("./testrepo/this-is-dir/contained_file", "content\n");
683
684 cl_git_pass(git_index_add_bypath(index, "this-is-dir/contained_file"));
685 git_index_write_tree(&tree_id, index);
686 cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
687
688 cl_git_pass(p_unlink("./testrepo/this-is-dir/contained_file"));
689
690 opts.checkout_strategy = GIT_CHECKOUT_SAFE;
691
692 opts.checkout_strategy = 1;
693 git_checkout_tree(g_repo, (git_object *)tree, &opts);
694
695 git_tree_free(tree);
696 git_commit_free(commit);
697 git_index_free(index);
698 }
699
700 void test_checkout_tree__issue_1397(void)
701 {
702 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
703 const char *partial_oid = "8a7ef04";
704 git_object *tree = NULL;
705
706 test_checkout_tree__cleanup(); /* cleanup default checkout */
707
708 g_repo = cl_git_sandbox_init("issue_1397");
709
710 cl_repo_set_bool(g_repo, "core.autocrlf", true);
711
712 cl_git_pass(git_revparse_single(&tree, g_repo, partial_oid));
713
714 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
715
716 cl_git_pass(git_checkout_tree(g_repo, tree, &opts));
717
718 check_file_contents("./issue_1397/crlf_file.txt", "first line\r\nsecond line\r\nboth with crlf");
719
720 git_object_free(tree);
721 }
722
723 void test_checkout_tree__can_write_to_empty_dirs(void)
724 {
725 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
726 git_oid oid;
727 git_object *obj = NULL;
728
729 assert_on_branch(g_repo, "master");
730
731 cl_git_pass(p_mkdir("testrepo/a", 0777));
732
733 /* do first checkout with FORCE because we don't know if testrepo
734 * base data is clean for a checkout or not
735 */
736 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
737
738 cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
739 cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
740
741 cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
742
743 cl_assert(git_path_isfile("testrepo/a/b.txt"));
744
745 git_object_free(obj);
746 }
747
748 void test_checkout_tree__fails_when_dir_in_use(void)
749 {
750 #ifdef GIT_WIN32
751 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
752 git_oid oid;
753 git_object *obj = NULL;
754
755 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
756
757 cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
758 cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
759
760 cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
761
762 cl_assert(git_path_isfile("testrepo/a/b.txt"));
763
764 git_object_free(obj);
765
766 cl_git_pass(p_chdir("testrepo/a"));
767
768 cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/master"));
769 cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
770
771 cl_git_fail(git_checkout_tree(g_repo, obj, &opts));
772
773 cl_git_pass(p_chdir("../.."));
774
775 cl_assert(git_path_is_empty_dir("testrepo/a"));
776
777 git_object_free(obj);
778 #endif
779 }
780
781 void test_checkout_tree__can_continue_when_dir_in_use(void)
782 {
783 #ifdef GIT_WIN32
784 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
785 git_oid oid;
786 git_object *obj = NULL;
787
788 opts.checkout_strategy = GIT_CHECKOUT_FORCE |
789 GIT_CHECKOUT_SKIP_LOCKED_DIRECTORIES;
790
791 cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
792 cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
793
794 cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
795
796 cl_assert(git_path_isfile("testrepo/a/b.txt"));
797
798 git_object_free(obj);
799
800 cl_git_pass(p_chdir("testrepo/a"));
801
802 cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/master"));
803 cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
804
805 cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
806
807 cl_git_pass(p_chdir("../.."));
808
809 cl_assert(git_path_is_empty_dir("testrepo/a"));
810
811 git_object_free(obj);
812 #endif
813 }
814
815 void test_checkout_tree__target_directory_from_bare(void)
816 {
817 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
818 git_oid oid;
819 checkout_counts cts;
820 memset(&cts, 0, sizeof(cts));
821
822 test_checkout_tree__cleanup(); /* cleanup default checkout */
823
824 g_repo = cl_git_sandbox_init("testrepo.git");
825 cl_assert(git_repository_is_bare(g_repo));
826
827 opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
828
829 opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
830 opts.notify_cb = checkout_count_callback;
831 opts.notify_payload = &cts;
832
833 cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
834 cl_git_pass(git_object_lookup(&g_object, g_repo, &oid, GIT_OBJ_ANY));
835
836 cl_git_fail(git_checkout_tree(g_repo, g_object, &opts));
837
838 opts.target_directory = "alternative";
839 cl_assert(!git_path_isdir("alternative"));
840
841 cl_git_pass(git_checkout_tree(g_repo, g_object, &opts));
842
843 cl_assert_equal_i(0, cts.n_untracked);
844 cl_assert_equal_i(0, cts.n_ignored);
845 cl_assert_equal_i(3, cts.n_updates);
846
847 check_file_contents_nocr("./alternative/README", "hey there\n");
848 check_file_contents_nocr("./alternative/branch_file.txt", "hi\nbye!\n");
849 check_file_contents_nocr("./alternative/new.txt", "my new file\n");
850
851 cl_git_pass(git_futils_rmdir_r(
852 "alternative", NULL, GIT_RMDIR_REMOVE_FILES));
853 }
854
855 void test_checkout_tree__extremely_long_file_name(void)
856 {
857 // A utf-8 string with 83 characters, but 249 bytes.
858 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";
859 char path[1024];
860
861 g_opts.checkout_strategy = GIT_CHECKOUT_FORCE;
862 cl_git_pass(git_revparse_single(&g_object, g_repo, "long-file-name"));
863 cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
864
865 sprintf(path, "testrepo/%s.txt", longname);
866 cl_assert(git_path_exists(path));
867
868 git_object_free(g_object);
869 cl_git_pass(git_revparse_single(&g_object, g_repo, "master"));
870 cl_git_pass(git_checkout_tree(g_repo, g_object, &g_opts));
871 cl_assert(!git_path_exists(path));
872 }
873
874 static void create_conflict(const char *path)
875 {
876 git_index *index;
877 git_index_entry entry;
878
879 cl_git_pass(git_repository_index(&index, g_repo));
880
881 memset(&entry, 0x0, sizeof(git_index_entry));
882 entry.mode = 0100644;
883 entry.flags = 1 << GIT_IDXENTRY_STAGESHIFT;
884 git_oid_fromstr(&entry.id, "d427e0b2e138501a3d15cc376077a3631e15bd46");
885 entry.path = path;
886 cl_git_pass(git_index_add(index, &entry));
887
888 entry.flags = 2 << GIT_IDXENTRY_STAGESHIFT;
889 git_oid_fromstr(&entry.id, "ee3fa1b8c00aff7fe02065fdb50864bb0d932ccf");
890 cl_git_pass(git_index_add(index, &entry));
891
892 entry.flags = 3 << GIT_IDXENTRY_STAGESHIFT;
893 git_oid_fromstr(&entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863");
894 cl_git_pass(git_index_add(index, &entry));
895
896 git_index_write(index);
897 git_index_free(index);
898 }
899
900 void test_checkout_tree__fails_when_conflicts_exist_in_index(void)
901 {
902 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
903 git_oid oid;
904 git_object *obj = NULL;
905
906 opts.checkout_strategy = GIT_CHECKOUT_SAFE;
907
908 cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
909 cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
910
911 create_conflict("conflicts.txt");
912
913 cl_git_fail(git_checkout_tree(g_repo, obj, &opts));
914
915 git_object_free(obj);
916 }
917
918 void test_checkout_tree__filemode_preserved_in_index(void)
919 {
920 git_oid executable_oid;
921 git_commit *commit;
922 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
923 git_index *index;
924 const git_index_entry *entry;
925
926 cl_git_pass(git_repository_index(&index, g_repo));
927
928 cl_git_pass(git_oid_fromstr(&executable_oid, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6"));
929 cl_git_pass(git_commit_lookup(&commit, g_repo, &executable_oid));
930
931 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
932
933 cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
934 cl_assert(entry = git_index_get_bypath(index, "executable.txt", 0));
935 cl_assert_equal_i(0100755, entry->mode);
936
937 git_commit_free(commit);
938 git_index_free(index);
939 }
940
941 void test_checkout_tree__removes_conflicts(void)
942 {
943 git_oid commit_id;
944 git_commit *commit;
945 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
946 git_index *index;
947
948 cl_git_pass(git_oid_fromstr(&commit_id, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6"));
949 cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id));
950
951 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
952
953 cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
954
955 cl_git_pass(git_repository_index(&index, g_repo));
956 cl_git_pass(git_index_remove(index, "executable.txt", 0));
957
958 create_conflict("executable.txt");
959 cl_git_mkfile("testrepo/executable.txt", "This is the conflict file.\n");
960
961 create_conflict("other.txt");
962 cl_git_mkfile("testrepo/other.txt", "This is another conflict file.\n");
963
964 git_index_write(index);
965
966 cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
967
968 cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 1));
969 cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 2));
970 cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 3));
971
972 cl_assert_equal_p(NULL, git_index_get_bypath(index, "other.txt", 1));
973 cl_assert_equal_p(NULL, git_index_get_bypath(index, "other.txt", 2));
974 cl_assert_equal_p(NULL, git_index_get_bypath(index, "other.txt", 3));
975
976 cl_assert(!git_path_exists("testrepo/other.txt"));
977
978 git_commit_free(commit);
979 git_index_free(index);
980 }
981
982
983 void test_checkout_tree__removes_conflicts_only_by_pathscope(void)
984 {
985 git_oid commit_id;
986 git_commit *commit;
987 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
988 git_index *index;
989 const char *path = "executable.txt";
990
991 cl_git_pass(git_oid_fromstr(&commit_id, "afe4393b2b2a965f06acf2ca9658eaa01e0cd6b6"));
992 cl_git_pass(git_commit_lookup(&commit, g_repo, &commit_id));
993
994 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
995 opts.paths.count = 1;
996 opts.paths.strings = (char **)&path;
997
998 cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
999
1000 cl_git_pass(git_repository_index(&index, g_repo));
1001 cl_git_pass(git_index_remove(index, "executable.txt", 0));
1002
1003 create_conflict("executable.txt");
1004 cl_git_mkfile("testrepo/executable.txt", "This is the conflict file.\n");
1005
1006 create_conflict("other.txt");
1007 cl_git_mkfile("testrepo/other.txt", "This is another conflict file.\n");
1008
1009 git_index_write(index);
1010
1011 cl_git_pass(git_checkout_tree(g_repo, (const git_object *)commit, &opts));
1012
1013 cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 1));
1014 cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 2));
1015 cl_assert_equal_p(NULL, git_index_get_bypath(index, "executable.txt", 3));
1016
1017 cl_assert(git_index_get_bypath(index, "other.txt", 1) != NULL);
1018 cl_assert(git_index_get_bypath(index, "other.txt", 2) != NULL);
1019 cl_assert(git_index_get_bypath(index, "other.txt", 3) != NULL);
1020
1021 cl_assert(git_path_exists("testrepo/other.txt"));
1022
1023 git_commit_free(commit);
1024 git_index_free(index);
1025 }
1026
1027 void test_checkout_tree__case_changing_rename(void)
1028 {
1029 git_index *index;
1030 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
1031 git_oid master_id, dir_commit_id, tree_id, commit_id;
1032 git_commit *master_commit, *dir_commit;
1033 git_tree *tree;
1034 git_signature *signature;
1035 const git_index_entry *index_entry;
1036 bool case_sensitive;
1037
1038 assert_on_branch(g_repo, "master");
1039
1040 cl_git_pass(git_repository_index(&index, g_repo));
1041
1042 /* Switch branches and perform a case-changing rename */
1043
1044 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
1045
1046 cl_git_pass(git_reference_name_to_id(&dir_commit_id, g_repo, "refs/heads/dir"));
1047 cl_git_pass(git_commit_lookup(&dir_commit, g_repo, &dir_commit_id));
1048
1049 cl_git_pass(git_checkout_tree(g_repo, (git_object *)dir_commit, &opts));
1050 cl_git_pass(git_repository_set_head(g_repo, "refs/heads/dir", NULL, NULL));
1051
1052 cl_assert(git_path_isfile("testrepo/README"));
1053 case_sensitive = !git_path_isfile("testrepo/readme");
1054
1055 cl_assert(index_entry = git_index_get_bypath(index, "README", 0));
1056 cl_assert_equal_s("README", index_entry->path);
1057
1058 cl_git_pass(git_index_remove_bypath(index, "README"));
1059 cl_git_pass(p_rename("testrepo/README", "testrepo/__readme__"));
1060 cl_git_pass(p_rename("testrepo/__readme__", "testrepo/readme"));
1061 cl_git_append2file("testrepo/readme", "An addendum...");
1062 cl_git_pass(git_index_add_bypath(index, "readme"));
1063
1064 cl_git_pass(git_index_write(index));
1065
1066 cl_git_pass(git_index_write_tree(&tree_id, index));
1067 cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
1068
1069 cl_git_pass(git_signature_new(&signature, "Renamer", "rename@contoso.com", time(NULL), 0));
1070
1071 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));
1072
1073 cl_assert(git_path_isfile("testrepo/readme"));
1074 if (case_sensitive)
1075 cl_assert(!git_path_isfile("testrepo/README"));
1076
1077 cl_assert(index_entry = git_index_get_bypath(index, "readme", 0));
1078 cl_assert_equal_s("readme", index_entry->path);
1079
1080 /* Switching back to master should rename readme -> README */
1081 opts.checkout_strategy = GIT_CHECKOUT_SAFE;
1082
1083 cl_git_pass(git_reference_name_to_id(&master_id, g_repo, "refs/heads/master"));
1084 cl_git_pass(git_commit_lookup(&master_commit, g_repo, &master_id));
1085
1086 cl_git_pass(git_checkout_tree(g_repo, (git_object *)master_commit, &opts));
1087 cl_git_pass(git_repository_set_head(g_repo, "refs/heads/master", NULL, NULL));
1088
1089 assert_on_branch(g_repo, "master");
1090
1091 cl_assert(git_path_isfile("testrepo/README"));
1092 if (case_sensitive)
1093 cl_assert(!git_path_isfile("testrepo/readme"));
1094
1095 cl_assert(index_entry = git_index_get_bypath(index, "README", 0));
1096 cl_assert_equal_s("README", index_entry->path);
1097
1098 git_index_free(index);
1099 git_signature_free(signature);
1100 git_tree_free(tree);
1101 git_commit_free(dir_commit);
1102 git_commit_free(master_commit);
1103 }
1104
1105 void perfdata_cb(const git_checkout_perfdata *in, void *payload)
1106 {
1107 memcpy(payload, in, sizeof(git_checkout_perfdata));
1108 }
1109
1110 void test_checkout_tree__can_collect_perfdata(void)
1111 {
1112 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
1113 git_oid oid;
1114 git_object *obj = NULL;
1115 git_checkout_perfdata perfdata = {0};
1116
1117 opts.perfdata_cb = perfdata_cb;
1118 opts.perfdata_payload = &perfdata;
1119
1120 assert_on_branch(g_repo, "master");
1121 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
1122
1123 cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
1124 cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
1125
1126 cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
1127
1128 cl_assert(perfdata.mkdir_calls > 0);
1129 cl_assert(perfdata.stat_calls > 0);
1130
1131 git_object_free(obj);
1132 }
1133
1134 void update_attr_callback(
1135 const char *path,
1136 size_t completed_steps,
1137 size_t total_steps,
1138 void *payload)
1139 {
1140 GIT_UNUSED(completed_steps);
1141 GIT_UNUSED(total_steps);
1142 GIT_UNUSED(payload);
1143
1144 if (path && strcmp(path, "ident1.txt") == 0)
1145 cl_git_write2file("testrepo/.gitattributes",
1146 "*.txt ident\n", 12, O_RDWR|O_CREAT, 0666);
1147 }
1148
1149 void test_checkout_tree__caches_attributes_during_checkout(void)
1150 {
1151 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
1152 git_oid oid;
1153 git_object *obj = NULL;
1154 git_buf ident1 = GIT_BUF_INIT, ident2 = GIT_BUF_INIT;
1155 char *ident_paths[] = { "ident1.txt", "ident2.txt" };
1156
1157 opts.progress_cb = update_attr_callback;
1158
1159 assert_on_branch(g_repo, "master");
1160 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
1161 opts.paths.strings = ident_paths;
1162 opts.paths.count = 2;
1163
1164 cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/ident"));
1165 cl_git_pass(git_object_lookup(&obj, g_repo, &oid, GIT_OBJ_ANY));
1166
1167 cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
1168
1169 cl_git_pass(git_futils_readbuffer(&ident1, "testrepo/ident1.txt"));
1170 cl_git_pass(git_futils_readbuffer(&ident2, "testrepo/ident2.txt"));
1171
1172 cl_assert_equal_strn(ident1.ptr, "# $Id$", 6);
1173 cl_assert_equal_strn(ident2.ptr, "# $Id$", 6);
1174
1175 cl_git_pass(git_checkout_tree(g_repo, obj, &opts));
1176
1177 cl_git_pass(git_futils_readbuffer(&ident1, "testrepo/ident1.txt"));
1178 cl_git_pass(git_futils_readbuffer(&ident2, "testrepo/ident2.txt"));
1179
1180 cl_assert_equal_strn(ident1.ptr, "# $Id: ", 7);
1181 cl_assert_equal_strn(ident2.ptr, "# $Id: ", 7);
1182
1183 git_buf_free(&ident1);
1184 git_buf_free(&ident2);
1185 git_object_free(obj);
1186 }
1187
1188 void test_checkout_tree__can_not_update_index(void)
1189 {
1190 git_oid oid;
1191 git_object *head;
1192 unsigned int status;
1193 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
1194 git_index *index;
1195
1196 opts.checkout_strategy |=
1197 GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DONT_UPDATE_INDEX;
1198
1199 cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
1200 cl_git_pass(git_object_lookup(&head, g_repo, &oid, GIT_OBJ_ANY));
1201
1202 cl_git_pass(git_reset(g_repo, head, GIT_RESET_HARD, &g_opts, NULL, NULL));
1203
1204 cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
1205
1206 cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees"));
1207
1208 cl_git_pass(git_checkout_tree(g_repo, g_object, &opts));
1209
1210 cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/2.txt"));
1211 cl_git_pass(git_status_file(&status, g_repo, "ab/de/2.txt"));
1212 cl_assert_equal_i(GIT_STATUS_WT_NEW, status);
1213
1214 cl_git_pass(git_repository_index(&index, g_repo));
1215 cl_git_pass(git_index_write(index));
1216
1217 cl_git_pass(git_status_file(&status, g_repo, "ab/de/2.txt"));
1218 cl_assert_equal_i(GIT_STATUS_WT_NEW, status);
1219
1220 git_object_free(head);
1221 git_index_free(index);
1222 }
1223
1224 void test_checkout_tree__can_update_but_not_write_index(void)
1225 {
1226 git_oid oid;
1227 git_object *head;
1228 unsigned int status;
1229 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
1230 git_index *index;
1231 git_repository *other;
1232
1233 opts.checkout_strategy |=
1234 GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DONT_WRITE_INDEX;
1235
1236 cl_git_pass(git_reference_name_to_id(&oid, g_repo, "HEAD"));
1237 cl_git_pass(git_object_lookup(&head, g_repo, &oid, GIT_OBJ_ANY));
1238
1239 cl_git_pass(git_reset(g_repo, head, GIT_RESET_HARD, &g_opts, NULL, NULL));
1240
1241 cl_assert_equal_i(false, git_path_isdir("./testrepo/ab/"));
1242
1243 cl_git_pass(git_revparse_single(&g_object, g_repo, "subtrees"));
1244
1245 cl_git_pass(git_checkout_tree(g_repo, g_object, &opts));
1246
1247 cl_assert_equal_i(true, git_path_isfile("./testrepo/ab/de/2.txt"));
1248 cl_git_pass(git_status_file(&status, g_repo, "ab/de/2.txt"));
1249 cl_assert_equal_i(GIT_STATUS_INDEX_NEW, status);
1250
1251 cl_git_pass(git_repository_open(&other, "testrepo"));
1252 cl_git_pass(git_status_file(&status, other, "ab/de/2.txt"));
1253 cl_assert_equal_i(GIT_STATUS_WT_NEW, status);
1254 git_repository_free(other);
1255
1256 cl_git_pass(git_repository_index(&index, g_repo));
1257 cl_git_pass(git_index_write(index));
1258
1259 cl_git_pass(git_repository_open(&other, "testrepo"));
1260 cl_git_pass(git_status_file(&status, other, "ab/de/2.txt"));
1261 cl_assert_equal_i(GIT_STATUS_INDEX_NEW, status);
1262 git_repository_free(other);
1263
1264 git_object_free(head);
1265 git_index_free(index);
1266 }