index.c 24 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
#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 152 153 154 155 156 157 158 159
{
	git_repository *repo;
	git_remote *origin;
	git_object *target;

	const char *url = git_repository_path(g_repo);

	cl_git_pass(git_repository_init(&repo, "../symlink.git", true));
	cl_git_pass(git_repository_set_workdir(repo, "symlink", 1));

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

163
	cl_git_pass(git_remote_create(&origin, repo, GIT_REMOTE_ORIGIN, url));
164
	cl_git_pass(git_remote_fetch(origin, NULL, NULL, NULL));
165 166 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));
	git_object_free(target);
	git_repository_free(repo);
171
}
172

173 174 175 176
void test_checkout_index__honor_coresymlinks_default_true(void)
{
	char link_data[GIT_PATH_MAX];
	int link_size = GIT_PATH_MAX;
177

178
	cl_must_pass(p_mkdir("symlink", 0777));
179

180 181
	if (!filesystem_supports_symlinks("symlink/test"))
		cl_skip();
182

183 184 185 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 217 218
#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.
	 */
	if (filesystem_supports_symlinks("symlink/test"))
		cl_skip();
#endif

	populate_symlink_workdir();
	check_file_contents("./symlink/link_to_new.txt", "new.txt");
219 220
}

221 222 223 224
void test_checkout_index__coresymlinks_set_to_true_fails_when_unsupported(void)
{
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;

225
	if (filesystem_supports_symlinks("testrepo/test")) {
226 227 228 229 230 231 232 233 234
		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));
}

235 236
void test_checkout_index__honor_coresymlinks_setting_set_to_true(void)
{
237
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
238 239 240
	char link_data[GIT_PATH_MAX];
	size_t link_size = GIT_PATH_MAX;

241
	if (!filesystem_supports_symlinks("testrepo/test")) {
242 243
		cl_skip();
	}
244

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

247
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
248

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

251 252 253 254 255
	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");
256 257 258 259
}

void test_checkout_index__honor_coresymlinks_setting_set_to_false(void)
{
260
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
261

262
	cl_repo_set_bool(g_repo, "core.symlinks", false);
263

264
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
265

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

268
	check_file_contents("./testrepo/link_to_new.txt", "new.txt");
269 270
}

271
void test_checkout_index__donot_overwrite_modified_file_by_default(void)
272
{
273
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
274

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

277 278 279
	/* set this up to not return an error code on conflicts, but it
	 * still will not have permission to overwrite anything...
	 */
280
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_ALLOW_CONFLICTS;
281

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

284
	check_file_contents("./testrepo/new.txt", "This isn't what's stored!");
285 286
}

287
void test_checkout_index__can_overwrite_modified_file(void)
288
{
289
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
290

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

293
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
294

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

297
	check_file_contents("./testrepo/new.txt", "my new file\n");
298 299 300 301
}

void test_checkout_index__options_disable_filters(void)
{
302
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
303

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

306
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
307
	opts.disable_filters = false;
308

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

311
	check_file_contents("./testrepo/new.txt", "my new file\r\n");
312 313 314

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

315 316
	opts.disable_filters = true;
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
317

318
	check_file_contents("./testrepo/new.txt", "my new file\n");
319 320 321 322
}

void test_checkout_index__options_dir_modes(void)
{
323
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
324 325 326
	struct stat st;
	git_oid oid;
	git_commit *commit;
327
	mode_t um;
328

329 330 331
	if (!cl_is_chmod_supported())
		return;

332
	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
333 334 335 336
	cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));

	reset_index_to_treeish((git_object *)commit);

337
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
338
	opts.dir_mode = 0701;
339

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

342 343 344
	/* umask will influence actual directory creation mode */
	(void)p_umask(um = p_umask(022));

345
	cl_git_pass(p_stat("./testrepo/a", &st));
346 347
	/* 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");
348 349 350

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

	git_commit_free(commit);
}

void test_checkout_index__options_override_file_modes(void)
{
358
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
359 360
	struct stat st;

361 362 363
	if (!cl_is_chmod_supported())
		return;

364
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
365
	opts.file_mode = 0700;
366

367
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
368 369

	cl_git_pass(p_stat("./testrepo/new.txt", &st));
Russell Belfer committed
370
	cl_assert_equal_i_fmt(st.st_mode & GIT_MODE_PERMS_MASK, 0700, "%07o");
371 372 373 374
}

void test_checkout_index__options_open_flags(void)
{
375
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
376

377 378
	cl_git_mkfile("./testrepo/new.txt", "hi\n");

379 380
	opts.checkout_strategy =
		GIT_CHECKOUT_FORCE | GIT_CHECKOUT_DONT_REMOVE_EXISTING;
381
	opts.file_open_flags = O_CREAT | O_RDWR | O_APPEND;
382

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

385
	check_file_contents("./testrepo/new.txt", "hi\nmy new file\n");
386
}
387

388
struct notify_data {
389 390 391 392
	const char *file;
	const char *sha;
};

393 394 395 396 397 398
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,
399 400
	void *payload)
{
401
	struct notify_data *expectations = (struct notify_data *)payload;
402

403
	GIT_UNUSED(workdir);
404

405 406
	cl_assert_equal_i(GIT_CHECKOUT_NOTIFY_CONFLICT, why);
	cl_assert_equal_s(expectations->file, path);
407 408
	cl_assert_equal_i(0, git_oid_streq(&baseline->id, expectations->sha));
	cl_assert_equal_i(0, git_oid_streq(&target->id, expectations->sha));
409 410 411 412 413 414

	return 0;
}

void test_checkout_index__can_notify_of_skipped_files(void)
{
415
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
416
	struct notify_data data;
417 418 419 420 421 422 423 424 425 426 427 428

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

429 430 431
	opts.checkout_strategy = GIT_CHECKOUT_SAFE |
		GIT_CHECKOUT_RECREATE_MISSING |
		GIT_CHECKOUT_ALLOW_CONFLICTS;
432 433 434
	opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
	opts.notify_cb = test_checkout_notify_cb;
	opts.notify_payload = &data;
435

436
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
437 438
}

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

	cl_assert(false);

	return 0;
}

void test_checkout_index__wont_notify_of_expected_line_ending_changes(void)
{
461
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
462

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

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

468
	opts.checkout_strategy =
469 470 471
		GIT_CHECKOUT_SAFE |
		GIT_CHECKOUT_RECREATE_MISSING |
		GIT_CHECKOUT_ALLOW_CONFLICTS;
472 473 474
	opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
	opts.notify_cb = dont_notify_cb;
	opts.notify_payload = NULL;
475

476
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
477 478
}

479 480
static void checkout_progress_counter(
	const char *path, size_t cur, size_t tot, void *payload)
481
{
Ben Straub committed
482
	GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
483
	(*(int *)payload)++;
484 485 486 487
}

void test_checkout_index__calls_progress_callback(void)
{
488
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
489 490
	int calls = 0;

491
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
492 493
	opts.progress_cb = checkout_progress_counter;
	opts.progress_payload = &calls;
494

495
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
496
	cl_assert(calls > 0);
497
}
498 499 500

void test_checkout_index__can_overcome_name_clashes(void)
{
501
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
502 503 504 505 506 507 508 509 510
	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");

511 512
	cl_git_pass(git_index_add_bypath(index, "path0"));
	cl_git_pass(git_index_add_bypath(index, "path1/file1"));
513

514 515 516 517 518 519 520 521
	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");

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

525
	opts.checkout_strategy =
526
		GIT_CHECKOUT_SAFE |
527 528
		GIT_CHECKOUT_RECREATE_MISSING |
		GIT_CHECKOUT_ALLOW_CONFLICTS;
529
	cl_git_pass(git_checkout_index(g_repo, index, &opts));
530 531 532 533

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

534 535
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
	cl_git_pass(git_checkout_index(g_repo, index, &opts));
536 537 538 539

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

540 541
	git_index_free(index);
}
542 543 544

void test_checkout_index__validates_struct_version(void)
{
545
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
546 547
	const git_error *err;

548 549
	opts.version = 1024;
	cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
550

551 552
	err = git_error_last();
	cl_assert_equal_i(err->klass, GIT_ERROR_INVALID);
553

554
	opts.version = 0;
555
	git_error_clear();
556
	cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
557

558 559
	err = git_error_last();
	cl_assert_equal_i(err->klass, GIT_ERROR_INVALID);
560
}
561 562 563

void test_checkout_index__can_update_prefixed_files(void)
{
564
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
565

566 567 568 569
	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"));

570 571 572 573 574 575 576
	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));

577
	opts.checkout_strategy =
578 579 580
		GIT_CHECKOUT_SAFE |
		GIT_CHECKOUT_RECREATE_MISSING |
		GIT_CHECKOUT_REMOVE_UNTRACKED;
581 582 583

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

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

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

void test_checkout_index__can_checkout_a_newly_initialized_repository(void)
{
599
	cl_git_sandbox_cleanup();
600
	g_repo = cl_git_sandbox_init("empty_standard_repo");
601

602 603 604 605
	cl_git_remove_placeholders(git_repository_path(g_repo), "dummy-marker.txt");

	cl_git_pass(git_checkout_index(g_repo, NULL, NULL));
}
606 607 608

void test_checkout_index__issue_1397(void)
{
609
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
610

611
	cl_git_sandbox_cleanup();
612 613
	g_repo = cl_git_sandbox_init("issue_1397");

Russell Belfer committed
614
	cl_repo_set_bool(g_repo, "core.autocrlf", true);
615 616 617 618 619

	opts.checkout_strategy = GIT_CHECKOUT_FORCE;

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

620
	check_file_contents("./issue_1397/crlf_file.txt", "first line\r\nsecond line\r\nboth with crlf");
621
}
622 623 624

void test_checkout_index__target_directory(void)
{
625
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
626 627 628
	checkout_counts cts;
	memset(&cts, 0, sizeof(cts));

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

	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");
651 652 653 654 655 656 657

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

void test_checkout_index__target_directory_from_bare(void)
{
658
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
659 660 661 662 663
	git_index *index;
	git_object *head = NULL;
	checkout_counts cts;
	memset(&cts, 0, sizeof(cts));

664
	cl_git_sandbox_cleanup();
665 666 667 668 669 670 671 672 673
	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);

674 675
	opts.checkout_strategy = GIT_CHECKOUT_SAFE |
		GIT_CHECKOUT_RECREATE_MISSING;
676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692

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

693 694 695 696
	/* 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");
697 698 699

	cl_git_pass(git_futils_rmdir_r(
		"alternative", NULL, GIT_RMDIR_REMOVE_FILES));
nulltoken committed
700 701

	git_object_free(head);
702 703 704 705 706
}

void test_checkout_index__can_get_repo_from_index(void)
{
	git_index *index;
707
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
708 709 710 711 712

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

713
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_RECREATE_MISSING;
714 715 716 717 718 719 720 721 722 723

	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);
724
}
725

726
static void add_conflict(git_index *index, const char *path)
727 728 729 730 731 732
{
	git_index_entry entry;

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

	entry.mode = 0100644;
733
	entry.path = path;
734 735

	git_oid_fromstr(&entry.id, "d427e0b2e138501a3d15cc376077a3631e15bd46");
736
	GIT_INDEX_ENTRY_STAGE_SET(&entry, 1);
737 738 739
	cl_git_pass(git_index_add(index, &entry));

	git_oid_fromstr(&entry.id, "4e886e602529caa9ab11d71f86634bd1b6e0de10");
740
	GIT_INDEX_ENTRY_STAGE_SET(&entry, 2);
741 742 743
	cl_git_pass(git_index_add(index, &entry));

	git_oid_fromstr(&entry.id, "2bd0a343aeef7a2cf0d158478966a6e587ff3863");
744
	GIT_INDEX_ENTRY_STAGE_SET(&entry, 3);
745 746 747 748 749
	cl_git_pass(git_index_add(index, &entry));
}

void test_checkout_index__writes_conflict_file(void)
{
750
	git_index *index;
751 752 753
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
	git_buf conflicting_buf = GIT_BUF_INIT;

754 755 756 757 758
	cl_git_pass(git_repository_index(&index, g_repo));

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

759 760 761 762 763 764 765 766 767
	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);
768
	git_buf_dispose(&conflicting_buf);
769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791

	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);
792 793 794 795 796
}

void test_checkout_index__conflicts_honor_coreautocrlf(void)
{
#ifdef GIT_WIN32
797
	git_index *index;
798 799 800 801 802 803
	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);

804 805 806 807 808
	cl_git_pass(git_repository_index(&index, g_repo));

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

809 810 811 812 813 814 815 816 817
	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);
818
	git_buf_dispose(&conflicting_buf);
819 820

	git_index_free(index);
821 822
#endif
}