init.c 24.2 KB
Newer Older
1
#include "clar_libgit2.h"
2
#include "fileops.h"
3
#include "repository.h"
4
#include "config.h"
5
#include "path.h"
6
#include "config/config_helpers.h"
7 8 9 10 11 12

enum repo_mode {
	STANDARD_REPOSITORY = 0,
	BARE_REPOSITORY = 1
};

13
static git_repository *_repo = NULL;
14
static git_buf _global_path = GIT_BUF_INIT;
15
static git_buf _tmp_path = GIT_BUF_INIT;
16
static mode_t g_umask = 0;
17 18 19 20

void test_repo_init__initialize(void)
{
	_repo = NULL;
21 22

	/* load umask if not already loaded */
23 24 25
	if (!g_umask) {
		g_umask = p_umask(022);
		(void)p_umask(g_umask);
26
	}
27 28 29 30 31 32 33 34 35 36

    git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
		&_global_path);
}

void test_repo_init__cleanup(void)
{
	git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
		_global_path.ptr);
	git_buf_free(&_global_path);
37 38 39 40

	if (_tmp_path.size > 0 && git_path_isdir(_tmp_path.ptr))
		git_futils_rmdir_r(_tmp_path.ptr, NULL, GIT_RMDIR_REMOVE_FILES);
	git_buf_free(&_tmp_path);
41 42 43 44 45
}

static void cleanup_repository(void *path)
{
	git_repository_free(_repo);
46 47
	_repo = NULL;

48 49 50 51 52 53 54 55 56 57 58
	cl_fixture_cleanup((const char *)path);
}

static void ensure_repository_init(
	const char *working_directory,
	int is_bare,
	const char *expected_path_repository,
	const char *expected_working_directory)
{
	const char *workdir;

59 60
	cl_assert(!git_path_isdir(working_directory));

61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77
	cl_git_pass(git_repository_init(&_repo, working_directory, is_bare));

	workdir = git_repository_workdir(_repo);
	if (workdir != NULL || expected_working_directory != NULL) {
		cl_assert(
			git__suffixcmp(workdir, expected_working_directory) == 0
		);
	}

	cl_assert(
		git__suffixcmp(git_repository_path(_repo), expected_path_repository) == 0
	);

	cl_assert(git_repository_is_bare(_repo) == is_bare);

#ifdef GIT_WIN32
	if (!is_bare) {
78 79
		DWORD fattrs = GetFileAttributes(git_repository_path(_repo));
		cl_assert((fattrs & FILE_ATTRIBUTE_HIDDEN) != 0);
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
	}
#endif

	cl_assert(git_repository_is_empty(_repo));
}

void test_repo_init__standard_repo(void)
{
	cl_set_cleanup(&cleanup_repository, "testrepo");
	ensure_repository_init("testrepo/", 0, "testrepo/.git/", "testrepo/");
}

void test_repo_init__standard_repo_noslash(void)
{
	cl_set_cleanup(&cleanup_repository, "testrepo");
	ensure_repository_init("testrepo", 0, "testrepo/.git/", "testrepo/");
}

void test_repo_init__bare_repo(void)
{
	cl_set_cleanup(&cleanup_repository, "testrepo.git");
	ensure_repository_init("testrepo.git/", 1, "testrepo.git/", NULL);
}

void test_repo_init__bare_repo_noslash(void)
{
	cl_set_cleanup(&cleanup_repository, "testrepo.git");
	ensure_repository_init("testrepo.git", 1, "testrepo.git/", NULL);
}

110 111
void test_repo_init__bare_repo_escaping_current_workdir(void)
{
112
	git_buf path_repository = GIT_BUF_INIT;
113
	git_buf path_current_workdir = GIT_BUF_INIT;
114

115
	cl_git_pass(git_path_prettify_dir(&path_current_workdir, ".", NULL));
116

117
	cl_git_pass(git_buf_joinpath(&path_repository, git_buf_cstr(&path_current_workdir), "a/b/c"));
118
	cl_git_pass(git_futils_mkdir_r(git_buf_cstr(&path_repository), GIT_DIR_MODE));
119

120 121
	/* Change the current working directory */
	cl_git_pass(chdir(git_buf_cstr(&path_repository)));
122

123 124 125
	/* Initialize a bare repo with a relative path escaping out of the current working directory */
	cl_git_pass(git_repository_init(&_repo, "../d/e.git", 1));
	cl_git_pass(git__suffixcmp(git_repository_path(_repo), "/a/b/d/e.git/"));
126

127
	git_repository_free(_repo);
128

129 130
	/* Open a bare repo with a relative path escaping out of the current working directory */
	cl_git_pass(git_repository_open(&_repo, "../d/e.git"));
131

132
	cl_git_pass(chdir(git_buf_cstr(&path_current_workdir)));
133

134 135
	git_buf_free(&path_current_workdir);
	git_buf_free(&path_repository);
136

137 138
	cleanup_repository("a");
}
139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173

void test_repo_init__reinit_bare_repo(void)
{
	cl_set_cleanup(&cleanup_repository, "reinit.git");

	/* Initialize the repository */
	cl_git_pass(git_repository_init(&_repo, "reinit.git", 1));
	git_repository_free(_repo);

	/* Reinitialize the repository */
	cl_git_pass(git_repository_init(&_repo, "reinit.git", 1));
}

void test_repo_init__reinit_too_recent_bare_repo(void)
{
	git_config *config;

	/* Initialize the repository */
	cl_git_pass(git_repository_init(&_repo, "reinit.git", 1));
	git_repository_config(&config, _repo);

	/*
	 * Hack the config of the repository to make it look like it has
	 * been created by a recenter version of git/libgit2
	 */
	cl_git_pass(git_config_set_int32(config, "core.repositoryformatversion", 42));

	git_config_free(config);
	git_repository_free(_repo);

	/* Try to reinitialize the repository */
	cl_git_fail(git_repository_init(&_repo, "reinit.git", 1));

	cl_fixture_cleanup("reinit.git");
}
174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197

void test_repo_init__additional_templates(void)
{
	git_buf path = GIT_BUF_INIT;

	cl_set_cleanup(&cleanup_repository, "tester");

	ensure_repository_init("tester", 0, "tester/.git/", "tester/");

	cl_git_pass(
		git_buf_joinpath(&path, git_repository_path(_repo), "description"));
	cl_assert(git_path_isfile(git_buf_cstr(&path)));

	cl_git_pass(
		git_buf_joinpath(&path, git_repository_path(_repo), "info/exclude"));
	cl_assert(git_path_isfile(git_buf_cstr(&path)));

	cl_git_pass(
		git_buf_joinpath(&path, git_repository_path(_repo), "hooks"));
	cl_assert(git_path_isdir(git_buf_cstr(&path)));
	/* won't confirm specific contents of hooks dir since it may vary */

	git_buf_free(&path);
}
198

199 200
static void assert_config_entry_on_init_bytype(
	const char *config_key, int expected_value, bool is_bare)
201 202
{
	git_config *config;
203 204 205
	int error, current_value;
	const char *repo_path = is_bare ?
		"config_entry/test.bare.git" : "config_entry/test.non.bare.git";
206

207
	cl_set_cleanup(&cleanup_repository, "config_entry");
208

209
	cl_git_pass(git_repository_init(&_repo, repo_path, is_bare));
210

211 212 213
	cl_git_pass(git_repository_config(&config, _repo));
	error = git_config_get_bool(&current_value, config, config_key);
	git_config_free(config);
214

215
	if (expected_value >= 0) {
216
		cl_assert_equal_i(0, error);
217 218 219 220 221
		cl_assert_equal_i(expected_value, current_value);
	} else {
		cl_assert_equal_i(expected_value, error);
	}
}
222

223 224
static void assert_config_entry_on_init(
	const char *config_key, int expected_value)
225 226 227 228 229 230 231
{
	assert_config_entry_on_init_bytype(config_key, expected_value, true);
	git_repository_free(_repo);

	assert_config_entry_on_init_bytype(config_key, expected_value, false);
}

232 233
void test_repo_init__detect_filemode(void)
{
234
	assert_config_entry_on_init("core.filemode", cl_is_chmod_supported());
235
}
236 237 238

void test_repo_init__detect_ignorecase(void)
{
239 240 241 242 243 244 245 246 247
	struct stat st;
	bool found_without_match;

	cl_git_write2file("testCAPS", "whatever\n", 0, O_CREAT | O_WRONLY, 0666);
	found_without_match = (p_stat("Testcaps", &st) == 0);
	cl_must_pass(p_unlink("testCAPS"));

	assert_config_entry_on_init(
		"core.ignorecase", found_without_match ? true : GIT_ENOTFOUND);
248 249
}

250 251
void test_repo_init__detect_precompose_unicode_required(void)
{
252
#ifdef GIT_USE_ICONV
253 254 255 256 257 258 259 260 261
	char *composed = "ḱṷṓn", *decomposed = "ḱṷṓn";
	struct stat st;
	bool found_with_nfd;

	cl_git_write2file(composed, "whatever\n", 0, O_CREAT | O_WRONLY, 0666);
	found_with_nfd = (p_stat(decomposed, &st) == 0);
	cl_must_pass(p_unlink(composed));

	assert_config_entry_on_init("core.precomposeunicode", found_with_nfd);
262 263 264 265 266
#else
	assert_config_entry_on_init("core.precomposeunicode", GIT_ENOTFOUND);
#endif
}

267 268 269 270 271 272
void test_repo_init__reinit_doesnot_overwrite_ignorecase(void)
{
	git_config *config;
	int current_value;

	/* Init a new repo */
273
	cl_set_cleanup(&cleanup_repository, "not.overwrite.git");
274
	cl_git_pass(git_repository_init(&_repo, "not.overwrite.git", 1));
275 276 277 278 279 280

	/* Change the "core.ignorecase" config value to something unlikely */
	git_repository_config(&config, _repo);
	git_config_set_int32(config, "core.ignorecase", 42);
	git_config_free(config);
	git_repository_free(_repo);
281
	_repo = NULL;
282 283

	/* Reinit the repository */
284
	cl_git_pass(git_repository_init(&_repo, "not.overwrite.git", 1));
285 286 287 288 289
	git_repository_config(&config, _repo);

	/* Ensure the "core.ignorecase" config value hasn't been updated */
	cl_git_pass(git_config_get_int32(&current_value, config, "core.ignorecase"));
	cl_assert_equal_i(42, current_value);
290 291 292

	git_config_free(config);
}
293

294 295
void test_repo_init__reinit_overwrites_filemode(void)
{
296
	int expected = cl_is_chmod_supported(), current_value;
297

298 299 300
	/* Init a new repo */
	cl_set_cleanup(&cleanup_repository, "overwrite.git");
	cl_git_pass(git_repository_init(&_repo, "overwrite.git", 1));
301

302
	/* Change the "core.filemode" config value to something unlikely */
303 304
	cl_repo_set_bool(_repo, "core.filemode", !expected);

305 306
	git_repository_free(_repo);
	_repo = NULL;
307

308 309
	/* Reinit the repository */
	cl_git_pass(git_repository_init(&_repo, "overwrite.git", 1));
310

311
	/* Ensure the "core.filemode" config value has been reset */
312
	current_value = cl_repo_get_bool(_repo, "core.filemode");
313
	cl_assert_equal_i(expected, current_value);
314 315
}

316 317 318
void test_repo_init__sets_logAllRefUpdates_according_to_type_of_repository(void)
{
	assert_config_entry_on_init_bytype("core.logallrefupdates", GIT_ENOTFOUND, true);
319
	git_repository_free(_repo);
320 321
	assert_config_entry_on_init_bytype("core.logallrefupdates", true, false);
}
322

323 324 325 326 327 328 329 330 331 332 333
void test_repo_init__empty_template_path(void)
{
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
	opts.template_path = "";

	cl_git_pass(git_futils_mkdir("foo", 0755, 0));
	cl_git_pass(git_repository_init_ext(&_repo, "foo", &opts));

	cleanup_repository("foo");
}

334 335
void test_repo_init__extended_0(void)
{
336
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
337 338 339 340 341

	/* without MKDIR this should fail */
	cl_git_fail(git_repository_init_ext(&_repo, "extended", &opts));

	/* make the directory first, then it should succeed */
342
	cl_git_pass(git_futils_mkdir("extended", 0775, 0));
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357
	cl_git_pass(git_repository_init_ext(&_repo, "extended", &opts));

	cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "/extended/"));
	cl_assert(!git__suffixcmp(git_repository_path(_repo), "/extended/.git/"));
	cl_assert(!git_repository_is_bare(_repo));
	cl_assert(git_repository_is_empty(_repo));

	cleanup_repository("extended");
}

void test_repo_init__extended_1(void)
{
	git_reference *ref;
	git_remote *remote;
	struct stat st;
358
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378

	opts.flags = GIT_REPOSITORY_INIT_MKPATH |
		GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;
	opts.mode = GIT_REPOSITORY_INIT_SHARED_GROUP;
	opts.workdir_path = "../c_wd";
	opts.description = "Awesomest test repository evah";
	opts.initial_head = "development";
	opts.origin_url = "https://github.com/libgit2/libgit2.git";

	cl_git_pass(git_repository_init_ext(&_repo, "root/b/c.git", &opts));

	cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "/c_wd/"));
	cl_assert(!git__suffixcmp(git_repository_path(_repo), "/c.git/"));
	cl_assert(git_path_isfile("root/b/c_wd/.git"));
	cl_assert(!git_repository_is_bare(_repo));
	/* repo will not be counted as empty because we set head to "development" */
	cl_assert(!git_repository_is_empty(_repo));

	cl_git_pass(git_path_lstat(git_repository_path(_repo), &st));
	cl_assert(S_ISDIR(st.st_mode));
379
	if (cl_is_chmod_supported())
380 381 382
		cl_assert((S_ISGID & st.st_mode) == S_ISGID);
	else
		cl_assert((S_ISGID & st.st_mode) == 0);
383 384 385

	cl_git_pass(git_reference_lookup(&ref, _repo, "HEAD"));
	cl_assert(git_reference_type(ref) == GIT_REF_SYMBOLIC);
386
	cl_assert_equal_s("refs/heads/development", git_reference_symbolic_target(ref));
387 388
	git_reference_free(ref);

389
	cl_git_pass(git_remote_lookup(&remote, _repo, "origin"));
390 391 392 393 394 395 396 397
	cl_assert_equal_s("origin", git_remote_name(remote));
	cl_assert_equal_s(opts.origin_url, git_remote_url(remote));
	git_remote_free(remote);

	git_repository_free(_repo);
	cl_fixture_cleanup("root");
}

398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
void test_repo_init__relative_gitdir(void)
{
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
	git_buf dot_git_content = GIT_BUF_INIT;

	opts.workdir_path = "../c_wd";
	opts.flags =
		GIT_REPOSITORY_INIT_MKPATH |
		GIT_REPOSITORY_INIT_RELATIVE_GITLINK |
		GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;

	/* make the directory first, then it should succeed */
	cl_git_pass(git_repository_init_ext(&_repo, "root/b/my_repository", &opts));

	cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "root/b/c_wd/"));
	cl_assert(!git__suffixcmp(git_repository_path(_repo), "root/b/my_repository/"));
	cl_assert(!git_repository_is_bare(_repo));
	cl_assert(git_repository_is_empty(_repo));

	/* Verify that the gitlink and worktree entries are relative */

	/* Verify worktree */
420
	assert_config_entry_value(_repo, "core.worktree", "../c_wd/");
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446

	/* Verify gitlink */
	cl_git_pass(git_futils_readbuffer(&dot_git_content, "root/b/c_wd/.git"));
	cl_assert_equal_s("gitdir: ../my_repository/", dot_git_content.ptr);

	git_buf_free(&dot_git_content);
	cleanup_repository("root");
}

void test_repo_init__relative_gitdir_2(void)
{
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
	git_buf dot_git_content = GIT_BUF_INIT;
	git_buf full_path = GIT_BUF_INIT;

	cl_git_pass(git_path_prettify(&full_path, ".", NULL));
	cl_git_pass(git_buf_joinpath(&full_path, full_path.ptr, "root/b/c_wd"));

	opts.workdir_path = full_path.ptr;
	opts.flags =
		GIT_REPOSITORY_INIT_MKPATH |
		GIT_REPOSITORY_INIT_RELATIVE_GITLINK |
		GIT_REPOSITORY_INIT_NO_DOTGIT_DIR;

	/* make the directory first, then it should succeed */
	cl_git_pass(git_repository_init_ext(&_repo, "root/b/my_repository", &opts));
447
	git_buf_free(&full_path);
448 449 450 451 452 453 454 455 456

	cl_assert(!git__suffixcmp(git_repository_workdir(_repo), "root/b/c_wd/"));
	cl_assert(!git__suffixcmp(git_repository_path(_repo), "root/b/my_repository/"));
	cl_assert(!git_repository_is_bare(_repo));
	cl_assert(git_repository_is_empty(_repo));

	/* Verify that the gitlink and worktree entries are relative */

	/* Verify worktree */
457
	assert_config_entry_value(_repo, "core.worktree", "../c_wd/");
458 459 460 461 462 463 464 465 466

	/* Verify gitlink */
	cl_git_pass(git_futils_readbuffer(&dot_git_content, "root/b/c_wd/.git"));
	cl_assert_equal_s("gitdir: ../my_repository/", dot_git_content.ptr);

	git_buf_free(&dot_git_content);
	cleanup_repository("root");
}

Russell Belfer committed
467 468
#define CLEAR_FOR_CORE_FILEMODE(M) ((M) &= ~0177)

469 470 471 472
static void assert_hooks_match(
	const char *template_dir,
	const char *repo_dir,
	const char *hook_path,
473
	bool core_filemode)
474
{
475 476
	git_buf expected = GIT_BUF_INIT;
	git_buf actual = GIT_BUF_INIT;
477 478 479 480 481 482 483 484
	struct stat expected_st, st;

	cl_git_pass(git_buf_joinpath(&expected, template_dir, hook_path));
	cl_git_pass(git_path_lstat(expected.ptr, &expected_st));

	cl_git_pass(git_buf_joinpath(&actual, repo_dir, hook_path));
	cl_git_pass(git_path_lstat(actual.ptr, &st));

485
	cl_assert(expected_st.st_size == st.st_size);
486

Russell Belfer committed
487 488 489 490
	if (GIT_MODE_TYPE(expected_st.st_mode) != GIT_FILEMODE_LINK) {
		mode_t expected_mode =
			GIT_MODE_TYPE(expected_st.st_mode) |
			(GIT_PERMS_FOR_WRITE(expected_st.st_mode) & ~g_umask);
491

Russell Belfer committed
492 493 494 495
		if (!core_filemode) {
			CLEAR_FOR_CORE_FILEMODE(expected_mode);
			CLEAR_FOR_CORE_FILEMODE(st.st_mode);
		}
496

Russell Belfer committed
497 498
		cl_assert_equal_i_fmt(expected_mode, st.st_mode, "%07o");
	}
499

500 501 502 503
	git_buf_free(&expected);
	git_buf_free(&actual);
}

504 505 506
static void assert_mode_seems_okay(
	const char *base, const char *path,
	git_filemode_t expect_mode, bool expect_setgid, bool core_filemode)
507 508 509 510 511 512 513 514
{
	git_buf full = GIT_BUF_INIT;
	struct stat st;

	cl_git_pass(git_buf_joinpath(&full, base, path));
	cl_git_pass(git_path_lstat(full.ptr, &st));
	git_buf_free(&full);

515
	if (!core_filemode) {
Russell Belfer committed
516 517
		CLEAR_FOR_CORE_FILEMODE(expect_mode);
		CLEAR_FOR_CORE_FILEMODE(st.st_mode);
518 519 520
		expect_setgid = false;
	}

521 522
	if (S_ISGID != 0)
		cl_assert_equal_b(expect_setgid, (st.st_mode & S_ISGID) != 0);
523

Russell Belfer committed
524
	cl_assert_equal_b(
525
		GIT_PERMS_IS_EXEC(expect_mode), GIT_PERMS_IS_EXEC(st.st_mode));
526

Russell Belfer committed
527 528
	cl_assert_equal_i_fmt(
		GIT_MODE_TYPE(expect_mode), GIT_MODE_TYPE(st.st_mode), "%07o");
529 530
}

531 532
static const char *template_sandbox(const char *name)
{
533 534
	git_buf hooks_path = GIT_BUF_INIT, link_path = GIT_BUF_INIT,
		dotfile_path = GIT_BUF_INIT;
535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551
	const char *path = cl_fixture(name);

	cl_fixture_sandbox(name);

	/* create a symlink from link.sample to update.sample if the filesystem
	 * supports it.
	 */

	cl_git_pass(git_buf_joinpath(&hooks_path, name, "hooks"));
	cl_git_pass(git_buf_joinpath(&link_path, hooks_path.ptr, "link.sample"));

#ifdef GIT_WIN32
	cl_git_mkfile(link_path.ptr, "#!/bin/sh\necho hello, world\n");
#else
	cl_must_pass(symlink("update.sample", link_path.ptr));
#endif

552 553 554 555 556 557
	/* create a file starting with a dot */
	cl_git_pass(git_buf_joinpath(&dotfile_path, hooks_path.ptr, ".dotfile"));
	cl_git_mkfile(dotfile_path.ptr, "something\n");
	git_buf_free(&dotfile_path);

	git_buf_free(&dotfile_path);
558 559 560 561 562 563
	git_buf_free(&link_path);
	git_buf_free(&hooks_path);

	return path;
}

564 565 566 567 568 569
static void configure_templatedir(const char *template_path)
{
	git_buf config_path = GIT_BUF_INIT;
	git_buf config_data = GIT_BUF_INIT;

    cl_git_pass(git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH,
570 571
		GIT_CONFIG_LEVEL_GLOBAL, &_tmp_path));
	cl_git_pass(git_buf_puts(&_tmp_path, ".tmp"));
572
	cl_git_pass(git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH,
573
		GIT_CONFIG_LEVEL_GLOBAL, _tmp_path.ptr));
574

575
	cl_must_pass(p_mkdir(_tmp_path.ptr, 0777));
576

577
	cl_git_pass(git_buf_joinpath(&config_path, _tmp_path.ptr, ".gitconfig"));
578 579 580 581 582 583 584 585 586 587 588

	cl_git_pass(git_buf_printf(&config_data,
		"[init]\n\ttemplatedir = \"%s\"\n", template_path));

	cl_git_mkfile(config_path.ptr, config_data.ptr);

	git_buf_free(&config_path);
	git_buf_free(&config_data);
}

static void validate_templates(git_repository *repo, const char *template_path)
589
{
590 591
	git_buf template_description = GIT_BUF_INIT;
	git_buf repo_description = GIT_BUF_INIT;
592 593
	git_buf expected = GIT_BUF_INIT;
	git_buf actual = GIT_BUF_INIT;
594
	int filemode;
595

596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615
	cl_git_pass(git_buf_joinpath(&template_description, template_path,
		"description"));
	cl_git_pass(git_buf_joinpath(&repo_description, git_repository_path(repo),
		"description"));

	cl_git_pass(git_futils_readbuffer(&expected, template_description.ptr));
	cl_git_pass(git_futils_readbuffer(&actual, repo_description.ptr));

	cl_assert_equal_s(expected.ptr, actual.ptr);

	filemode = cl_repo_get_bool(repo, "core.filemode");

	assert_hooks_match(
		template_path, git_repository_path(repo),
		"hooks/update.sample", filemode);

	assert_hooks_match(
		template_path, git_repository_path(repo),
		"hooks/link.sample", filemode);

616 617 618 619
	assert_hooks_match(
		template_path, git_repository_path(repo),
		"hooks/.dotfile", filemode);

620 621 622 623 624 625 626 627 628 629
	git_buf_free(&expected);
	git_buf_free(&actual);
	git_buf_free(&repo_description);
	git_buf_free(&template_description);
}

void test_repo_init__external_templates_specified_in_options(void)
{
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;

630
	cl_set_cleanup(&cleanup_repository, "templated.git");
631
	template_sandbox("template");
632 633 634

	opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_BARE |
		GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
635
	opts.template_path = "template";
636 637 638 639

	cl_git_pass(git_repository_init_ext(&_repo, "templated.git", &opts));

	cl_assert(git_repository_is_bare(_repo));
640

641 642
	cl_assert(!git__suffixcmp(git_repository_path(_repo), "/templated.git/"));

643 644 645
	validate_templates(_repo, "template");
	cl_fixture_cleanup("template");
}
646

647 648 649
void test_repo_init__external_templates_specified_in_config(void)
{
	git_buf template_path = GIT_BUF_INIT;
650

651
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
652

653 654
	cl_set_cleanup(&cleanup_repository, "templated.git");
	template_sandbox("template");
655

656 657
	cl_git_pass(git_buf_joinpath(&template_path, clar_sandbox_path(),
		"template"));
658

659 660 661 662 663 664 665 666
	configure_templatedir(template_path.ptr);

	opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_BARE |
		GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;

	cl_git_pass(git_repository_init_ext(&_repo, "templated.git", &opts));

	validate_templates(_repo, "template");
667
	cl_fixture_cleanup("template");
668 669

	git_buf_free(&template_path);
670 671
}

672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698
void test_repo_init__external_templates_with_leading_dot(void)
{
	git_buf template_path = GIT_BUF_INIT;

	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;

	cl_set_cleanup(&cleanup_repository, "templated.git");
	template_sandbox("template");

	cl_must_pass(p_rename("template", ".template_with_leading_dot"));

	cl_git_pass(git_buf_joinpath(&template_path, clar_sandbox_path(),
		".template_with_leading_dot"));

	configure_templatedir(template_path.ptr);

	opts.flags = GIT_REPOSITORY_INIT_MKPATH | GIT_REPOSITORY_INIT_BARE |
		GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;

	cl_git_pass(git_repository_init_ext(&_repo, "templated.git", &opts));

	validate_templates(_repo, ".template_with_leading_dot");
	cl_fixture_cleanup(".template_with_leading_dot");

	git_buf_free(&template_path);
}

699 700 701
void test_repo_init__extended_with_template_and_shared_mode(void)
{
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
702
	int filemode = true;
703
	const char *repo_path = NULL;
704 705

	cl_set_cleanup(&cleanup_repository, "init_shared_from_tpl");
706
	template_sandbox("template");
707 708 709

	opts.flags = GIT_REPOSITORY_INIT_MKPATH |
		GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE;
710
	opts.template_path = "template";
711 712 713 714 715 716 717
	opts.mode = GIT_REPOSITORY_INIT_SHARED_GROUP;

	cl_git_pass(git_repository_init_ext(&_repo, "init_shared_from_tpl", &opts));

	cl_assert(!git_repository_is_bare(_repo));
	cl_assert(!git__suffixcmp(git_repository_path(_repo), "/init_shared_from_tpl/.git/"));

718
	filemode = cl_repo_get_bool(_repo, "core.filemode");
719

720 721 722 723 724 725 726
	repo_path = git_repository_path(_repo);
	assert_mode_seems_okay(repo_path, "hooks",
		GIT_FILEMODE_TREE | GIT_REPOSITORY_INIT_SHARED_GROUP, true, filemode);
	assert_mode_seems_okay(repo_path, "info",
		GIT_FILEMODE_TREE | GIT_REPOSITORY_INIT_SHARED_GROUP, true, filemode);
	assert_mode_seems_okay(repo_path, "description",
		GIT_FILEMODE_BLOB, false, filemode);
727

728
	validate_templates(_repo, "template");
729 730

	cl_fixture_cleanup("template");
731
}
732 733 734 735 736

void test_repo_init__can_reinit_an_initialized_repository(void)
{
	git_repository *reinit;

737 738
	cl_set_cleanup(&cleanup_repository, "extended");

739
	cl_git_pass(git_futils_mkdir("extended", 0775, 0));
740 741 742 743 744 745 746 747
	cl_git_pass(git_repository_init(&_repo, "extended", false));

	cl_git_pass(git_repository_init(&reinit, "extended", false));

	cl_assert_equal_s(git_repository_path(_repo), git_repository_path(reinit));

	git_repository_free(reinit);
}
748 749 750 751 752 753 754 755 756 757

void test_repo_init__init_with_initial_commit(void)
{
	git_index *index;

	cl_set_cleanup(&cleanup_repository, "committed");

	/* Initialize the repository */
	cl_git_pass(git_repository_init(&_repo, "committed", 0));

758
	/* Index will be automatically created when requested for a new repo */
759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776
	cl_git_pass(git_repository_index(&index, _repo));

	/* Create a file so we can commit it
	 *
	 * If you are writing code outside the test suite, you can create this
	 * file any way that you like, such as:
	 *      FILE *fp = fopen("committed/file.txt", "w");
	 *      fputs("some stuff\n", fp);
	 *      fclose(fp);
	 * We like to use the help functions because they do error detection
	 * in a way that's easily compatible with our test suite.
	 */
	cl_git_mkfile("committed/file.txt", "some stuff\n");

	/* Add file to the index */
	cl_git_pass(git_index_add_bypath(index, "file.txt"));
	cl_git_pass(git_index_write(index));

777 778 779 780 781
	/* Intentionally not using cl_repo_commit_from_index here so this code
	 * can be used as an example of how an initial commit is typically
	 * made to a repository...
	 */

782 783 784 785 786 787 788 789 790 791 792
	/* Make sure we're ready to use git_signature_default :-) */
	{
		git_config *cfg, *local;
		cl_git_pass(git_repository_config(&cfg, _repo));
		cl_git_pass(git_config_open_level(&local, cfg, GIT_CONFIG_LEVEL_LOCAL));
		cl_git_pass(git_config_set_string(local, "user.name", "Test User"));
		cl_git_pass(git_config_set_string(local, "user.email", "t@example.com"));
		git_config_free(local);
		git_config_free(cfg);
	}

793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812
	/* Create a commit with the new contents of the index */
	{
		git_signature *sig;
		git_oid tree_id, commit_id;
		git_tree *tree;

		cl_git_pass(git_signature_default(&sig, _repo));
		cl_git_pass(git_index_write_tree(&tree_id, index));
		cl_git_pass(git_tree_lookup(&tree, _repo, &tree_id));

		cl_git_pass(git_commit_create_v(
			&commit_id, _repo, "HEAD", sig, sig,
			NULL, "First", tree, 0));

		git_tree_free(tree);
		git_signature_free(sig);
	}

	git_index_free(index);
}
813 814 815 816 817 818 819 820

void test_repo_init__at_filesystem_root(void)
{
	git_repository *repo;
	const char *sandbox = clar_sandbox_path();
	git_buf root = GIT_BUF_INIT;
	int root_len;

821
	if (!cl_is_env_set("GITTEST_INVASIVE_FS_STRUCTURE"))
822 823 824 825 826 827 828 829 830 831 832 833 834 835 836 837 838
		cl_skip();

	root_len = git_path_root(sandbox);
	cl_assert(root_len >= 0);

	git_buf_put(&root, sandbox, root_len+1);
	git_buf_joinpath(&root, root.ptr, "libgit2_test_dir");

	cl_assert(!git_path_exists(root.ptr));

	cl_git_pass(git_repository_init(&repo, root.ptr, 0));
	cl_assert(git_path_isdir(root.ptr));
	cl_git_pass(git_futils_rmdir_r(root.ptr, NULL, GIT_RMDIR_REMOVE_FILES));

	git_buf_free(&root);
	git_repository_free(repo);
}