index.c 24.1 KB
Newer Older
1
#include "clar_libgit2.h"
2
#include "checkout_helpers.h"
3 4

#include "git2/checkout.h"
5
#include "futils.h"
6
#include "repository.h"
7
#include "remote.h"
8
#include "repo/repo_helpers.h"
9 10

static git_repository *g_repo;
11
static git_buf g_global_path = GIT_BUF_INIT;
12 13 14 15 16 17 18 19 20 21 22 23 24 25 26

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");
27 28 29

	git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
		&g_global_path);
30 31 32 33
}

void test_checkout_index__cleanup(void)
{
34 35 36 37
	git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
		g_global_path.ptr);
	git_buf_dispose(&g_global_path);

38
	cl_git_sandbox_cleanup();
39

40 41 42 43 44
	/* try to remove directories created by tests */
	cl_fixture_cleanup("alternative");
	cl_fixture_cleanup("symlink");
	cl_fixture_cleanup("symlink.git");
	cl_fixture_cleanup("tmp_global_path");
45 46 47 48
}

void test_checkout_index__cannot_checkout_a_bare_repository(void)
{
49
	cl_git_sandbox_cleanup();
50 51
	g_repo = cl_git_sandbox_init("testrepo.git");

52
	cl_git_fail(git_checkout_index(g_repo, NULL, NULL));
53 54
}

55
void test_checkout_index__can_create_missing_files(void)
56
{
57
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
58

59 60 61 62
	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"));

63
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
64

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

67 68 69
	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");
70 71
}

72 73
void test_checkout_index__can_remove_untracked_files(void)
{
74
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
75

76
	git_futils_mkdir("./testrepo/dir/subdir/subsubdir", 0755, GIT_MKDIR_PATH);
77 78 79 80 81
	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"));

82
	opts.checkout_strategy =
83 84 85
		GIT_CHECKOUT_SAFE |
		GIT_CHECKOUT_RECREATE_MISSING |
		GIT_CHECKOUT_REMOVE_UNTRACKED;
86

87
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
88 89 90 91

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

92 93
void test_checkout_index__honor_the_specified_pathspecs(void)
{
94
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
95 96
	char *entries[] = { "*.txt" };

97 98
	opts.paths.strings = entries;
	opts.paths.count = 1;
99 100 101 102 103

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

104
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
105

106
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
107 108

	cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
109 110
	check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
	check_file_contents("./testrepo/new.txt", "my new file\n");
111 112 113 114
}

void test_checkout_index__honor_the_gitattributes_directives(void)
{
115
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
116 117 118 119 120
	const char *attributes =
		"branch_file.txt text eol=crlf\n"
		"new.txt text eol=lf\n";

	cl_git_mkfile("./testrepo/.gitattributes", attributes);
121
	cl_repo_set_bool(g_repo, "core.autocrlf", false);
122

123
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
124

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

127 128 129
	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");
130 131 132 133 134
}

void test_checkout_index__honor_coreautocrlf_setting_set_to_true(void)
{
#ifdef GIT_WIN32
135
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
136 137 138
	const char *expected_readme_text = "hey there\r\n";

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

141
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
142

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

145
	check_file_contents("./testrepo/README", expected_readme_text);
146 147 148
#endif
}

149
static void populate_symlink_workdir(void)
150
{
151
	git_buf path = GIT_BUF_INIT;
152 153 154 155 156 157
	git_repository *repo;
	git_remote *origin;
	git_object *target;

	const char *url = git_repository_path(g_repo);

158 159
	cl_git_pass(git_buf_joinpath(&path, clar_sandbox_path(), "symlink.git"));
	cl_git_pass(git_repository_init(&repo, path.ptr, true));
160 161
	cl_git_pass(git_repository_set_workdir(repo, "symlink", 1));

162 163 164
	/* Delete the `origin` repo (if it exists) so we can recreate it. */
	git_remote_delete(repo, GIT_REMOTE_ORIGIN);

165
	cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
166
	cl_git_pass(git_remote_fetch(origin, NULL, NULL, NULL));
167 168 169 170
	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));
171

172 173
	git_object_free(target);
	git_repository_free(repo);
174
	git_buf_dispose(&path);
175
}
176

177 178 179 180
void test_checkout_index__honor_coresymlinks_default_true(void)
{
	char link_data[GIT_PATH_MAX];
	int link_size = GIT_PATH_MAX;
181

182
	cl_must_pass(p_mkdir("symlink", 0777));
183

184
	if (!git_path_supports_symlinks("symlink/test"))
185
		cl_skip();
186

187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216
#ifdef GIT_WIN32
	/*
	 * Windows explicitly requires the global configuration to have
	 * core.symlinks=true in addition to actual filesystem support.
	 */
	create_tmp_global_config("tmp_global_path", "core.symlinks", "true");
#endif

	populate_symlink_workdir();

	link_size = p_readlink("./symlink/link_to_new.txt", link_data, link_size);
	cl_assert(link_size >= 0);

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

void test_checkout_index__honor_coresymlinks_default_false(void)
{
	cl_must_pass(p_mkdir("symlink", 0777));

#ifndef GIT_WIN32
	/*
	 * This test is largely for Windows platforms to ensure that
	 * we respect an unset core.symlinks even when the platform
	 * supports symlinks.  Bail entirely on POSIX platforms that
	 * do support symlinks.
	 */
217
	if (git_path_supports_symlinks("symlink/test"))
218 219 220 221 222
		cl_skip();
#endif

	populate_symlink_workdir();
	check_file_contents("./symlink/link_to_new.txt", "new.txt");
223 224
}

225 226 227 228
void test_checkout_index__coresymlinks_set_to_true_fails_when_unsupported(void)
{
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;

229
	if (git_path_supports_symlinks("testrepo/test")) {
230 231 232 233 234 235 236 237 238
		cl_skip();
	}

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

	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
	cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
}

239 240
void test_checkout_index__honor_coresymlinks_setting_set_to_true(void)
{
241
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
242 243 244
	char link_data[GIT_PATH_MAX];
	size_t link_size = GIT_PATH_MAX;

245
	if (!git_path_supports_symlinks("testrepo/test")) {
246 247
		cl_skip();
	}
248

249
	cl_repo_set_bool(g_repo, "core.symlinks", true);
250

251
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
252

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

255 256 257 258 259
	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");
	check_file_contents("./testrepo/link_to_new.txt", "my new file\n");
260 261 262 263
}

void test_checkout_index__honor_coresymlinks_setting_set_to_false(void)
{
264
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
265

266
	cl_repo_set_bool(g_repo, "core.symlinks", false);
267

268
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
269

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

272
	check_file_contents("./testrepo/link_to_new.txt", "new.txt");
273 274
}

275
void test_checkout_index__donot_overwrite_modified_file_by_default(void)
276
{
277
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
278

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

281 282 283
	/* set this up to not return an error code on conflicts, but it
	 * still will not have permission to overwrite anything...
	 */
284
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_ALLOW_CONFLICTS;
285

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

288
	check_file_contents("./testrepo/new.txt", "This isn't what's stored!");
289 290
}

291
void test_checkout_index__can_overwrite_modified_file(void)
292
{
293
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
294

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

297
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
298

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

301
	check_file_contents("./testrepo/new.txt", "my new file\n");
302 303 304 305
}

void test_checkout_index__options_disable_filters(void)
{
306
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
307

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

310
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
311
	opts.disable_filters = false;
312

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

315
	check_file_contents("./testrepo/new.txt", "my new file\r\n");
316 317 318

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

319 320
	opts.disable_filters = true;
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
321

322
	check_file_contents("./testrepo/new.txt", "my new file\n");
323 324 325 326
}

void test_checkout_index__options_dir_modes(void)
{
327
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
328 329 330
	struct stat st;
	git_oid oid;
	git_commit *commit;
331
	mode_t um;
332

333 334 335
	if (!cl_is_chmod_supported())
		return;

336
	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
337 338 339 340
	cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));

	reset_index_to_treeish((git_object *)commit);

341
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
342
	opts.dir_mode = 0701;
343

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

346 347 348
	/* umask will influence actual directory creation mode */
	(void)p_umask(um = p_umask(022));

349
	cl_git_pass(p_stat("./testrepo/a", &st));
350 351
	/* 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");
352 353 354

	/* File-mode test, since we're on the 'dir' branch */
	cl_git_pass(p_stat("./testrepo/a/b.txt", &st));
355
	cl_assert_equal_i_fmt(st.st_mode & (S_IFMT | 07777), GIT_FILEMODE_BLOB_EXECUTABLE & ~um, "%07o");
356 357 358 359 360 361

	git_commit_free(commit);
}

void test_checkout_index__options_override_file_modes(void)
{
362
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
363 364
	struct stat st;

365 366 367
	if (!cl_is_chmod_supported())
		return;

368
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
369
	opts.file_mode = 0700;
370

371
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
372 373

	cl_git_pass(p_stat("./testrepo/new.txt", &st));
Russell Belfer committed
374
	cl_assert_equal_i_fmt(st.st_mode & GIT_MODE_PERMS_MASK, 0700, "%07o");
375 376 377 378
}

void test_checkout_index__options_open_flags(void)
{
379
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
380

381 382
	cl_git_mkfile("./testrepo/new.txt", "hi\n");

383 384
	opts.checkout_strategy =
		GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DONT_REMOVE_EXISTING;
385
	opts.file_open_flags = O_CREAT | O_RDWR | O_APPEND;
386

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

389
	check_file_contents("./testrepo/new.txt", "hi\nmy new file\n");
390
}
391

392
struct notify_data {
393 394 395 396
	const char *file;
	const char *sha;
};

397 398 399 400 401 402
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,
403 404
	void *payload)
{
405
	struct notify_data *expectations = (struct notify_data *)payload;
406

407
	GIT_UNUSED(workdir);
408

409 410
	cl_assert_equal_i(GIT_CHECKOUT_NOTIFY_CONFLICT, why);
	cl_assert_equal_s(expectations->file, path);
411 412
	cl_assert_equal_i(0, git_oid_streq(&baseline->id, expectations->sha));
	cl_assert_equal_i(0, git_oid_streq(&target->id, expectations->sha));
413 414 415 416 417 418

	return 0;
}

void test_checkout_index__can_notify_of_skipped_files(void)
{
419
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
420
	struct notify_data data;
421 422 423 424 425 426 427 428 429 430 431 432

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

433 434 435
	opts.checkout_strategy = GIT_CHECKOUT_SAFE |
		GIT_CHECKOUT_RECREATE_MISSING |
		GIT_CHECKOUT_ALLOW_CONFLICTS;
436 437 438
	opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
	opts.notify_cb = test_checkout_notify_cb;
	opts.notify_payload = &data;
439

440
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
441 442
}

443
static int dont_notify_cb(
444 445 446 447 448
	git_checkout_notify_t why,
	const char *path,
	const git_diff_file *baseline,
	const git_diff_file *target,
	const git_diff_file *workdir,
449 450
	void *payload)
{
451 452 453 454 455
	GIT_UNUSED(why);
	GIT_UNUSED(path);
	GIT_UNUSED(baseline);
	GIT_UNUSED(target);
	GIT_UNUSED(workdir);
456 457 458 459 460 461 462 463 464
	GIT_UNUSED(payload);

	cl_assert(false);

	return 0;
}

void test_checkout_index__wont_notify_of_expected_line_ending_changes(void)
{
465
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
466

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

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

472
	opts.checkout_strategy =
473 474 475
		GIT_CHECKOUT_SAFE |
		GIT_CHECKOUT_RECREATE_MISSING |
		GIT_CHECKOUT_ALLOW_CONFLICTS;
476 477 478
	opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
	opts.notify_cb = dont_notify_cb;
	opts.notify_payload = NULL;
479

480
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
481 482
}

483 484
static void checkout_progress_counter(
	const char *path, size_t cur, size_t tot, void *payload)
485
{
Ben Straub committed
486
	GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
487
	(*(int *)payload)++;
488 489 490 491
}

void test_checkout_index__calls_progress_callback(void)
{
492
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
493 494
	int calls = 0;

495
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
496 497
	opts.progress_cb = checkout_progress_counter;
	opts.progress_payload = &calls;
498

499
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
500
	cl_assert(calls > 0);
501
}
502 503 504

void test_checkout_index__can_overcome_name_clashes(void)
{
505
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
506 507 508 509 510 511 512 513 514
	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");

515 516
	cl_git_pass(git_index_add_bypath(index, "path0"));
	cl_git_pass(git_index_add_bypath(index, "path1/file1"));
517

518 519 520 521 522 523 524 525
	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");

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

529
	opts.checkout_strategy =
530
		GIT_CHECKOUT_SAFE |
531 532
		GIT_CHECKOUT_RECREATE_MISSING |
		GIT_CHECKOUT_ALLOW_CONFLICTS;
533
	cl_git_pass(git_checkout_index(g_repo, index, &opts));
534 535 536 537

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

538 539
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
	cl_git_pass(git_checkout_index(g_repo, index, &opts));
540 541 542 543

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

544 545
	git_index_free(index);
}
546 547 548

void test_checkout_index__validates_struct_version(void)
{
549
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
550 551
	const git_error *err;

552 553
	opts.version = 1024;
	cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
554

555 556
	err = git_error_last();
	cl_assert_equal_i(err->klass, GIT_ERROR_INVALID);
557

558
	opts.version = 0;
559
	git_error_clear();
560
	cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
561

562 563
	err = git_error_last();
	cl_assert_equal_i(err->klass, GIT_ERROR_INVALID);
564
}
565 566 567

void test_checkout_index__can_update_prefixed_files(void)
{
568
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
569

570 571 572 573
	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"));

574 575 576 577 578 579 580
	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));

581
	opts.checkout_strategy =
582 583 584
		GIT_CHECKOUT_SAFE |
		GIT_CHECKOUT_RECREATE_MISSING |
		GIT_CHECKOUT_REMOVE_UNTRACKED;
585 586 587

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

588 589 590
	/* remove untracked will remove the .gitattributes file before the blobs
	 * were created, so they will have had crlf filtering applied on Windows
	 */
591 592 593
	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");
594 595 596 597 598 599

	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"));
}
600 601 602

void test_checkout_index__can_checkout_a_newly_initialized_repository(void)
{
603
	cl_git_sandbox_cleanup();
604
	g_repo = cl_git_sandbox_init("empty_standard_repo");
605

606 607 608 609
	cl_git_remove_placeholders(git_repository_path(g_repo), "dummy-marker.txt");

	cl_git_pass(git_checkout_index(g_repo, NULL, NULL));
}
610 611 612

void test_checkout_index__issue_1397(void)
{
613
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
614

615
	cl_git_sandbox_cleanup();
616 617
	g_repo = cl_git_sandbox_init("issue_1397");

Russell Belfer committed
618
	cl_repo_set_bool(g_repo, "core.autocrlf", true);
619 620 621 622 623

	opts.checkout_strategy = GIT_CHECKOUT_FORCE;

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

624
	check_file_contents("./issue_1397/crlf_file.txt", "first line\r\nsecond line\r\nboth with crlf");
625
}
626 627 628

void test_checkout_index__target_directory(void)
{
629
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
630 631 632
	checkout_counts cts;
	memset(&cts, 0, sizeof(cts));

633 634
	opts.checkout_strategy = GIT_CHECKOUT_SAFE |
		GIT_CHECKOUT_RECREATE_MISSING;
635
	opts.target_directory = "alternative";
636
	cl_assert(!git_path_isdir("alternative"));
637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654

	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");
655 656 657 658 659 660 661

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

void test_checkout_index__target_directory_from_bare(void)
{
662
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
663 664 665 666 667
	git_index *index;
	git_object *head = NULL;
	checkout_counts cts;
	memset(&cts, 0, sizeof(cts));

668
	cl_git_sandbox_cleanup();
669 670 671 672 673 674 675 676 677
	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);

678 679
	opts.checkout_strategy = GIT_CHECKOUT_SAFE |
		GIT_CHECKOUT_RECREATE_MISSING;
680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696

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

697 698 699 700
	/* 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");
701 702 703

	cl_git_pass(git_futils_rmdir_r(
		"alternative", NULL, GIT_RMDIR_REMOVE_FILES));
nulltoken committed
704 705

	git_object_free(head);
706 707 708 709 710
}

void test_checkout_index__can_get_repo_from_index(void)
{
	git_index *index;
711
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
712 713 714 715 716

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

717
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
718 719 720 721 722 723 724 725 726 727

	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);
728
}
729

730
static void add_conflict(git_index *index, const char *path)
731 732 733 734 735 736
{
	git_index_entry entry;

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

	entry.mode = 0100644;
737
	entry.path = path;
738 739

	git_oid_fromstr(&entry.id, "d427e0b2e138501a3d15cc376077a3631e15bd46");
740
	GIT_INDEX_ENTRY_STAGE_SET(&entry, 1);
741 742 743
	cl_git_pass(git_index_add(index, &entry));

	git_oid_fromstr(&entry.id, "4e886e602529caa9ab11d71f86634bd1b6e0de10");
744
	GIT_INDEX_ENTRY_STAGE_SET(&entry, 2);
745 746 747
	cl_git_pass(git_index_add(index, &entry));

	git_oid_fromstr(&entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863");
748
	GIT_INDEX_ENTRY_STAGE_SET(&entry, 3);
749 750 751 752 753
	cl_git_pass(git_index_add(index, &entry));
}

void test_checkout_index__writes_conflict_file(void)
{
754
	git_index *index;
755 756 757
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
	git_buf conflicting_buf = GIT_BUF_INIT;

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
	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);
772
	git_buf_dispose(&conflicting_buf);
773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795

	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);
796 797 798 799 800
}

void test_checkout_index__conflicts_honor_coreautocrlf(void)
{
#ifdef GIT_WIN32
801
	git_index *index;
802 803 804 805 806 807
	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);

808 809 810 811 812
	cl_git_pass(git_repository_index(&index, g_repo));

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

813 814 815 816 817 818 819 820 821
	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);
822
	git_buf_dispose(&conflicting_buf);
823 824

	git_index_free(index);
825 826
#endif
}