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