index.c 22.6 KB
Newer Older
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 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29

static git_repository *g_repo;

void test_checkout_index__initialize(void)
{
	git_tree *tree;

	g_repo = cl_git_sandbox_init("testrepo");

	cl_git_pass(git_repository_head_tree(&tree, g_repo));

	reset_index_to_treeish((git_object *)tree);
	git_tree_free(tree);

	cl_git_rewritefile(
		"./testrepo/.gitattributes",
		"* text eol=lf\n");
}

void test_checkout_index__cleanup(void)
{
	cl_git_sandbox_cleanup();
30 31 32 33

	/* try to remove alternative dir */
	if (git_path_isdir("alternative"))
		git_futils_rmdir_r("alternative", NULL, GIT_RMDIR_REMOVE_FILES);
34 35 36 37 38 39 40 41
}

void test_checkout_index__cannot_checkout_a_bare_repository(void)
{
	test_checkout_index__cleanup();

	g_repo = cl_git_sandbox_init("testrepo.git");

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 50 51 52
	cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
	cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
	cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));

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 58 59
	check_file_contents("./testrepo/README", "hey there\n");
	check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
	check_file_contents("./testrepo/new.txt", "my new file\n");
60 61
}

62 63
void test_checkout_index__can_remove_untracked_files(void)
{
64
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
65

66 67 68 69 70 71
	git_futils_mkdir("./testrepo/dir/subdir/subsubdir", NULL, 0755, GIT_MKDIR_PATH);
	cl_git_mkfile("./testrepo/dir/one", "one\n");
	cl_git_mkfile("./testrepo/dir/subdir/two", "two\n");

	cl_assert_equal_i(true, git_path_isdir("./testrepo/dir/subdir/subsubdir"));

72
	opts.checkout_strategy =
73 74 75
		GIT_CHECKOUT_SAFE |
		GIT_CHECKOUT_RECREATE_MISSING |
		GIT_CHECKOUT_REMOVE_UNTRACKED;
76

77
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
78 79 80 81

	cl_assert_equal_i(false, git_path_isdir("./testrepo/dir"));
}

82 83
void test_checkout_index__honor_the_specified_pathspecs(void)
{
84
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
85 86
	char *entries[] = { "*.txt" };

87 88
	opts.paths.strings = entries;
	opts.paths.count = 1;
89 90 91 92 93

	cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
	cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
	cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));

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 100
	check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
	check_file_contents("./testrepo/new.txt", "my new file\n");
101 102 103 104
}

void test_checkout_index__honor_the_gitattributes_directives(void)
{
105
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
106 107 108 109 110
	const char *attributes =
		"branch_file.txt text eol=crlf\n"
		"new.txt text eol=lf\n";

	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 118 119
	check_file_contents("./testrepo/README", "hey there\n");
	check_file_contents("./testrepo/new.txt", "my new file\n");
	check_file_contents("./testrepo/branch_file.txt", "hi\r\nbye!\r\n");
120 121 122 123 124
}

void test_checkout_index__honor_coreautocrlf_setting_set_to_true(void)
{
#ifdef GIT_WIN32
125
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
126 127 128
	const char *expected_readme_text = "hey there\r\n";

	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 137 138
#endif
}

139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
void test_checkout_index__honor_coresymlinks_default(void)
{
	git_repository *repo;
	git_remote *origin;
	git_object *target;
	char cwd[GIT_PATH_MAX];

	const char *url = git_repository_path(g_repo);

	getcwd(cwd, sizeof(cwd));
	cl_assert_equal_i(0, p_mkdir("readonly", 0555)); // Read-only directory
	cl_assert_equal_i(0, chdir("readonly"));
	cl_git_pass(git_repository_init(&repo, "../symlink.git", true));
	cl_assert_equal_i(0, chdir(cwd));
	cl_assert_equal_i(0, p_mkdir("symlink", 0777));
	cl_git_pass(git_repository_set_workdir(repo, "symlink", 1));

	cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
	cl_git_pass(git_remote_connect(origin, GIT_DIRECTION_FETCH));
	cl_git_pass(git_remote_download(origin, NULL));
	cl_git_pass(git_remote_update_tips(origin, NULL));
	git_remote_free(origin);

	cl_git_pass(git_revparse_single(&target, repo, "remotes/origin/master"));
	cl_git_pass(git_reset(repo, target, GIT_RESET_HARD, NULL));
	git_object_free(target);
	git_repository_free(repo);

#ifdef GIT_WIN32
	check_file_contents("./symlink/link_to_new.txt", "new.txt");
#else
	{
		char link_data[1024];
		size_t link_size = 1024;

		link_size = p_readlink("./symlink/link_to_new.txt", link_data, link_size);
		link_data[link_size] = '\0';
		cl_assert_equal_i(link_size, strlen("new.txt"));
		cl_assert_equal_s(link_data, "new.txt");
		check_file_contents("./symlink/link_to_new.txt", "my new file\n");
	}
#endif

	cl_fixture_cleanup("symlink");
}

185 186
void test_checkout_index__honor_coresymlinks_setting_set_to_true(void)
{
187
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
188

189
	cl_repo_set_bool(g_repo, "core.symlinks", true);
190

191
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
192

193
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
194 195

#ifdef GIT_WIN32
196
	check_file_contents("./testrepo/link_to_new.txt", "new.txt");
197 198 199 200 201 202 203 204 205
#else
	{
		char link_data[1024];
		size_t link_size = 1024;

		link_size = p_readlink("./testrepo/link_to_new.txt", link_data, link_size);
		link_data[link_size] = '\0';
		cl_assert_equal_i(link_size, strlen("new.txt"));
		cl_assert_equal_s(link_data, "new.txt");
206
		check_file_contents("./testrepo/link_to_new.txt", "my new file\n");
207 208 209 210 211 212
	}
#endif
}

void test_checkout_index__honor_coresymlinks_setting_set_to_false(void)
{
213
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
214

215
	cl_repo_set_bool(g_repo, "core.symlinks", false);
216

217
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
218

219
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
220

221
	check_file_contents("./testrepo/link_to_new.txt", "new.txt");
222 223
}

224
void test_checkout_index__donot_overwrite_modified_file_by_default(void)
225
{
226
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
227

228 229
	cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");

230 231 232
	/* set this up to not return an error code on conflicts, but it
	 * still will not have permission to overwrite anything...
	 */
233
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_ALLOW_CONFLICTS;
234

235
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
236

237
	check_file_contents("./testrepo/new.txt", "This isn't what's stored!");
238 239
}

240
void test_checkout_index__can_overwrite_modified_file(void)
241
{
242
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
243

244 245
	cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");

246
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
247

248
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
249

250
	check_file_contents("./testrepo/new.txt", "my new file\n");
251 252 253 254
}

void test_checkout_index__options_disable_filters(void)
{
255
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
256

257 258
	cl_git_mkfile("./testrepo/.gitattributes", "*.txt text eol=crlf\n");

259
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
260
	opts.disable_filters = false;
261

262
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
263

264
	check_file_contents("./testrepo/new.txt", "my new file\r\n");
265 266 267

	p_unlink("./testrepo/new.txt");

268 269
	opts.disable_filters = true;
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
270

271
	check_file_contents("./testrepo/new.txt", "my new file\n");
272 273 274 275
}

void test_checkout_index__options_dir_modes(void)
{
276
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
277 278 279
	struct stat st;
	git_oid oid;
	git_commit *commit;
280
	mode_t um;
281

282 283 284
	if (!cl_is_chmod_supported())
		return;

285
	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
286 287 288 289
	cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));

	reset_index_to_treeish((git_object *)commit);

290
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
291
	opts.dir_mode = 0701;
292

293
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
294

295 296 297
	/* umask will influence actual directory creation mode */
	(void)p_umask(um = p_umask(022));

298
	cl_git_pass(p_stat("./testrepo/a", &st));
299
	cl_assert_equal_i_fmt(st.st_mode, (GIT_FILEMODE_TREE | 0701) & ~um, "%07o");
300 301 302

	/* File-mode test, since we're on the 'dir' branch */
	cl_git_pass(p_stat("./testrepo/a/b.txt", &st));
303
	cl_assert_equal_i_fmt(st.st_mode, GIT_FILEMODE_BLOB_EXECUTABLE, "%07o");
304 305 306 307 308 309

	git_commit_free(commit);
}

void test_checkout_index__options_override_file_modes(void)
{
310
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
311 312
	struct stat st;

313 314 315
	if (!cl_is_chmod_supported())
		return;

316
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
317
	opts.file_mode = 0700;
318

319
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
320 321

	cl_git_pass(p_stat("./testrepo/new.txt", &st));
Russell Belfer committed
322
	cl_assert_equal_i_fmt(st.st_mode & GIT_MODE_PERMS_MASK, 0700, "%07o");
323 324 325 326
}

void test_checkout_index__options_open_flags(void)
{
327
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
328

329 330
	cl_git_mkfile("./testrepo/new.txt", "hi\n");

331 332
	opts.checkout_strategy =
		GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DONT_REMOVE_EXISTING;
333
	opts.file_open_flags = O_CREAT | O_RDWR | O_APPEND;
334

335
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
336

337
	check_file_contents("./testrepo/new.txt", "hi\nmy new file\n");
338
}
339

340
struct notify_data {
341 342 343 344
	const char *file;
	const char *sha;
};

345 346 347 348 349 350
static int test_checkout_notify_cb(
	git_checkout_notify_t why,
	const char *path,
	const git_diff_file *baseline,
	const git_diff_file *target,
	const git_diff_file *workdir,
351 352
	void *payload)
{
353
	struct notify_data *expectations = (struct notify_data *)payload;
354

355
	GIT_UNUSED(workdir);
356

357 358
	cl_assert_equal_i(GIT_CHECKOUT_NOTIFY_CONFLICT, why);
	cl_assert_equal_s(expectations->file, path);
359 360
	cl_assert_equal_i(0, git_oid_streq(&baseline->id, expectations->sha));
	cl_assert_equal_i(0, git_oid_streq(&target->id, expectations->sha));
361 362 363 364 365 366

	return 0;
}

void test_checkout_index__can_notify_of_skipped_files(void)
{
367
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
368
	struct notify_data data;
369 370 371 372 373 374 375 376 377 378 379 380

	cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");

	/*
	 * $ git ls-tree HEAD
	 * 100644 blob a8233120f6ad708f843d861ce2b7228ec4e3dec6    README
	 * 100644 blob 3697d64be941a53d4ae8f6a271e4e3fa56b022cc    branch_file.txt
	 * 100644 blob a71586c1dfe8a71c6cbf6c129f404c5642ff31bd    new.txt
	 */
	data.file = "new.txt";
	data.sha = "a71586c1dfe8a71c6cbf6c129f404c5642ff31bd";

381 382 383
	opts.checkout_strategy = GIT_CHECKOUT_SAFE |
		GIT_CHECKOUT_RECREATE_MISSING |
		GIT_CHECKOUT_ALLOW_CONFLICTS;
384 385 386
	opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
	opts.notify_cb = test_checkout_notify_cb;
	opts.notify_payload = &data;
387

388
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
389 390
}

391
static int dont_notify_cb(
392 393 394 395 396
	git_checkout_notify_t why,
	const char *path,
	const git_diff_file *baseline,
	const git_diff_file *target,
	const git_diff_file *workdir,
397 398
	void *payload)
{
399 400 401 402 403
	GIT_UNUSED(why);
	GIT_UNUSED(path);
	GIT_UNUSED(baseline);
	GIT_UNUSED(target);
	GIT_UNUSED(workdir);
404 405 406 407 408 409 410 411 412
	GIT_UNUSED(payload);

	cl_assert(false);

	return 0;
}

void test_checkout_index__wont_notify_of_expected_line_ending_changes(void)
{
413
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
414

415
	cl_git_pass(p_unlink("./testrepo/.gitattributes"));
416
	cl_repo_set_bool(g_repo, "core.autocrlf", true);
417

418 419
	cl_git_mkfile("./testrepo/new.txt", "my new file\r\n");

420
	opts.checkout_strategy =
421 422 423
		GIT_CHECKOUT_SAFE |
		GIT_CHECKOUT_RECREATE_MISSING |
		GIT_CHECKOUT_ALLOW_CONFLICTS;
424 425 426
	opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
	opts.notify_cb = dont_notify_cb;
	opts.notify_payload = NULL;
427

428
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
429 430
}

431 432
static void checkout_progress_counter(
	const char *path, size_t cur, size_t tot, void *payload)
433
{
Ben Straub committed
434
	GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
435
	(*(int *)payload)++;
436 437 438 439
}

void test_checkout_index__calls_progress_callback(void)
{
440
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
441 442
	int calls = 0;

443
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
444 445
	opts.progress_cb = checkout_progress_counter;
	opts.progress_payload = &calls;
446

447
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
448
	cl_assert(calls > 0);
449
}
450 451 452

void test_checkout_index__can_overcome_name_clashes(void)
{
453
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
454 455 456 457 458 459 460 461 462
	git_index *index;

	cl_git_pass(git_repository_index(&index, g_repo));
	git_index_clear(index);

	cl_git_mkfile("./testrepo/path0", "content\r\n");
	cl_git_pass(p_mkdir("./testrepo/path1", 0777));
	cl_git_mkfile("./testrepo/path1/file1", "content\r\n");

463 464
	cl_git_pass(git_index_add_bypath(index, "path0"));
	cl_git_pass(git_index_add_bypath(index, "path1/file1"));
465

466 467 468 469 470 471 472 473
	cl_git_pass(p_unlink("./testrepo/path0"));
	cl_git_pass(git_futils_rmdir_r(
		"./testrepo/path1", NULL, GIT_RMDIR_REMOVE_FILES));

	cl_git_mkfile("./testrepo/path1", "content\r\n");
	cl_git_pass(p_mkdir("./testrepo/path0", 0777));
	cl_git_mkfile("./testrepo/path0/file0", "content\r\n");

474 475 476
	cl_assert(git_path_isfile("./testrepo/path1"));
	cl_assert(git_path_isfile("./testrepo/path0/file0"));

477
	opts.checkout_strategy =
478 479 480
		GIT_CHECKOUT_SAFE | 
		GIT_CHECKOUT_RECREATE_MISSING |
		GIT_CHECKOUT_ALLOW_CONFLICTS;
481
	cl_git_pass(git_checkout_index(g_repo, index, &opts));
482 483 484 485

	cl_assert(git_path_isfile("./testrepo/path1"));
	cl_assert(git_path_isfile("./testrepo/path0/file0"));

486 487
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
	cl_git_pass(git_checkout_index(g_repo, index, &opts));
488 489 490 491

	cl_assert(git_path_isfile("./testrepo/path0"));
	cl_assert(git_path_isfile("./testrepo/path1/file1"));

492 493
	git_index_free(index);
}
494 495 496

void test_checkout_index__validates_struct_version(void)
{
497
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
498 499
	const git_error *err;

500 501
	opts.version = 1024;
	cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
502 503 504 505

	err = giterr_last();
	cl_assert_equal_i(err->klass, GITERR_INVALID);

506
	opts.version = 0;
507
	giterr_clear();
508
	cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
509 510 511 512

	err = giterr_last();
	cl_assert_equal_i(err->klass, GITERR_INVALID);
}
513 514 515

void test_checkout_index__can_update_prefixed_files(void)
{
516
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
517

518 519 520 521
	cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
	cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
	cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));

522 523 524 525 526 527 528
	cl_git_mkfile("./testrepo/READ", "content\n");
	cl_git_mkfile("./testrepo/README.after", "content\n");
	cl_git_pass(p_mkdir("./testrepo/branch_file", 0777));
	cl_git_pass(p_mkdir("./testrepo/branch_file/contained_dir", 0777));
	cl_git_mkfile("./testrepo/branch_file/contained_file", "content\n");
	cl_git_pass(p_mkdir("./testrepo/branch_file.txt.after", 0777));

529
	opts.checkout_strategy =
530 531 532
		GIT_CHECKOUT_SAFE |
		GIT_CHECKOUT_RECREATE_MISSING |
		GIT_CHECKOUT_REMOVE_UNTRACKED;
533 534 535

	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));

536 537 538
	/* remove untracked will remove the .gitattributes file before the blobs
	 * were created, so they will have had crlf filtering applied on Windows
	 */
539 540 541
	check_file_contents_nocr("./testrepo/README", "hey there\n");
	check_file_contents_nocr("./testrepo/branch_file.txt", "hi\nbye!\n");
	check_file_contents_nocr("./testrepo/new.txt", "my new file\n");
542 543 544 545 546 547

	cl_assert(!git_path_exists("testrepo/READ"));
	cl_assert(!git_path_exists("testrepo/README.after"));
	cl_assert(!git_path_exists("testrepo/branch_file"));
	cl_assert(!git_path_exists("testrepo/branch_file.txt.after"));
}
548 549 550 551 552 553 554 555 556 557

void test_checkout_index__can_checkout_a_newly_initialized_repository(void)
{
	test_checkout_index__cleanup();

	g_repo = cl_git_sandbox_init("empty_standard_repo");
	cl_git_remove_placeholders(git_repository_path(g_repo), "dummy-marker.txt");

	cl_git_pass(git_checkout_index(g_repo, NULL, NULL));
}
558 559 560

void test_checkout_index__issue_1397(void)
{
561
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
562 563 564 565 566

	test_checkout_index__cleanup();

	g_repo = cl_git_sandbox_init("issue_1397");

Russell Belfer committed
567
	cl_repo_set_bool(g_repo, "core.autocrlf", true);
568 569 570 571 572

	opts.checkout_strategy = GIT_CHECKOUT_FORCE;

	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));

573
	check_file_contents("./issue_1397/crlf_file.txt", "first line\r\nsecond line\r\nboth with crlf");
574
}
575 576 577

void test_checkout_index__target_directory(void)
{
578
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
579 580 581
	checkout_counts cts;
	memset(&cts, 0, sizeof(cts));

582 583
	opts.checkout_strategy = GIT_CHECKOUT_SAFE |
		GIT_CHECKOUT_RECREATE_MISSING;
584
	opts.target_directory = "alternative";
585
	cl_assert(!git_path_isdir("alternative"));
586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603

	opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
	opts.notify_cb = checkout_count_callback;
	opts.notify_payload = &cts;

	/* create some files that *would* conflict if we were using the wd */
	cl_git_mkfile("testrepo/README", "I'm in the way!\n");
	cl_git_mkfile("testrepo/new.txt", "my new file\n");

	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));

	cl_assert_equal_i(0, cts.n_untracked);
	cl_assert_equal_i(0, cts.n_ignored);
	cl_assert_equal_i(4, cts.n_updates);

	check_file_contents("./alternative/README", "hey there\n");
	check_file_contents("./alternative/branch_file.txt", "hi\nbye!\n");
	check_file_contents("./alternative/new.txt", "my new file\n");
604 605 606 607 608 609 610

	cl_git_pass(git_futils_rmdir_r(
		"alternative", NULL, GIT_RMDIR_REMOVE_FILES));
}

void test_checkout_index__target_directory_from_bare(void)
{
611
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627
	git_index *index;
	git_object *head = NULL;
	checkout_counts cts;
	memset(&cts, 0, sizeof(cts));

	test_checkout_index__cleanup();

	g_repo = cl_git_sandbox_init("testrepo.git");
	cl_assert(git_repository_is_bare(g_repo));

	cl_git_pass(git_repository_index(&index, g_repo));
	cl_git_pass(git_revparse_single(&head, g_repo, "HEAD^{tree}"));
	cl_git_pass(git_index_read_tree(index, (const git_tree *)head));
	cl_git_pass(git_index_write(index));
	git_index_free(index);

628 629
	opts.checkout_strategy = GIT_CHECKOUT_SAFE |
		GIT_CHECKOUT_RECREATE_MISSING;
630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646

	opts.notify_flags = GIT_CHECKOUT_NOTIFY_ALL;
	opts.notify_cb = checkout_count_callback;
	opts.notify_payload = &cts;

	/* fail to checkout a bare repo */
	cl_git_fail(git_checkout_index(g_repo, NULL, &opts));

	opts.target_directory = "alternative";
	cl_assert(!git_path_isdir("alternative"));

	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));

	cl_assert_equal_i(0, cts.n_untracked);
	cl_assert_equal_i(0, cts.n_ignored);
	cl_assert_equal_i(3, cts.n_updates);

647 648 649 650
	/* files will have been filtered if needed, so strip CR */
	check_file_contents_nocr("./alternative/README", "hey there\n");
	check_file_contents_nocr("./alternative/branch_file.txt", "hi\nbye!\n");
	check_file_contents_nocr("./alternative/new.txt", "my new file\n");
651 652 653

	cl_git_pass(git_futils_rmdir_r(
		"alternative", NULL, GIT_RMDIR_REMOVE_FILES));
nulltoken committed
654 655

	git_object_free(head);
656 657 658 659 660
}

void test_checkout_index__can_get_repo_from_index(void)
{
	git_index *index;
661
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
662 663 664 665 666

	cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
	cl_assert_equal_i(false, git_path_isfile("./testrepo/branch_file.txt"));
	cl_assert_equal_i(false, git_path_isfile("./testrepo/new.txt"));

667
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
668 669 670 671 672 673 674 675 676 677

	cl_git_pass(git_repository_index(&index, g_repo));

	cl_git_pass(git_checkout_index(NULL, index, &opts));

	check_file_contents("./testrepo/README", "hey there\n");
	check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
	check_file_contents("./testrepo/new.txt", "my new file\n");

	git_index_free(index);
678
}
679

680
static void add_conflict(git_index *index, const char *path)
681 682 683 684 685 686
{
	git_index_entry entry;

	memset(&entry, 0, sizeof(git_index_entry));

	entry.mode = 0100644;
687
	entry.path = path;
688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703

	git_oid_fromstr(&entry.id, "d427e0b2e138501a3d15cc376077a3631e15bd46");
	entry.flags = (1 << GIT_IDXENTRY_STAGESHIFT);
	cl_git_pass(git_index_add(index, &entry));

	git_oid_fromstr(&entry.id, "4e886e602529caa9ab11d71f86634bd1b6e0de10");
	entry.flags = (2 << GIT_IDXENTRY_STAGESHIFT);
	cl_git_pass(git_index_add(index, &entry));

	git_oid_fromstr(&entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863");
	entry.flags = (3 << GIT_IDXENTRY_STAGESHIFT);
	cl_git_pass(git_index_add(index, &entry));
}

void test_checkout_index__writes_conflict_file(void)
{
704
	git_index *index;
705 706 707
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
	git_buf conflicting_buf = GIT_BUF_INIT;

708 709 710 711 712
	cl_git_pass(git_repository_index(&index, g_repo));

	add_conflict(index, "conflicting.txt");
	cl_git_pass(git_index_write(index));

713 714 715 716 717 718 719 720 721 722
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));

	cl_git_pass(git_futils_readbuffer(&conflicting_buf, "testrepo/conflicting.txt"));
	cl_assert(strcmp(conflicting_buf.ptr,
		"<<<<<<< ours\n"
		"this file is changed in master and branch\n"
		"=======\n"
		"this file is changed in branch and master\n"
		">>>>>>> theirs\n") == 0);
	git_buf_free(&conflicting_buf);
723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745

	git_index_free(index);
}

void test_checkout_index__adding_conflict_removes_stage_0(void)
{
	git_index *new_index, *index;
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;

	cl_git_pass(git_index_new(&new_index));

	add_conflict(new_index, "new.txt");
	cl_git_pass(git_checkout_index(g_repo, new_index, &opts));

	cl_git_pass(git_repository_index(&index, g_repo));

	cl_assert(git_index_get_bypath(index, "new.txt", 0) == NULL);
	cl_assert(git_index_get_bypath(index, "new.txt", 1) != NULL);
	cl_assert(git_index_get_bypath(index, "new.txt", 2) != NULL);
	cl_assert(git_index_get_bypath(index, "new.txt", 3) != NULL);

	git_index_free(index);
	git_index_free(new_index);
746 747 748 749 750
}

void test_checkout_index__conflicts_honor_coreautocrlf(void)
{
#ifdef GIT_WIN32
751
	git_index *index;
752 753 754 755 756 757
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
	git_buf conflicting_buf = GIT_BUF_INIT;

	cl_git_pass(p_unlink("./testrepo/.gitattributes"));
	cl_repo_set_bool(g_repo, "core.autocrlf", true);

758 759 760 761 762
	cl_git_pass(git_repository_index(&index, g_repo));

	add_conflict(index, "conflicting.txt");
	cl_git_pass(git_index_write(index));

763 764 765 766 767 768 769 770 771 772
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));

	cl_git_pass(git_futils_readbuffer(&conflicting_buf, "testrepo/conflicting.txt"));
	cl_assert(strcmp(conflicting_buf.ptr,
		"<<<<<<< ours\r\n"
		"this file is changed in master and branch\r\n"
		"=======\r\n"
		"this file is changed in branch and master\r\n"
		">>>>>>> theirs\r\n") == 0);
	git_buf_free(&conflicting_buf);
773 774

	git_index_free(index);
775 776
#endif
}