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

#include "git2/checkout.h"
5
#include "fileops.h"
6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
#include "repository.h"

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

void test_checkout_index__cannot_checkout_a_bare_repository(void)
{
	test_checkout_index__cleanup();

	g_repo = cl_git_sandbox_init("testrepo.git");

37
	cl_git_fail(git_checkout_index(g_repo, NULL, NULL));
38 39
}

40
void test_checkout_index__can_create_missing_files(void)
41
{
42 43
	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;

44 45 46 47
	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"));

48
	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
49

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

52 53 54
	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");
55 56
}

57 58
void test_checkout_index__can_remove_untracked_files(void)
{
59 60
	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;

61 62 63 64 65 66
	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"));

67
	opts.checkout_strategy =
68 69
		GIT_CHECKOUT_SAFE_CREATE | GIT_CHECKOUT_REMOVE_UNTRACKED;

70
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
71 72 73 74

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

75 76
void test_checkout_index__honor_the_specified_pathspecs(void)
{
77
	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
78 79
	char *entries[] = { "*.txt" };

80 81
	opts.paths.strings = entries;
	opts.paths.count = 1;
82 83 84 85 86

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

87
	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
88

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

	cl_assert_equal_i(false, git_path_isfile("./testrepo/README"));
92 93
	check_file_contents("./testrepo/branch_file.txt", "hi\nbye!\n");
	check_file_contents("./testrepo/new.txt", "my new file\n");
94 95 96 97
}

void test_checkout_index__honor_the_gitattributes_directives(void)
{
98
	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
99 100 101 102 103
	const char *attributes =
		"branch_file.txt text eol=crlf\n"
		"new.txt text eol=lf\n";

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

106
	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
107

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

110 111 112
	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");
113 114 115 116 117
}

void test_checkout_index__honor_coreautocrlf_setting_set_to_true(void)
{
#ifdef GIT_WIN32
118
	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
119 120 121
	const char *expected_readme_text = "hey there\r\n";

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

124
	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
125

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

128
	check_file_contents("./testrepo/README", expected_readme_text);
129 130 131 132 133
#endif
}

void test_checkout_index__honor_coresymlinks_setting_set_to_true(void)
{
134 135
	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;

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

138
	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
139

140
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
141 142

#ifdef GIT_WIN32
143
	check_file_contents("./testrepo/link_to_new.txt", "new.txt");
144 145 146 147 148 149 150 151 152
#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");
153
		check_file_contents("./testrepo/link_to_new.txt", "my new file\n");
154 155 156 157 158 159
	}
#endif
}

void test_checkout_index__honor_coresymlinks_setting_set_to_false(void)
{
160 161
	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;

162
	cl_repo_set_bool(g_repo, "core.symlinks", false);
163

164
	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
165

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

168
	check_file_contents("./testrepo/link_to_new.txt", "new.txt");
169 170
}

171
void test_checkout_index__donot_overwrite_modified_file_by_default(void)
172
{
173 174
	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;

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

177 178 179
	/* set this up to not return an error code on conflicts, but it
	 * still will not have permission to overwrite anything...
	 */
180
	opts.checkout_strategy = GIT_CHECKOUT_SAFE | GIT_CHECKOUT_ALLOW_CONFLICTS;
181

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

184
	check_file_contents("./testrepo/new.txt", "This isn't what's stored!");
185 186
}

187
void test_checkout_index__can_overwrite_modified_file(void)
188
{
189 190
	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;

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

193
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
194

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

197
	check_file_contents("./testrepo/new.txt", "my new file\n");
198 199 200 201
}

void test_checkout_index__options_disable_filters(void)
{
202 203
	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;

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

206 207
	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
	opts.disable_filters = false;
208

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

211
	check_file_contents("./testrepo/new.txt", "my new file\r\n");
212 213 214

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

215 216
	opts.disable_filters = true;
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
217

218
	check_file_contents("./testrepo/new.txt", "my new file\n");
219 220 221 222 223
}

void test_checkout_index__options_dir_modes(void)
{
#ifndef GIT_WIN32
224
	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
225 226 227 228
	struct stat st;
	git_oid oid;
	git_commit *commit;

229
	cl_git_pass(git_reference_name_to_id(&oid, g_repo, "refs/heads/dir"));
230 231 232 233
	cl_git_pass(git_commit_lookup(&commit, g_repo, &oid));

	reset_index_to_treeish((git_object *)commit);

234 235
	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
	opts.dir_mode = 0701;
236

237
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
238 239 240 241 242 243 244 245 246 247 248 249 250 251 252

	cl_git_pass(p_stat("./testrepo/a", &st));
	cl_assert_equal_i(st.st_mode & 0777, 0701);

	/* File-mode test, since we're on the 'dir' branch */
	cl_git_pass(p_stat("./testrepo/a/b.txt", &st));
	cl_assert_equal_i(st.st_mode & 0777, 0755);

	git_commit_free(commit);
#endif
}

void test_checkout_index__options_override_file_modes(void)
{
#ifndef GIT_WIN32
253
	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
254 255
	struct stat st;

256 257
	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
	opts.file_mode = 0700;
258

259
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
260 261 262 263 264 265 266 267

	cl_git_pass(p_stat("./testrepo/new.txt", &st));
	cl_assert_equal_i(st.st_mode & 0777, 0700);
#endif
}

void test_checkout_index__options_open_flags(void)
{
268 269
	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;

270 271
	cl_git_mkfile("./testrepo/new.txt", "hi\n");

272 273
	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
	opts.file_open_flags = O_CREAT | O_RDWR | O_APPEND;
274

275 276
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
277

278
	check_file_contents("./testrepo/new.txt", "hi\nmy new file\n");
279
}
280

281
struct notify_data {
282 283 284 285
	const char *file;
	const char *sha;
};

286 287 288 289 290 291
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,
292 293
	void *payload)
{
294
	struct notify_data *expectations = (struct notify_data *)payload;
295

296
	GIT_UNUSED(workdir);
297

298 299 300 301
	cl_assert_equal_i(GIT_CHECKOUT_NOTIFY_CONFLICT, why);
	cl_assert_equal_s(expectations->file, path);
	cl_assert_equal_i(0, git_oid_streq(&baseline->oid, expectations->sha));
	cl_assert_equal_i(0, git_oid_streq(&target->oid, expectations->sha));
302 303 304 305 306 307

	return 0;
}

void test_checkout_index__can_notify_of_skipped_files(void)
{
308
	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
309
	struct notify_data data;
310 311 312 313 314 315 316 317 318 319 320 321

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

322
	opts.checkout_strategy =
323
		GIT_CHECKOUT_SAFE_CREATE | GIT_CHECKOUT_ALLOW_CONFLICTS;
324 325 326
	opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
	opts.notify_cb = test_checkout_notify_cb;
	opts.notify_payload = &data;
327

328
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
329 330
}

331
static int dont_notify_cb(
332 333 334 335 336
	git_checkout_notify_t why,
	const char *path,
	const git_diff_file *baseline,
	const git_diff_file *target,
	const git_diff_file *workdir,
337 338
	void *payload)
{
339 340 341 342 343
	GIT_UNUSED(why);
	GIT_UNUSED(path);
	GIT_UNUSED(baseline);
	GIT_UNUSED(target);
	GIT_UNUSED(workdir);
344 345 346 347 348 349 350 351 352
	GIT_UNUSED(payload);

	cl_assert(false);

	return 0;
}

void test_checkout_index__wont_notify_of_expected_line_ending_changes(void)
{
353 354
	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;

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

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

360
	opts.checkout_strategy =
361
		GIT_CHECKOUT_SAFE_CREATE | GIT_CHECKOUT_ALLOW_CONFLICTS;
362 363 364
	opts.notify_flags = GIT_CHECKOUT_NOTIFY_CONFLICT;
	opts.notify_cb = dont_notify_cb;
	opts.notify_payload = NULL;
365

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

369 370
static void checkout_progress_counter(
	const char *path, size_t cur, size_t tot, void *payload)
371
{
Ben Straub committed
372
	GIT_UNUSED(path); GIT_UNUSED(cur); GIT_UNUSED(tot);
373
	(*(int *)payload)++;
374 375 376 377
}

void test_checkout_index__calls_progress_callback(void)
{
378
	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
379 380
	int calls = 0;

381 382 383
	opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
	opts.progress_cb = checkout_progress_counter;
	opts.progress_payload = &calls;
384

385
	cl_git_pass(git_checkout_index(g_repo, NULL, &opts));
386
	cl_assert(calls > 0);
387
}
388 389 390

void test_checkout_index__can_overcome_name_clashes(void)
{
391
	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
392 393 394 395 396 397 398 399 400
	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");

401 402
	cl_git_pass(git_index_add_bypath(index, "path0"));
	cl_git_pass(git_index_add_bypath(index, "path1/file1"));
403

404 405 406 407 408 409 410 411
	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");

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

415
	opts.checkout_strategy =
416
		GIT_CHECKOUT_SAFE_CREATE | GIT_CHECKOUT_ALLOW_CONFLICTS;
417
	cl_git_pass(git_checkout_index(g_repo, index, &opts));
418 419 420 421

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

422 423
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
	cl_git_pass(git_checkout_index(g_repo, index, &opts));
424 425 426 427

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

428 429
	git_index_free(index);
}
430 431 432

void test_checkout_index__validates_struct_version(void)
{
433
	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
434 435
	const git_error *err;

436 437
	opts.version = 1024;
	cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
438 439 440 441

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

442
	opts.version = 0;
443
	giterr_clear();
444
	cl_git_fail(git_checkout_index(g_repo, NULL, &opts));
445 446 447 448

	err = giterr_last();
	cl_assert_equal_i(err->klass, GITERR_INVALID);
}
449 450 451 452 453

void test_checkout_index__can_update_prefixed_files(void)
{
	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;

454 455 456 457
	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"));

458 459 460 461 462 463 464
	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));

465 466
	opts.checkout_strategy =
		GIT_CHECKOUT_SAFE_CREATE | GIT_CHECKOUT_REMOVE_UNTRACKED;
467 468 469

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

470 471 472
	/* remove untracked will remove the .gitattributes file before the blobs
	 * were created, so they will have had crlf filtering applied on Windows
	 */
473 474 475
	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");
476 477 478 479 480 481

	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"));
}
482 483 484 485 486 487 488 489 490 491

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));
}
492 493 494 495 496 497 498 499 500

void test_checkout_index__issue_1397(void)
{
	git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;

	test_checkout_index__cleanup();

	g_repo = cl_git_sandbox_init("issue_1397");

Russell Belfer committed
501
	cl_repo_set_bool(g_repo, "core.autocrlf", true);
502 503 504 505 506

	opts.checkout_strategy = GIT_CHECKOUT_FORCE;

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

507
	check_file_contents("./issue_1397/crlf_file.txt", "first line\r\nsecond line\r\nboth with crlf");
508
}