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
	git_futils_mkdir("./testrepo/dir/subdir/subsubdir", 0755, GIT_MKDIR_PATH);
67 68 69 70 71
	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
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);

148
	cl_assert(getcwd(cwd, sizeof(cwd)) != NULL);
149 150 151 152 153 154 155 156
	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));
157
	cl_git_pass(git_remote_fetch(origin, NULL, NULL, NULL));
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
	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");
}

183 184
void test_checkout_index__honor_coresymlinks_setting_set_to_true(void)
{
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 196 197 198 199 200 201 202 203
#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");
204
		check_file_contents("./testrepo/link_to_new.txt", "my new file\n");
205 206 207 208 209 210
	}
#endif
}

void test_checkout_index__honor_coresymlinks_setting_set_to_false(void)
{
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 227
	cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");

228 229 230
	/* set this up to not return an error code on conflicts, but it
	 * still will not have permission to overwrite anything...
	 */
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 243
	cl_git_mkfile("./testrepo/new.txt", "This isn't what's stored!");

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 252
}

void test_checkout_index__options_disable_filters(void)
{
253
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
254

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

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 265

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

266 267
	opts.disable_filters = true;
	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 273
}

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

280 281 282
	if (!cl_is_chmod_supported())
		return;

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

	reset_index_to_treeish((git_object *)commit);

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 294 295
	/* umask will influence actual directory creation mode */
	(void)p_umask(um = p_umask(022));

296
	cl_git_pass(p_stat("./testrepo/a", &st));
297 298
	/* Haiku & Hurd use other mode bits, so we must mask them out */
	cl_assert_equal_i_fmt(st.st_mode & (S_IFMT | 07777), (GIT_FILEMODE_TREE | 0701) & ~um, "%07o");
299 300 301

	/* File-mode test, since we're on the 'dir' branch */
	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 305 306 307 308

	git_commit_free(commit);
}

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

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

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));
Russell Belfer committed
321
	cl_assert_equal_i_fmt(st.st_mode & GIT_MODE_PERMS_MASK, 0700, "%07o");
322 323 324 325
}

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

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

330 331
	opts.checkout_strategy =
		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 341 342 343
	const char *file;
	const char *sha;
};

344 345 346 347 348 349
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,
350 351
	void *payload)
{
352
	struct notify_data *expectations = (struct notify_data *)payload;
353

354
	GIT_UNUSED(workdir);
355

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

	return 0;
}

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

	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";

380 381 382
	opts.checkout_strategy = GIT_CHECKOUT_SAFE |
		GIT_CHECKOUT_RECREATE_MISSING |
		GIT_CHECKOUT_ALLOW_CONFLICTS;
383 384 385
	opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
	opts.notify_cb = test_checkout_notify_cb;
	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 392 393 394 395
	git_checkout_notify_t why,
	const char *path,
	const git_diff_file *baseline,
	const git_diff_file *target,
	const git_diff_file *workdir,
396 397
	void *payload)
{
398 399 400 401 402
	GIT_UNUSED(why);
	GIT_UNUSED(path);
	GIT_UNUSED(baseline);
	GIT_UNUSED(target);
	GIT_UNUSED(workdir);
403 404 405 406 407 408 409 410 411
	GIT_UNUSED(payload);

	cl_assert(false);

	return 0;
}

void test_checkout_index__wont_notify_of_expected_line_ending_changes(void)
{
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 418
	cl_git_mkfile("./testrepo/new.txt", "my new file\r\n");

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

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

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

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

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

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

void test_checkout_index__can_overcome_name_clashes(void)
{
452
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
453 454 455 456 457 458 459 460 461
	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");

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

465 466 467 468 469 470 471 472
	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");

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

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

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

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

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

491 492
	git_index_free(index);
}
493 494 495

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

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

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

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

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

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

517 518 519 520
	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"));

521 522 523 524 525 526 527
	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));

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

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

535 536 537
	/* remove untracked will remove the .gitattributes file before the blobs
	 * were created, so they will have had crlf filtering applied on Windows
	 */
538 539 540
	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");
541 542 543 544 545 546

	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"));
}
547 548 549 550 551 552 553 554 555 556

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));
}
557 558 559

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

	test_checkout_index__cleanup();

	g_repo = cl_git_sandbox_init("issue_1397");

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

	opts.checkout_strategy = GIT_CHECKOUT_FORCE;

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

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

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

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

	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");
603 604 605 606 607 608 609

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

void test_checkout_index__target_directory_from_bare(void)
{
610
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
	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);

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

	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);

646 647 648 649
	/* 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");
650 651 652

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

	git_object_free(head);
655 656 657 658 659
}

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

	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"));

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

	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);
677
}
678

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

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

	entry.mode = 0100644;
686
	entry.path = path;
687 688

	git_oid_fromstr(&entry.id, "d427e0b2e138501a3d15cc376077a3631e15bd46");
689
	GIT_IDXENTRY_STAGE_SET(&entry, 1);
690 691 692
	cl_git_pass(git_index_add(index, &entry));

	git_oid_fromstr(&entry.id, "4e886e602529caa9ab11d71f86634bd1b6e0de10");
693
	GIT_IDXENTRY_STAGE_SET(&entry, 2);
694 695 696
	cl_git_pass(git_index_add(index, &entry));

	git_oid_fromstr(&entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863");
697
	GIT_IDXENTRY_STAGE_SET(&entry, 3);
698 699 700 701 702
	cl_git_pass(git_index_add(index, &entry));
}

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

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

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

712 713 714 715 716 717 718 719 720 721
	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);
722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744

	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);
745 746 747 748 749
}

void test_checkout_index__conflicts_honor_coreautocrlf(void)
{
#ifdef GIT_WIN32
750
	git_index *index;
751 752 753 754 755 756
	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);

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

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

762 763 764 765 766 767 768 769 770 771
	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);
772 773

	git_index_free(index);
774 775
#endif
}