crlf.c 12.7 KB
Newer Older
1 2
#include "clar_libgit2.h"
#include "checkout_helpers.h"
3
#include "../filter/crlf.h"
4
#include "fileops.h"
5 6 7

#include "git2/checkout.h"
#include "repository.h"
8
#include "index.h"
9
#include "posix.h"
10 11 12

static git_repository *g_repo;

13 14 15
static const char *systype;
static git_buf expected_fixture = GIT_BUF_INIT;

16 17 18
void test_checkout_crlf__initialize(void)
{
	g_repo = cl_git_sandbox_init("crlf");
19 20 21 22 23

	if (GIT_EOL_NATIVE == GIT_EOL_CRLF)
		systype = "windows";
	else
		systype = "posix";
24 25 26 27 28
}

void test_checkout_crlf__cleanup(void)
{
	cl_git_sandbox_cleanup();
29 30 31

	if (expected_fixture.size) {
		cl_fixture_cleanup(expected_fixture.ptr);
32
		git_buf_dispose(&expected_fixture);
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
	}
}

struct compare_data
{
	const char *dirname;
	const char *autocrlf;
	const char *attrs;
};

static int compare_file(void *payload, git_buf *actual_path)
{
	git_buf expected_path = GIT_BUF_INIT;
	git_buf actual_contents = GIT_BUF_INIT;
	git_buf expected_contents = GIT_BUF_INIT;
	struct compare_data *cd = payload;
	bool failed = true;
50 51
	int cmp_git, cmp_gitattributes;
	char *basename;
52

53 54 55 56 57
	basename = git_path_basename(actual_path->ptr);
	cmp_git = strcmp(basename, ".git");
	cmp_gitattributes = strcmp(basename, ".gitattributes");

	if (cmp_git == 0 || cmp_gitattributes == 0) {
58 59 60 61
		failed = false;
		goto done;
	}

62
	cl_git_pass(git_buf_joinpath(&expected_path, cd->dirname, basename));
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86

	if (!git_path_isfile(expected_path.ptr) ||
		!git_path_isfile(actual_path->ptr))
		goto done;

	if (git_futils_readbuffer(&actual_contents, actual_path->ptr) < 0 ||
		git_futils_readbuffer(&expected_contents, expected_path.ptr) < 0)
		goto done;

	if (actual_contents.size != expected_contents.size)
		goto done;

	if (memcmp(actual_contents.ptr, expected_contents.ptr, expected_contents.size) != 0)
		goto done;

	failed = false;

done:
	if (failed) {
		git_buf details = GIT_BUF_INIT;
		git_buf_printf(&details, "filename=%s, system=%s, autocrlf=%s, attrs={%s}",
			git_path_basename(actual_path->ptr), systype, cd->autocrlf, cd->attrs);
		clar__fail(__FILE__, __LINE__,
			"checked out contents did not match expected", details.ptr, 0);
87
		git_buf_dispose(&details);
88 89
	}

90
	git__free(basename);
91 92 93
	git_buf_dispose(&expected_contents);
	git_buf_dispose(&actual_contents);
	git_buf_dispose(&expected_path);
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141

	return 0;
}

static void test_checkout(const char *autocrlf, const char *attrs)
{
	git_buf attrbuf = GIT_BUF_INIT;
	git_buf expected_dirname = GIT_BUF_INIT;
	git_buf sandboxname = GIT_BUF_INIT;
	git_buf reponame = GIT_BUF_INIT;
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
	struct compare_data compare_data = { NULL, autocrlf, attrs };
	const char *c;

	git_buf_puts(&reponame, "crlf");

	git_buf_puts(&sandboxname, "autocrlf_");
	git_buf_puts(&sandboxname, autocrlf);

	if (*attrs) {
		git_buf_puts(&sandboxname, ",");

		for (c = attrs; *c; c++) {
			if (*c == ' ')
				git_buf_putc(&sandboxname, ',');
			else if (*c == '=')
				git_buf_putc(&sandboxname, '_');
			else
				git_buf_putc(&sandboxname, *c);
		}

		git_buf_printf(&attrbuf, "* %s\n", attrs);
		cl_git_mkfile("crlf/.gitattributes", attrbuf.ptr);
	}

	cl_repo_set_string(g_repo, "core.autocrlf", autocrlf);

	git_buf_joinpath(&expected_dirname, systype, sandboxname.ptr);
	git_buf_joinpath(&expected_fixture, "crlf_data", expected_dirname.ptr);
	cl_fixture_sandbox(expected_fixture.ptr);

	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
	git_checkout_head(g_repo, &opts);

	compare_data.dirname = sandboxname.ptr;
	cl_git_pass(git_path_direach(&reponame, 0, compare_file, &compare_data));

	cl_fixture_cleanup(expected_fixture.ptr);
142
	git_buf_dispose(&expected_fixture);
143

144 145 146 147 148
	git_buf_dispose(&attrbuf);
	git_buf_dispose(&expected_fixture);
	git_buf_dispose(&expected_dirname);
	git_buf_dispose(&sandboxname);
	git_buf_dispose(&reponame);
149 150 151 152 153 154 155 156 157 158
}

static void empty_workdir(const char *name)
{
	git_vector contents = GIT_VECTOR_INIT;
	size_t i;
	const char *fn;

	git_path_dirload(&contents, name, 0, 0);
	git_vector_foreach(&contents, i, fn) {
159 160 161 162 163 164
		char *basename = git_path_basename(fn);
		int cmp = strncasecmp(basename, ".git", 4);

		git__free(basename);

		if (cmp == 0)
165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185
			continue;
		p_unlink(fn);
	}
	git_vector_free_deep(&contents);
}

void test_checkout_crlf__matches_core_git(void)
{
	const char *autocrlf[] = { "true", "false", "input", NULL };
	const char *attrs[] = { "", "-crlf", "-text", "eol=crlf", "eol=lf",
		"text", "text eol=crlf", "text eol=lf",
		"text=auto", "text=auto eol=crlf", "text=auto eol=lf", 
		NULL };
	const char **a, **b;

	for (a = autocrlf; *a; a++) {
		for (b = attrs; *b; b++) {
			empty_workdir("crlf");
			test_checkout(*a, *b);
		}
	}
186 187
}

188 189 190
void test_checkout_crlf__detect_crlf_autocrlf_false(void)
{
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
191
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
192 193 194 195 196 197 198 199 200

	cl_repo_set_bool(g_repo, "core.autocrlf", false);

	git_checkout_head(g_repo, &opts);

	check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
	check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
}

201 202 203 204
void test_checkout_crlf__autocrlf_false_index_size_is_unfiltered_size(void)
{
	git_index *index;
	const git_index_entry *entry;
205
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
206
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
207

208
	cl_repo_set_bool(g_repo, "core.autocrlf", false);
209 210

	git_repository_index(&index, g_repo);
211 212 213
	tick_index(index);

	git_checkout_head(g_repo, &opts);
214 215 216 217

	cl_assert((entry = git_index_get_bypath(index, "all-lf", 0)) != NULL);
	cl_assert(entry->file_size == strlen(ALL_LF_TEXT_RAW));

218 219 220
	cl_assert((entry = git_index_get_bypath(index, "all-crlf", 0)) != NULL);
	cl_assert(entry->file_size == strlen(ALL_CRLF_TEXT_RAW));

221 222 223 224 225
	git_index_free(index);
}

void test_checkout_crlf__detect_crlf_autocrlf_true(void)
{
226
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
227
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
228

229
	cl_repo_set_bool(g_repo, "core.autocrlf", true);
230 231 232

	git_checkout_head(g_repo, &opts);

233
	check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF);
234
	check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
235 236
}

237 238 239
void test_checkout_crlf__detect_crlf_autocrlf_true_utf8(void)
{
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
240
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
241 242 243

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

244
	git_repository_set_head(g_repo, "refs/heads/master");
245 246
	git_checkout_head(g_repo, &opts);

247 248
	check_file_contents("./crlf/few-utf8-chars-lf", FEW_UTF8_CRLF_RAW);
	check_file_contents("./crlf/many-utf8-chars-lf", MANY_UTF8_CRLF_RAW);
249

250 251
	check_file_contents("./crlf/few-utf8-chars-crlf", FEW_UTF8_CRLF_RAW);
	check_file_contents("./crlf/many-utf8-chars-crlf", MANY_UTF8_CRLF_RAW);
252 253
}

254 255 256 257
void test_checkout_crlf__autocrlf_true_index_size_is_filtered_size(void)
{
	git_index *index;
	const git_index_entry *entry;
258
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
259
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
260

261
	cl_repo_set_bool(g_repo, "core.autocrlf", true);
262 263

	git_repository_index(&index, g_repo);
264 265 266
	tick_index(index);

	git_checkout_head(g_repo, &opts);
267 268

	cl_assert((entry = git_index_get_bypath(index, "all-lf", 0)) != NULL);
269

270
	cl_assert_equal_sz(strlen(ALL_LF_TEXT_AS_CRLF), entry->file_size);
271

272
	cl_assert((entry = git_index_get_bypath(index, "all-crlf", 0)) != NULL);
273
	cl_assert_equal_sz(strlen(ALL_CRLF_TEXT_RAW), entry->file_size);
274

275 276
	git_index_free(index);
}
277 278 279 280

void test_checkout_crlf__with_ident(void)
{
	git_index *index;
Jacques Germishuys committed
281
	const git_index_entry *entry;
282
	git_blob *blob;
283
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
284
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299

	cl_git_mkfile("crlf/.gitattributes",
		"*.txt text\n*.bin binary\n"
		"*.crlf text eol=crlf\n"
		"*.lf text eol=lf\n"
		"*.ident text ident\n"
		"*.identcrlf ident text eol=crlf\n"
		"*.identlf ident text eol=lf\n");

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

	/* add files with $Id$ */

	cl_git_mkfile("crlf/lf.ident", ALL_LF_TEXT_RAW "\n$Id: initial content$\n");
	cl_git_mkfile("crlf/crlf.ident", ALL_CRLF_TEXT_RAW "\r\n$Id$\r\n\r\n");
300 301
	cl_git_mkfile("crlf/more1.identlf", "$Id$\n" MORE_LF_TEXT_RAW);
	cl_git_mkfile("crlf/more2.identcrlf", "\r\n$Id: $\r\n" MORE_CRLF_TEXT_RAW);
302 303 304 305

	cl_git_pass(git_repository_index(&index, g_repo));
	cl_git_pass(git_index_add_bypath(index, "lf.ident"));
	cl_git_pass(git_index_add_bypath(index, "crlf.ident"));
306 307
	cl_git_pass(git_index_add_bypath(index, "more1.identlf"));
	cl_git_pass(git_index_add_bypath(index, "more2.identcrlf"));
308 309 310 311
	cl_repo_commit_from_index(NULL, g_repo, NULL, 0, "Some ident files\n");

	git_checkout_head(g_repo, &opts);

312
	/* check that blobs have $Id$ */
313

314 315
	cl_assert((entry = git_index_get_bypath(index, "lf.ident", 0)));
	cl_git_pass(git_blob_lookup(&blob, g_repo, &entry->id));
316 317
	cl_assert_equal_s(
		ALL_LF_TEXT_RAW "\n$Id$\n", git_blob_rawcontent(blob));
318
	git_blob_free(blob);
319

320 321
	cl_assert((entry = git_index_get_bypath(index, "more2.identcrlf", 0)));
	cl_git_pass(git_blob_lookup(&blob, g_repo, &entry->id));
322 323
	cl_assert_equal_s(
		"\n$Id$\n" MORE_CRLF_TEXT_AS_LF, git_blob_rawcontent(blob));
324 325 326 327 328 329 330 331 332 333
	git_blob_free(blob);

	/* check that filesystem is initially untouched - matching core Git */

	cl_assert_equal_file(
		ALL_LF_TEXT_RAW "\n$Id: initial content$\n", 0, "crlf/lf.ident");

	/* check that forced checkout rewrites correctly */

	p_unlink("crlf/lf.ident");
334 335 336
	p_unlink("crlf/crlf.ident");
	p_unlink("crlf/more1.identlf");
	p_unlink("crlf/more2.identcrlf");
337 338 339

	git_checkout_head(g_repo, &opts);

340 341 342 343 344 345 346 347
	cl_assert_equal_file(
		ALL_LF_TEXT_AS_CRLF
		"\r\n$Id: fcf6d4d9c212dc66563b1171b1cd99953c756467 $\r\n",
		0, "crlf/lf.ident");
	cl_assert_equal_file(
		ALL_CRLF_TEXT_RAW
		"\r\n$Id: f2c66ad9b2b5a734d9bf00d5000cc10a62b8a857 $\r\n\r\n",
		0, "crlf/crlf.ident");
348 349

	cl_assert_equal_file(
350
		"$Id: f7830382dac1f1583422be5530fdfbd26289431b $\n"
351 352 353
		MORE_LF_TEXT_AS_LF, 0, "crlf/more1.identlf");

	cl_assert_equal_file(
354
		"\r\n$Id: 74677a68413012ce8d7e7cfc3f12603df3a3eac4 $\r\n"
355
		MORE_CRLF_TEXT_AS_CRLF, 0, "crlf/more2.identcrlf");
356 357 358

	git_index_free(index);
}
359 360 361

void test_checkout_crlf__autocrlf_false_no_attrs(void)
{
362
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
363
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
364 365 366 367 368 369 370 371 372 373 374

	cl_repo_set_bool(g_repo, "core.autocrlf", false);

	git_checkout_head(g_repo, &opts);

	check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
	check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
}

void test_checkout_crlf__autocrlf_true_no_attrs(void)
{
375
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
376
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
377 378 379 380 381

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

	git_checkout_head(g_repo, &opts);

382 383
	check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF);
	check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_AS_CRLF);
384 385 386 387
}

void test_checkout_crlf__autocrlf_input_no_attrs(void)
{
388
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
389
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
390 391 392 393 394 395 396 397 398 399 400

	cl_repo_set_string(g_repo, "core.autocrlf", "input");

	git_checkout_head(g_repo, &opts);

	check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
	check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
}

void test_checkout_crlf__autocrlf_false_text_auto_attr(void)
{
401
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
402
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
403 404 405 406 407 408 409

	cl_git_mkfile("./crlf/.gitattributes", "* text=auto\n");

	cl_repo_set_bool(g_repo, "core.autocrlf", false);

	git_checkout_head(g_repo, &opts);

410 411 412 413 414 415 416
	if (GIT_EOL_NATIVE == GIT_EOL_CRLF) {
		check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF);
		check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_AS_CRLF);
	} else {
		check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
		check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
	}
417 418 419 420
}

void test_checkout_crlf__autocrlf_true_text_auto_attr(void)
{
421
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
422
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
423 424 425 426 427 428 429

	cl_git_mkfile("./crlf/.gitattributes", "* text=auto\n");

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

	git_checkout_head(g_repo, &opts);

430 431
	check_file_contents("./crlf/all-lf", ALL_LF_TEXT_AS_CRLF);
	check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_AS_CRLF);
432 433 434 435
}

void test_checkout_crlf__autocrlf_input_text_auto_attr(void)
{
436
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
437
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;
438 439 440 441 442 443 444 445 446 447

	cl_git_mkfile("./crlf/.gitattributes", "* text=auto\n");

	cl_repo_set_string(g_repo, "core.autocrlf", "input");

	git_checkout_head(g_repo, &opts);

	check_file_contents("./crlf/all-lf", ALL_LF_TEXT_RAW);
	check_file_contents("./crlf/all-crlf", ALL_CRLF_TEXT_RAW);
}
448 449 450 451 452 453 454 455 456 457 458 459 460

void test_checkout_crlf__can_write_empty_file(void)
{
	git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT;
	opts.checkout_strategy = GIT_CHECKOUT_FORCE;

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

	git_repository_set_head(g_repo, "refs/heads/empty-files");
	git_checkout_head(g_repo, &opts);

	check_file_contents("./crlf/test1.txt", "");

461
	check_file_contents("./crlf/test2.txt", "test2.txt's content\r\n");
462 463 464

	check_file_contents("./crlf/test3.txt", "");
}