]> git.proxmox.com Git - libgit2.git/blob - tests/checkout/index.c
Merge pull request #3796 from mmuman/haiku
[libgit2.git] / tests / checkout / index.c
1 #include "clar_libgit2.h"
2 #include "checkout_helpers.h"
3
4 #include "git2/checkout.h"
5 #include "fileops.h"
6 #include "repository.h"
7 #include "remote.h"
8
9 static git_repository *g_repo;
10
11 void test_checkout_index__initialize(void)
12 {
13 git_tree *tree;
14
15 g_repo = cl_git_sandbox_init("testrepo");
16
17 cl_git_pass(git_repository_head_tree(&tree, g_repo));
18
19 reset_index_to_treeish((git_object *)tree);
20 git_tree_free(tree);
21
22 cl_git_rewritefile(
23 "./testrepo/.gitattributes",
24 "* text eol=lf\n");
25 }
26
27 void test_checkout_index__cleanup(void)
28 {
29 cl_git_sandbox_cleanup();
30
31 /* try to remove alternative dir */
32 if (git_path_isdir("alternative"))
33 git_futils_rmdir_r("alternative", NULL, GIT_RMDIR_REMOVE_FILES);
34 }
35
36 void test_checkout_index__cannot_checkout_a_bare_repository(void)
37 {
38 test_checkout_index__cleanup();
39
40 g_repo = cl_git_sandbox_init("testrepo.git");
41
42 cl_git_fail(git_checkout_index(g_repo, NULL, NULL));
43 }
44
45 void test_checkout_index__can_create_missing_files(void)
46 {
47 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
48
49 cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
50 cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
51 cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));
52
53 opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
54
55 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
56
57 check_file_contents("./testrepo/README", "hey there\n");
58 check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
59 check_file_contents("./testrepo/new.txt", "my new file\n");
60 }
61
62 void test_checkout_index__can_remove_untracked_files(void)
63 {
64 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
65
66 git_futils_mkdir("./testrepo/dir/subdir/subsubdir", 0755, GIT_MKDIR_PATH);
67 cl_git_mkfile("./testrepo/dir/one", "one\n");
68 cl_git_mkfile("./testrepo/dir/subdir/two", "two\n");
69
70 cl_assert_equal_i(true, git_path_isdir("./testrepo/dir/subdir/subsubdir"));
71
72 opts.checkout_strategy =
73 GIT_CHECKOUT_SAFE |
74 GIT_CHECKOUT_RECREATE_MISSING |
75 GIT_CHECKOUT_REMOVE_UNTRACKED;
76
77 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
78
79 cl_assert_equal_i(false, git_path_isdir("./testrepo/dir"));
80 }
81
82 void test_checkout_index__honor_the_specified_pathspecs(void)
83 {
84 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
85 char *entries[] = { "*.txt" };
86
87 opts.paths.strings = entries;
88 opts.paths.count = 1;
89
90 cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
91 cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
92 cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));
93
94 opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
95
96 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
97
98 cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
99 check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
100 check_file_contents("./testrepo/new.txt", "my new file\n");
101 }
102
103 void test_checkout_index__honor_the_gitattributes_directives(void)
104 {
105 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
106 const char *attributes =
107 "branch_file.txt text eol=crlf\n"
108 "new.txt text eol=lf\n";
109
110 cl_git_mkfile("./testrepo/.gitattributes", attributes);
111 cl_repo_set_bool(g_repo, "core.autocrlf", false);
112
113 opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
114
115 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
116
117 check_file_contents("./testrepo/README", "hey there\n");
118 check_file_contents("./testrepo/new.txt", "my new file\n");
119 check_file_contents("./testrepo/branch_file.txt", "hi\r\nbye!\r\n");
120 }
121
122 void test_checkout_index__honor_coreautocrlf_setting_set_to_true(void)
123 {
124 #ifdef GIT_WIN32
125 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
126 const char *expected_readme_text = "hey there\r\n";
127
128 cl_git_pass(p_unlink("./testrepo/.gitattributes"));
129 cl_repo_set_bool(g_repo, "core.autocrlf", true);
130
131 opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
132
133 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
134
135 check_file_contents("./testrepo/README", expected_readme_text);
136 #endif
137 }
138
139 void test_checkout_index__honor_coresymlinks_default(void)
140 {
141 git_repository *repo;
142 git_remote *origin;
143 git_object *target;
144 char cwd[GIT_PATH_MAX];
145
146 const char *url = git_repository_path(g_repo);
147
148 cl_assert(getcwd(cwd, sizeof(cwd)) != NULL);
149 cl_assert_equal_i(0, p_mkdir("readonly", 0555)); // Read-only directory
150 cl_assert_equal_i(0, chdir("readonly"));
151 cl_git_pass(git_repository_init(&repo, "../symlink.git", true));
152 cl_assert_equal_i(0, chdir(cwd));
153 cl_assert_equal_i(0, p_mkdir("symlink", 0777));
154 cl_git_pass(git_repository_set_workdir(repo, "symlink", 1));
155
156 cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
157 cl_git_pass(git_remote_fetch(origin, NULL, NULL, NULL));
158 git_remote_free(origin);
159
160 cl_git_pass(git_revparse_single(&target, repo, "remotes/origin/master"));
161 cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL));
162 git_object_free(target);
163 git_repository_free(repo);
164
165 #ifdef GIT_WIN32
166 check_file_contents("./symlink/link_to_new.txt", "new.txt");
167 #else
168 {
169 char link_data[1024];
170 size_t link_size = 1024;
171
172 link_size = p_readlink("./symlink/link_to_new.txt", link_data, link_size);
173 link_data[link_size] = '\0';
174 cl_assert_equal_i(link_size, strlen("new.txt"));
175 cl_assert_equal_s(link_data, "new.txt");
176 check_file_contents("./symlink/link_to_new.txt", "my new file\n");
177 }
178 #endif
179
180 cl_fixture_cleanup("symlink");
181 }
182
183 void test_checkout_index__honor_coresymlinks_setting_set_to_true(void)
184 {
185 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
186
187 cl_repo_set_bool(g_repo, "core.symlinks", true);
188
189 opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
190
191 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
192
193 #ifdef GIT_WIN32
194 check_file_contents("./testrepo/link_to_new.txt", "new.txt");
195 #else
196 {
197 char link_data[1024];
198 size_t link_size = 1024;
199
200 link_size = p_readlink("./testrepo/link_to_new.txt", link_data, link_size);
201 link_data[link_size] = '\0';
202 cl_assert_equal_i(link_size, strlen("new.txt"));
203 cl_assert_equal_s(link_data, "new.txt");
204 check_file_contents("./testrepo/link_to_new.txt", "my new file\n");
205 }
206 #endif
207 }
208
209 void test_checkout_index__honor_coresymlinks_setting_set_to_false(void)
210 {
211 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
212
213 cl_repo_set_bool(g_repo, "core.symlinks", false);
214
215 opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
216
217 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
218
219 check_file_contents("./testrepo/link_to_new.txt", "new.txt");
220 }
221
222 void test_checkout_index__donot_overwrite_modified_file_by_default(void)
223 {
224 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
225
226 cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
227
228 /* set this up to not return an error code on conflicts, but it
229 * still will not have permission to overwrite anything...
230 */
231 opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_ALLOW_CONFLICTS;
232
233 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
234
235 check_file_contents("./testrepo/new.txt", "This isn't what's stored!");
236 }
237
238 void test_checkout_index__can_overwrite_modified_file(void)
239 {
240 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
241
242 cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
243
244 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
245
246 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
247
248 check_file_contents("./testrepo/new.txt", "my new file\n");
249 }
250
251 void test_checkout_index__options_disable_filters(void)
252 {
253 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
254
255 cl_git_mkfile("./testrepo/.gitattributes", "*.txt text eol=crlf\n");
256
257 opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
258 opts.disable_filters = false;
259
260 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
261
262 check_file_contents("./testrepo/new.txt", "my new file\r\n");
263
264 p_unlink("./testrepo/new.txt");
265
266 opts.disable_filters = true;
267 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
268
269 check_file_contents("./testrepo/new.txt", "my new file\n");
270 }
271
272 void test_checkout_index__options_dir_modes(void)
273 {
274 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
275 struct stat st;
276 git_oid oid;
277 git_commit *commit;
278 mode_t um;
279
280 if (!cl_is_chmod_supported())
281 return;
282
283 cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
284 cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));
285
286 reset_index_to_treeish((git_object *)commit);
287
288 opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
289 opts.dir_mode = 0701;
290
291 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
292
293 /* umask will influence actual directory creation mode */
294 (void)p_umask(um = p_umask(022));
295
296 cl_git_pass(p_stat("./testrepo/a", &st));
297 /* Haiku & Hurd use other mode bits, so we must mask them out */
298 cl_assert_equal_i_fmt(st.st_mode & (S_IFMT | 07777), (GIT_FILEMODE_TREE | 0701) & ~um, "%07o");
299
300 /* File-mode test, since we're on the 'dir' branch */
301 cl_git_pass(p_stat("./testrepo/a/b.txt", &st));
302 cl_assert_equal_i_fmt(st.st_mode & (S_IFMT | 07777), GIT_FILEMODE_BLOB_EXECUTABLE & ~um, "%07o");
303
304 git_commit_free(commit);
305 }
306
307 void test_checkout_index__options_override_file_modes(void)
308 {
309 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
310 struct stat st;
311
312 if (!cl_is_chmod_supported())
313 return;
314
315 opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
316 opts.file_mode = 0700;
317
318 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
319
320 cl_git_pass(p_stat("./testrepo/new.txt", &st));
321 cl_assert_equal_i_fmt(st.st_mode & GIT_MODE_PERMS_MASK, 0700, "%07o");
322 }
323
324 void test_checkout_index__options_open_flags(void)
325 {
326 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
327
328 cl_git_mkfile("./testrepo/new.txt", "hi\n");
329
330 opts.checkout_strategy =
331 GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DONT_REMOVE_EXISTING;
332 opts.file_open_flags = O_CREAT | O_RDWR | O_APPEND;
333
334 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
335
336 check_file_contents("./testrepo/new.txt", "hi\nmy new file\n");
337 }
338
339 struct notify_data {
340 const char *file;
341 const char *sha;
342 };
343
344 static int test_checkout_notify_cb(
345 git_checkout_notify_t why,
346 const char *path,
347 const git_diff_file *baseline,
348 const git_diff_file *target,
349 const git_diff_file *workdir,
350 void *payload)
351 {
352 struct notify_data *expectations = (struct notify_data *)payload;
353
354 GIT_UNUSED(workdir);
355
356 cl_assert_equal_i(GIT_CHECKOUT_NOTIFY_CONFLICT, why);
357 cl_assert_equal_s(expectations->file, path);
358 cl_assert_equal_i(0, git_oid_streq(&baseline->id, expectations->sha));
359 cl_assert_equal_i(0, git_oid_streq(&target->id, expectations->sha));
360
361 return 0;
362 }
363
364 void test_checkout_index__can_notify_of_skipped_files(void)
365 {
366 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
367 struct notify_data data;
368
369 cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");
370
371 /*
372 * $ git ls-tree HEAD
373 * 100644 blob a8233120f6ad708f843d861ce2b7228ec4e3dec6 README
374 * 100644 blob 3697d64be941a53d4ae8f6a271e4e3fa56b022cc branch_file.txt
375 * 100644 blob a71586c1dfe8a71c6cbf6c129f404c5642ff31bd new.txt
376 */
377 data.file = "new.txt";
378 data.sha = "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd";
379
380 opts.checkout_strategy = GIT_CHECKOUT_SAFE |
381 GIT_CHECKOUT_RECREATE_MISSING |
382 GIT_CHECKOUT_ALLOW_CONFLICTS;
383 opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
384 opts.notify_cb = test_checkout_notify_cb;
385 opts.notify_payload = &data;
386
387 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
388 }
389
390 static int dont_notify_cb(
391 git_checkout_notify_t why,
392 const char *path,
393 const git_diff_file *baseline,
394 const git_diff_file *target,
395 const git_diff_file *workdir,
396 void *payload)
397 {
398 GIT_UNUSED(why);
399 GIT_UNUSED(path);
400 GIT_UNUSED(baseline);
401 GIT_UNUSED(target);
402 GIT_UNUSED(workdir);
403 GIT_UNUSED(payload);
404
405 cl_assert(false);
406
407 return 0;
408 }
409
410 void test_checkout_index__wont_notify_of_expected_line_ending_changes(void)
411 {
412 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
413
414 cl_git_pass(p_unlink("./testrepo/.gitattributes"));
415 cl_repo_set_bool(g_repo, "core.autocrlf", true);
416
417 cl_git_mkfile("./testrepo/new.txt", "my new file\r\n");
418
419 opts.checkout_strategy =
420 GIT_CHECKOUT_SAFE |
421 GIT_CHECKOUT_RECREATE_MISSING |
422 GIT_CHECKOUT_ALLOW_CONFLICTS;
423 opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
424 opts.notify_cb = dont_notify_cb;
425 opts.notify_payload = NULL;
426
427 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
428 }
429
430 static void checkout_progress_counter(
431 const char *path, size_t cur, size_t tot, void *payload)
432 {
433 GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
434 (*(int *)payload)++;
435 }
436
437 void test_checkout_index__calls_progress_callback(void)
438 {
439 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
440 int calls = 0;
441
442 opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
443 opts.progress_cb = checkout_progress_counter;
444 opts.progress_payload = &calls;
445
446 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
447 cl_assert(calls > 0);
448 }
449
450 void test_checkout_index__can_overcome_name_clashes(void)
451 {
452 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
453 git_index *index;
454
455 cl_git_pass(git_repository_index(&index, g_repo));
456 git_index_clear(index);
457
458 cl_git_mkfile("./testrepo/path0", "content\r\n");
459 cl_git_pass(p_mkdir("./testrepo/path1", 0777));
460 cl_git_mkfile("./testrepo/path1/file1", "content\r\n");
461
462 cl_git_pass(git_index_add_bypath(index, "path0"));
463 cl_git_pass(git_index_add_bypath(index, "path1/file1"));
464
465 cl_git_pass(p_unlink("./testrepo/path0"));
466 cl_git_pass(git_futils_rmdir_r(
467 "./testrepo/path1", NULL, GIT_RMDIR_REMOVE_FILES));
468
469 cl_git_mkfile("./testrepo/path1", "content\r\n");
470 cl_git_pass(p_mkdir("./testrepo/path0", 0777));
471 cl_git_mkfile("./testrepo/path0/file0", "content\r\n");
472
473 cl_assert(git_path_isfile("./testrepo/path1"));
474 cl_assert(git_path_isfile("./testrepo/path0/file0"));
475
476 opts.checkout_strategy =
477 GIT_CHECKOUT_SAFE |
478 GIT_CHECKOUT_RECREATE_MISSING |
479 GIT_CHECKOUT_ALLOW_CONFLICTS;
480 cl_git_pass(git_checkout_index(g_repo, index, &opts));
481
482 cl_assert(git_path_isfile("./testrepo/path1"));
483 cl_assert(git_path_isfile("./testrepo/path0/file0"));
484
485 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
486 cl_git_pass(git_checkout_index(g_repo, index, &opts));
487
488 cl_assert(git_path_isfile("./testrepo/path0"));
489 cl_assert(git_path_isfile("./testrepo/path1/file1"));
490
491 git_index_free(index);
492 }
493
494 void test_checkout_index__validates_struct_version(void)
495 {
496 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
497 const git_error *err;
498
499 opts.version = 1024;
500 cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
501
502 err = giterr_last();
503 cl_assert_equal_i(err->klass, GITERR_INVALID);
504
505 opts.version = 0;
506 giterr_clear();
507 cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
508
509 err = giterr_last();
510 cl_assert_equal_i(err->klass, GITERR_INVALID);
511 }
512
513 void test_checkout_index__can_update_prefixed_files(void)
514 {
515 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
516
517 cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
518 cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
519 cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));
520
521 cl_git_mkfile("./testrepo/READ", "content\n");
522 cl_git_mkfile("./testrepo/README.after", "content\n");
523 cl_git_pass(p_mkdir("./testrepo/branch_file", 0777));
524 cl_git_pass(p_mkdir("./testrepo/branch_file/contained_dir", 0777));
525 cl_git_mkfile("./testrepo/branch_file/contained_file", "content\n");
526 cl_git_pass(p_mkdir("./testrepo/branch_file.txt.after", 0777));
527
528 opts.checkout_strategy =
529 GIT_CHECKOUT_SAFE |
530 GIT_CHECKOUT_RECREATE_MISSING |
531 GIT_CHECKOUT_REMOVE_UNTRACKED;
532
533 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
534
535 /* remove untracked will remove the .gitattributes file before the blobs
536 * were created, so they will have had crlf filtering applied on Windows
537 */
538 check_file_contents_nocr("./testrepo/README", "hey there\n");
539 check_file_contents_nocr("./testrepo/branch_file.txt", "hi\nbye!\n");
540 check_file_contents_nocr("./testrepo/new.txt", "my new file\n");
541
542 cl_assert(!git_path_exists("testrepo/READ"));
543 cl_assert(!git_path_exists("testrepo/README.after"));
544 cl_assert(!git_path_exists("testrepo/branch_file"));
545 cl_assert(!git_path_exists("testrepo/branch_file.txt.after"));
546 }
547
548 void test_checkout_index__can_checkout_a_newly_initialized_repository(void)
549 {
550 test_checkout_index__cleanup();
551
552 g_repo = cl_git_sandbox_init("empty_standard_repo");
553 cl_git_remove_placeholders(git_repository_path(g_repo), "dummy-marker.txt");
554
555 cl_git_pass(git_checkout_index(g_repo, NULL, NULL));
556 }
557
558 void test_checkout_index__issue_1397(void)
559 {
560 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
561
562 test_checkout_index__cleanup();
563
564 g_repo = cl_git_sandbox_init("issue_1397");
565
566 cl_repo_set_bool(g_repo, "core.autocrlf", true);
567
568 opts.checkout_strategy = GIT_CHECKOUT_FORCE;
569
570 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
571
572 check_file_contents("./issue_1397/crlf_file.txt", "first line\r\nsecond line\r\nboth with crlf");
573 }
574
575 void test_checkout_index__target_directory(void)
576 {
577 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
578 checkout_counts cts;
579 memset(&cts, 0, sizeof(cts));
580
581 opts.checkout_strategy = GIT_CHECKOUT_SAFE |
582 GIT_CHECKOUT_RECREATE_MISSING;
583 opts.target_directory = "alternative";
584 cl_assert(!git_path_isdir("alternative"));
585
586 opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
587 opts.notify_cb = checkout_count_callback;
588 opts.notify_payload = &cts;
589
590 /* create some files that *would* conflict if we were using the wd */
591 cl_git_mkfile("testrepo/README", "I'm in the way!\n");
592 cl_git_mkfile("testrepo/new.txt", "my new file\n");
593
594 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
595
596 cl_assert_equal_i(0, cts.n_untracked);
597 cl_assert_equal_i(0, cts.n_ignored);
598 cl_assert_equal_i(4, cts.n_updates);
599
600 check_file_contents("./alternative/README", "hey there\n");
601 check_file_contents("./alternative/branch_file.txt", "hi\nbye!\n");
602 check_file_contents("./alternative/new.txt", "my new file\n");
603
604 cl_git_pass(git_futils_rmdir_r(
605 "alternative", NULL, GIT_RMDIR_REMOVE_FILES));
606 }
607
608 void test_checkout_index__target_directory_from_bare(void)
609 {
610 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
611 git_index *index;
612 git_object *head = NULL;
613 checkout_counts cts;
614 memset(&cts, 0, sizeof(cts));
615
616 test_checkout_index__cleanup();
617
618 g_repo = cl_git_sandbox_init("testrepo.git");
619 cl_assert(git_repository_is_bare(g_repo));
620
621 cl_git_pass(git_repository_index(&index, g_repo));
622 cl_git_pass(git_revparse_single(&head, g_repo, "HEAD^{tree}"));
623 cl_git_pass(git_index_read_tree(index, (const git_tree *)head));
624 cl_git_pass(git_index_write(index));
625 git_index_free(index);
626
627 opts.checkout_strategy = GIT_CHECKOUT_SAFE |
628 GIT_CHECKOUT_RECREATE_MISSING;
629
630 opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
631 opts.notify_cb = checkout_count_callback;
632 opts.notify_payload = &cts;
633
634 /* fail to checkout a bare repo */
635 cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
636
637 opts.target_directory = "alternative";
638 cl_assert(!git_path_isdir("alternative"));
639
640 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
641
642 cl_assert_equal_i(0, cts.n_untracked);
643 cl_assert_equal_i(0, cts.n_ignored);
644 cl_assert_equal_i(3, cts.n_updates);
645
646 /* files will have been filtered if needed, so strip CR */
647 check_file_contents_nocr("./alternative/README", "hey there\n");
648 check_file_contents_nocr("./alternative/branch_file.txt", "hi\nbye!\n");
649 check_file_contents_nocr("./alternative/new.txt", "my new file\n");
650
651 cl_git_pass(git_futils_rmdir_r(
652 "alternative", NULL, GIT_RMDIR_REMOVE_FILES));
653
654 git_object_free(head);
655 }
656
657 void test_checkout_index__can_get_repo_from_index(void)
658 {
659 git_index *index;
660 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
661
662 cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
663 cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
664 cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));
665
666 opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
667
668 cl_git_pass(git_repository_index(&index, g_repo));
669
670 cl_git_pass(git_checkout_index(NULL, index, &opts));
671
672 check_file_contents("./testrepo/README", "hey there\n");
673 check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
674 check_file_contents("./testrepo/new.txt", "my new file\n");
675
676 git_index_free(index);
677 }
678
679 static void add_conflict(git_index *index, const char *path)
680 {
681 git_index_entry entry;
682
683 memset(&entry, 0, sizeof(git_index_entry));
684
685 entry.mode = 0100644;
686 entry.path = path;
687
688 git_oid_fromstr(&entry.id, "d427e0b2e138501a3d15cc376077a3631e15bd46");
689 GIT_IDXENTRY_STAGE_SET(&entry, 1);
690 cl_git_pass(git_index_add(index, &entry));
691
692 git_oid_fromstr(&entry.id, "4e886e602529caa9ab11d71f86634bd1b6e0de10");
693 GIT_IDXENTRY_STAGE_SET(&entry, 2);
694 cl_git_pass(git_index_add(index, &entry));
695
696 git_oid_fromstr(&entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863");
697 GIT_IDXENTRY_STAGE_SET(&entry, 3);
698 cl_git_pass(git_index_add(index, &entry));
699 }
700
701 void test_checkout_index__writes_conflict_file(void)
702 {
703 git_index *index;
704 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
705 git_buf conflicting_buf = GIT_BUF_INIT;
706
707 cl_git_pass(git_repository_index(&index, g_repo));
708
709 add_conflict(index, "conflicting.txt");
710 cl_git_pass(git_index_write(index));
711
712 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
713
714 cl_git_pass(git_futils_readbuffer(&conflicting_buf, "testrepo/conflicting.txt"));
715 cl_assert(strcmp(conflicting_buf.ptr,
716 "<<<<<<< ours\n"
717 "this file is changed in master and branch\n"
718 "=======\n"
719 "this file is changed in branch and master\n"
720 ">>>>>>> theirs\n") == 0);
721 git_buf_free(&conflicting_buf);
722
723 git_index_free(index);
724 }
725
726 void test_checkout_index__adding_conflict_removes_stage_0(void)
727 {
728 git_index *new_index, *index;
729 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
730
731 cl_git_pass(git_index_new(&new_index));
732
733 add_conflict(new_index, "new.txt");
734 cl_git_pass(git_checkout_index(g_repo, new_index, &opts));
735
736 cl_git_pass(git_repository_index(&index, g_repo));
737
738 cl_assert(git_index_get_bypath(index, "new.txt", 0) == NULL);
739 cl_assert(git_index_get_bypath(index, "new.txt", 1) != NULL);
740 cl_assert(git_index_get_bypath(index, "new.txt", 2) != NULL);
741 cl_assert(git_index_get_bypath(index, "new.txt", 3) != NULL);
742
743 git_index_free(index);
744 git_index_free(new_index);
745 }
746
747 void test_checkout_index__conflicts_honor_coreautocrlf(void)
748 {
749 #ifdef GIT_WIN32
750 git_index *index;
751 git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
752 git_buf conflicting_buf = GIT_BUF_INIT;
753
754 cl_git_pass(p_unlink("./testrepo/.gitattributes"));
755 cl_repo_set_bool(g_repo, "core.autocrlf", true);
756
757 cl_git_pass(git_repository_index(&index, g_repo));
758
759 add_conflict(index, "conflicting.txt");
760 cl_git_pass(git_index_write(index));
761
762 cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
763
764 cl_git_pass(git_futils_readbuffer(&conflicting_buf, "testrepo/conflicting.txt"));
765 cl_assert(strcmp(conflicting_buf.ptr,
766 "<<<<<<< ours\r\n"
767 "this file is changed in master and branch\r\n"
768 "=======\r\n"
769 "this file is changed in branch and master\r\n"
770 ">>>>>>> theirs\r\n") == 0);
771 git_buf_free(&conflicting_buf);
772
773 git_index_free(index);
774 #endif
775 }