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

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

14
static git_repository *g_repo = NULL;
15
static git_str g_global_path = GIT_STR_INIT;
16 17 18

void test_repo_init__initialize(void)
{
19
	g_repo = NULL;
20

21
	git_libgit2_opts(GIT_OPT_GET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
22
			 &g_global_path);
23 24 25 26 27
}

void test_repo_init__cleanup(void)
{
	git_libgit2_opts(GIT_OPT_SET_SEARCH_PATH, GIT_CONFIG_LEVEL_GLOBAL,
28
		g_global_path.ptr);
29
	git_str_dispose(&g_global_path);
30

31
	cl_fixture_cleanup("tmp_global_path");
32 33 34 35
}

static void cleanup_repository(void *path)
{
36 37
	git_repository_free(g_repo);
	g_repo = NULL;
38

39 40 41 42 43 44 45 46 47 48 49
	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;

50
	cl_assert(!git_fs_path_isdir(working_directory));
51

52
	cl_git_pass(git_repository_init(&g_repo, working_directory, is_bare));
53

54
	workdir = git_repository_workdir(g_repo);
55 56 57 58 59 60 61
	if (workdir != NULL || expected_working_directory != NULL) {
		cl_assert(
			git__suffixcmp(workdir, expected_working_directory) == 0
		);
	}

	cl_assert(
62
		git__suffixcmp(git_repository_path(g_repo), expected_path_repository) == 0
63 64
	);

65
	cl_assert(git_repository_is_bare(g_repo) == is_bare);
66 67 68

#ifdef GIT_WIN32
	if (!is_bare) {
69
		DWORD fattrs = GetFileAttributes(git_repository_path(g_repo));
70
		cl_assert((fattrs & FILE_ATTRIBUTE_HIDDEN) != 0);
71 72 73
	}
#endif

74
	cl_assert(git_repository_is_empty(g_repo));
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
}

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

101 102
void test_repo_init__bare_repo_escaping_current_workdir(void)
{
103 104
	git_str path_repository = GIT_STR_INIT;
	git_str path_current_workdir = GIT_STR_INIT;
105

106
	cl_git_pass(git_fs_path_prettify_dir(&path_current_workdir, ".", NULL));
107

108 109
	cl_git_pass(git_str_joinpath(&path_repository, git_str_cstr(&path_current_workdir), "a/b/c"));
	cl_git_pass(git_futils_mkdir_r(git_str_cstr(&path_repository), GIT_DIR_MODE));
110

111
	/* Change the current working directory */
112
	cl_git_pass(chdir(git_str_cstr(&path_repository)));
113

114
	/* Initialize a bare repo with a relative path escaping out of the current working directory */
115 116
	cl_git_pass(git_repository_init(&g_repo, "../d/e.git", 1));
	cl_git_pass(git__suffixcmp(git_repository_path(g_repo), "/a/b/d/e.git/"));
117

118 119
	git_repository_free(g_repo);
	g_repo = NULL;
120

121
	/* Open a bare repo with a relative path escaping out of the current working directory */
122
	cl_git_pass(git_repository_open(&g_repo, "../d/e.git"));
123

124
	cl_git_pass(chdir(git_str_cstr(&path_current_workdir)));
125

126 127
	git_str_dispose(&path_current_workdir);
	git_str_dispose(&path_repository);
128

129 130
	cleanup_repository("a");
}
131 132 133 134 135 136

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

	/* Initialize the repository */
137 138 139
	cl_git_pass(git_repository_init(&g_repo, "reinit.git", 1));
	git_repository_free(g_repo);
	g_repo = NULL;
140 141

	/* Reinitialize the repository */
142
	cl_git_pass(git_repository_init(&g_repo, "reinit.git", 1));
143 144
}

145
void test_repo_init__reinit_nondefault_version(void)
146 147 148
{
	git_config *config;

149 150
	cl_set_cleanup(&cleanup_repository, "reinit.git");

151
	/* Initialize the repository */
152 153
	cl_git_pass(git_repository_init(&g_repo, "reinit.git", 1));
	git_repository_config(&config, g_repo);
154

155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
	/* Set the config to a supported but not default version */
	cl_repo_set_string(g_repo, "core.repositoryformatversion", "1");
	git_config_free(config);
	git_repository_free(g_repo);
	g_repo = NULL;

	/* Try to reinitialize the repository */
	cl_git_pass(git_repository_init(&g_repo, "reinit.git", 1));
	cl_assert_equal_i(1, cl_repo_get_int(g_repo, "core.repositoryformatversion"));

	cl_fixture_cleanup("reinit.git");
}

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

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

175 176
	/*
	 * Hack the config of the repository to make it look like it has
177
	 * been created by a too new and unsupported version of git/libgit2
178
	 */
179
	cl_repo_set_string(g_repo, "core.repositoryformatversion", "42");
180 181
	git_repository_free(g_repo);
	g_repo = NULL;
182

183
	/* Try and fail to reinitialize the repository */
184
	cl_git_fail(git_repository_init(&g_repo, "reinit.git", 1));
185 186
	cl_fixture_cleanup("reinit.git");
}
187 188 189

void test_repo_init__additional_templates(void)
{
190
	git_str path = GIT_STR_INIT;
191 192 193 194 195 196

	cl_set_cleanup(&cleanup_repository, "tester");

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

	cl_git_pass(
197
		git_str_joinpath(&path, git_repository_path(g_repo), "description"));
198
	cl_assert(git_fs_path_isfile(git_str_cstr(&path)));
199 200

	cl_git_pass(
201
		git_str_joinpath(&path, git_repository_path(g_repo), "info/exclude"));
202
	cl_assert(git_fs_path_isfile(git_str_cstr(&path)));
203 204

	cl_git_pass(
205
		git_str_joinpath(&path, git_repository_path(g_repo), "hooks"));
206
	cl_assert(git_fs_path_isdir(git_str_cstr(&path)));
207 208
	/* won't confirm specific contents of hooks dir since it may vary */

209
	git_str_dispose(&path);
210
}
211

212 213
static void assert_config_entry_on_init_bytype(
	const char *config_key, int expected_value, bool is_bare)
214 215
{
	git_config *config;
216 217 218
	int error, current_value;
	const char *repo_path = is_bare ?
		"config_entry/test.bare.git" : "config_entry/test.non.bare.git";
219

220
	cl_set_cleanup(&cleanup_repository, "config_entry");
221

222
	cl_git_pass(git_repository_init(&g_repo, repo_path, is_bare));
223

224
	cl_git_pass(git_repository_config(&config, g_repo));
225 226
	error = git_config_get_bool(&current_value, config, config_key);
	git_config_free(config);
227

228
	if (expected_value >= 0) {
229
		cl_assert_equal_i(0, error);
230 231 232 233 234
		cl_assert_equal_i(expected_value, current_value);
	} else {
		cl_assert_equal_i(expected_value, error);
	}
}
235

236 237
static void assert_config_entry_on_init(
	const char *config_key, int expected_value)
238 239
{
	assert_config_entry_on_init_bytype(config_key, expected_value, true);
240 241
	git_repository_free(g_repo);
	g_repo = NULL;
242 243 244 245

	assert_config_entry_on_init_bytype(config_key, expected_value, false);
}

246 247
void test_repo_init__detect_filemode(void)
{
248
	assert_config_entry_on_init("core.filemode", cl_is_chmod_supported());
249
}
250 251 252

void test_repo_init__detect_ignorecase(void)
{
253 254 255 256 257 258 259 260 261
	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);
262 263
}

264 265 266 267 268 269 270 271
/*
 * Windows: if the filesystem supports symlinks (because we're running
 * as administrator, or because the user has opted into it for normal
 * users) then we can also opt-in explicitly by settings `core.symlinks`
 * in the global config.  Symlinks remain off by default.
 */

void test_repo_init__symlinks_win32_enabled_by_global_config(void)
272
{
273 274 275 276 277 278
#ifndef GIT_WIN32
	cl_skip();
#else
	git_config *config, *repo_config;
	int val;

279
	if (!git_fs_path_supports_symlinks("link"))
280 281 282 283 284 285 286 287
		cl_skip();

	create_tmp_global_config("tmp_global_config", "core.symlinks", "true");

	/*
	 * Create a new repository (can't use `assert_config_on_init` since we
	 * want to examine configuration levels with more granularity.)
	 */
288
	cl_git_pass(git_repository_init(&g_repo, "config_entry/test.non.bare.git", false));
289 290

	/* Ensure that core.symlinks remains set (via the global config). */
291
	cl_git_pass(git_repository_config(&config, g_repo));
292 293 294 295 296 297 298 299 300 301 302 303
	cl_git_pass(git_config_get_bool(&val, config, "core.symlinks"));
	cl_assert_equal_i(1, val);

	/*
	 * Ensure that the repository config does not set core.symlinks.
	 * It should remain inherited.
	 */
	cl_git_pass(git_config_open_level(&repo_config, config, GIT_CONFIG_LEVEL_LOCAL));
	cl_git_fail_with(GIT_ENOTFOUND, git_config_get_bool(&val, repo_config, "core.symlinks"));
	git_config_free(repo_config);

	git_config_free(config);
304 305 306

	git_repository_free(g_repo);
	g_repo = NULL;
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323
#endif
}

void test_repo_init__symlinks_win32_off_by_default(void)
{
#ifndef GIT_WIN32
	cl_skip();
#else
	assert_config_entry_on_init("core.symlinks", false);
#endif
}

void test_repo_init__symlinks_posix_detected(void)
{
#ifdef GIT_WIN32
	cl_skip();
#else
324
	assert_config_entry_on_init(
325
	    "core.symlinks", git_fs_path_supports_symlinks("link") ? GIT_ENOTFOUND : false);
326
#endif
327 328
}

329 330
void test_repo_init__detect_precompose_unicode_required(void)
{
331
#ifdef GIT_USE_ICONV
332 333 334 335 336 337 338 339 340
	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);
341 342 343 344 345
#else
	assert_config_entry_on_init("core.precomposeunicode", GIT_ENOTFOUND);
#endif
}

346 347 348 349 350 351
void test_repo_init__reinit_doesnot_overwrite_ignorecase(void)
{
	git_config *config;
	int current_value;

	/* Init a new repo */
352
	cl_set_cleanup(&cleanup_repository, "not.overwrite.git");
353
	cl_git_pass(git_repository_init(&g_repo, "not.overwrite.git", 1));
354 355

	/* Change the "core.ignorecase" config value to something unlikely */
356
	git_repository_config(&config, g_repo);
357 358
	git_config_set_int32(config, "core.ignorecase", 42);
	git_config_free(config);
359 360
	git_repository_free(g_repo);
	g_repo = NULL;
361 362

	/* Reinit the repository */
363 364
	cl_git_pass(git_repository_init(&g_repo, "not.overwrite.git", 1));
	git_repository_config(&config, g_repo);
365 366 367 368

	/* 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);
369 370 371

	git_config_free(config);
}
372

373 374
void test_repo_init__reinit_overwrites_filemode(void)
{
375
	int expected = cl_is_chmod_supported(), current_value;
376

377 378
	/* Init a new repo */
	cl_set_cleanup(&cleanup_repository, "overwrite.git");
379
	cl_git_pass(git_repository_init(&g_repo, "overwrite.git", 1));
380

381
	/* Change the "core.filemode" config value to something unlikely */
382
	cl_repo_set_bool(g_repo, "core.filemode", !expected);
383

384 385
	git_repository_free(g_repo);
	g_repo = NULL;
386

387
	/* Reinit the repository */
388
	cl_git_pass(git_repository_init(&g_repo, "overwrite.git", 1));
389

390
	/* Ensure the "core.filemode" config value has been reset */
391
	current_value = cl_repo_get_bool(g_repo, "core.filemode");
392
	cl_assert_equal_i(expected, current_value);
393 394
}

395 396 397
void test_repo_init__sets_logAllRefUpdates_according_to_type_of_repository(void)
{
	assert_config_entry_on_init_bytype("core.logallrefupdates", GIT_ENOTFOUND, true);
398
	git_repository_free(g_repo);
399 400
	assert_config_entry_on_init_bytype("core.logallrefupdates", true, false);
}
401 402 403

void test_repo_init__extended_0(void)
{
404
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
405 406

	/* without MKDIR this should fail */
407
	cl_git_fail(git_repository_init_ext(&g_repo, "extended", &opts));
408 409

	/* make the directory first, then it should succeed */
410
	cl_git_pass(git_futils_mkdir("extended", 0775, 0));
411
	cl_git_pass(git_repository_init_ext(&g_repo, "extended", &opts));
412

413 414 415 416
	cl_assert(!git__suffixcmp(git_repository_workdir(g_repo), "/extended/"));
	cl_assert(!git__suffixcmp(git_repository_path(g_repo), "/extended/.git/"));
	cl_assert(!git_repository_is_bare(g_repo));
	cl_assert(git_repository_is_empty(g_repo));
417 418 419 420 421 422 423 424 425

	cleanup_repository("extended");
}

void test_repo_init__extended_1(void)
{
	git_reference *ref;
	git_remote *remote;
	struct stat st;
426
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
427 428 429 430 431 432 433 434 435

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

436
	cl_git_pass(git_repository_init_ext(&g_repo, "root/b/c.git", &opts));
437

438 439
	cl_assert(!git__suffixcmp(git_repository_workdir(g_repo), "/c_wd/"));
	cl_assert(!git__suffixcmp(git_repository_path(g_repo), "/c.git/"));
440
	cl_assert(git_fs_path_isfile("root/b/c_wd/.git"));
441
	cl_assert(!git_repository_is_bare(g_repo));
442
	/* repo will not be counted as empty because we set head to "development" */
443
	cl_assert(!git_repository_is_empty(g_repo));
444

445
	cl_git_pass(git_fs_path_lstat(git_repository_path(g_repo), &st));
446
	cl_assert(S_ISDIR(st.st_mode));
447
	if (cl_is_chmod_supported())
448 449 450
		cl_assert((S_ISGID & st.st_mode) == S_ISGID);
	else
		cl_assert((S_ISGID & st.st_mode) == 0);
451

452
	cl_git_pass(git_reference_lookup(&ref, g_repo, "HEAD"));
453
	cl_assert(git_reference_type(ref) == GIT_REFERENCE_SYMBOLIC);
454
	cl_assert_equal_s("refs/heads/development", git_reference_symbolic_target(ref));
455 456
	git_reference_free(ref);

457
	cl_git_pass(git_remote_lookup(&remote, g_repo, "origin"));
458 459 460 461
	cl_assert_equal_s("origin", git_remote_name(remote));
	cl_assert_equal_s(opts.origin_url, git_remote_url(remote));
	git_remote_free(remote);

462
	git_repository_free(g_repo);
463 464 465
	cl_fixture_cleanup("root");
}

466 467 468
void test_repo_init__relative_gitdir(void)
{
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
469
	git_str dot_git_content = GIT_STR_INIT;
470 471 472 473 474 475 476 477

	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 */
478
	cl_git_pass(git_repository_init_ext(&g_repo, "root/b/my_repository", &opts));
479

480 481 482 483
	cl_assert(!git__suffixcmp(git_repository_workdir(g_repo), "root/b/c_wd/"));
	cl_assert(!git__suffixcmp(git_repository_path(g_repo), "root/b/my_repository/"));
	cl_assert(!git_repository_is_bare(g_repo));
	cl_assert(git_repository_is_empty(g_repo));
484 485 486 487

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

	/* Verify worktree */
488
	assert_config_entry_value(g_repo, "core.worktree", "../c_wd/");
489 490 491 492 493

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

494
	git_str_dispose(&dot_git_content);
495 496 497 498 499 500
	cleanup_repository("root");
}

void test_repo_init__relative_gitdir_2(void)
{
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
501 502
	git_str dot_git_content = GIT_STR_INIT;
	git_str full_path = GIT_STR_INIT;
503

504
	cl_git_pass(git_fs_path_prettify(&full_path, ".", NULL));
505
	cl_git_pass(git_str_joinpath(&full_path, full_path.ptr, "root/b/c_wd"));
506 507 508 509 510 511 512 513

	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 */
514
	cl_git_pass(git_repository_init_ext(&g_repo, "root/b/my_repository", &opts));
515
	git_str_dispose(&full_path);
516

517 518 519 520
	cl_assert(!git__suffixcmp(git_repository_workdir(g_repo), "root/b/c_wd/"));
	cl_assert(!git__suffixcmp(git_repository_path(g_repo), "root/b/my_repository/"));
	cl_assert(!git_repository_is_bare(g_repo));
	cl_assert(git_repository_is_empty(g_repo));
521 522 523 524

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

	/* Verify worktree */
525
	assert_config_entry_value(g_repo, "core.worktree", "../c_wd/");
526 527 528 529 530

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

531
	git_str_dispose(&dot_git_content);
532 533 534
	cleanup_repository("root");
}

535 536 537 538
void test_repo_init__can_reinit_an_initialized_repository(void)
{
	git_repository *reinit;

539 540
	cl_set_cleanup(&cleanup_repository, "extended");

541
	cl_git_pass(git_futils_mkdir("extended", 0775, 0));
542
	cl_git_pass(git_repository_init(&g_repo, "extended", false));
543 544 545

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

546
	cl_assert_equal_s(git_repository_path(g_repo), git_repository_path(reinit));
547 548 549

	git_repository_free(reinit);
}
550 551 552 553 554 555 556 557

void test_repo_init__init_with_initial_commit(void)
{
	git_index *index;

	cl_set_cleanup(&cleanup_repository, "committed");

	/* Initialize the repository */
558
	cl_git_pass(git_repository_init(&g_repo, "committed", 0));
559

560
	/* Index will be automatically created when requested for a new repo */
561
	cl_git_pass(git_repository_index(&index, g_repo));
562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578

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

579 580 581 582 583
	/* 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...
	 */

584 585 586
	/* Make sure we're ready to use git_signature_default :-) */
	{
		git_config *cfg, *local;
587
		cl_git_pass(git_repository_config(&cfg, g_repo));
588 589 590 591 592 593 594
		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);
	}

595 596 597 598 599 600
	/* Create a commit with the new contents of the index */
	{
		git_signature *sig;
		git_oid tree_id, commit_id;
		git_tree *tree;

601
		cl_git_pass(git_signature_default(&sig, g_repo));
602
		cl_git_pass(git_index_write_tree(&tree_id, index));
603
		cl_git_pass(git_tree_lookup(&tree, g_repo, &tree_id));
604 605

		cl_git_pass(git_commit_create_v(
606
			&commit_id, g_repo, "HEAD", sig, sig,
607 608 609 610 611 612 613 614
			NULL, "First", tree, 0));

		git_tree_free(tree);
		git_signature_free(sig);
	}

	git_index_free(index);
}
615 616 617 618 619

void test_repo_init__at_filesystem_root(void)
{
	git_repository *repo;
	const char *sandbox = clar_sandbox_path();
620
	git_str root = GIT_STR_INIT;
621 622
	int root_len;

623
	if (!cl_is_env_set("GITTEST_INVASIVE_FS_STRUCTURE"))
624 625
		cl_skip();

626
	root_len = git_fs_path_root(sandbox);
627 628
	cl_assert(root_len >= 0);

629 630
	git_str_put(&root, sandbox, root_len+1);
	git_str_joinpath(&root, root.ptr, "libgit2_test_dir");
631

632
	cl_assert(!git_fs_path_exists(root.ptr));
633 634

	cl_git_pass(git_repository_init(&repo, root.ptr, 0));
635
	cl_assert(git_fs_path_isdir(root.ptr));
636 637
	cl_git_pass(git_futils_rmdir_r(root.ptr, NULL, GIT_RMDIR_REMOVE_FILES));

638
	git_str_dispose(&root);
639 640
	git_repository_free(repo);
}
641

642
void test_repo_init__nonexisting_directory(void)
643
{
644
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
645 646
	git_repository *repo;

647 648 649 650 651 652 653 654 655 656 657
	/*
	 * If creating a repo with non-existing parent directories, then libgit2
	 * will by default create the complete directory hierarchy if using
	 * `git_repository_init`. Thus, let's use the extended version and not
	 * set the `GIT_REPOSITORY_INIT_MKPATH` flag.
	 */
	cl_git_fail(git_repository_init_ext(&repo, "nonexisting/path", &opts));
}

void test_repo_init__nonexisting_root(void)
{
658
#ifdef GIT_WIN32
659 660 661 662 663 664 665
	git_repository *repo;

	/*
	 * This really only depends on the nonexistence of the Q: drive. We
	 * cannot implement the equivalent test on Unix systems, as there is
	 * fundamentally no path that is disconnected from the root directory.
	 */
666 667 668
	cl_git_fail(git_repository_init(&repo, "Q:/non/existent/path", 0));
	cl_git_fail(git_repository_init(&repo, "Q:\\non\\existent\\path", 0));
#else
669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691
	clar__skip();
#endif
}

void test_repo_init__unwriteable_directory(void)
{
#ifndef GIT_WIN32
	git_repository *repo;

	if (geteuid() == 0)
		clar__skip();

	/*
	 * Create a non-writeable directory so that we cannot create directories
	 * inside of it. The root user has CAP_DAC_OVERRIDE, so he doesn't care
	 * for the directory permissions and thus we need to skip the test if
	 * run as root user.
	 */
	cl_must_pass(p_mkdir("unwriteable", 0444));
	cl_git_fail(git_repository_init(&repo, "unwriteable/repo", 0));
	cl_must_pass(p_rmdir("unwriteable"));
#else
	clar__skip();
692 693
#endif
}
694 695 696 697 698 699 700 701 702

void test_repo_init__defaultbranch_config(void)
{
	git_reference *head;

	cl_set_cleanup(&cleanup_repository, "repo");

	create_tmp_global_config("tmp_global_path", "init.defaultbranch", "my_default_branch");

703 704
	cl_git_pass(git_repository_init(&g_repo, "repo", 0));
	cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));
705 706 707 708 709

	cl_assert_equal_s("refs/heads/my_default_branch", git_reference_symbolic_target(head));

	git_reference_free(head);
}
710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725

void test_repo_init__defaultbranch_config_empty(void)
{
	git_reference *head;

	cl_set_cleanup(&cleanup_repository, "repo");

	create_tmp_global_config("tmp_global_path", "init.defaultbranch", "");

	cl_git_pass(git_repository_init(&g_repo, "repo", 0));
	cl_git_pass(git_reference_lookup(&head, g_repo, "HEAD"));

	cl_assert_equal_s("refs/heads/master", git_reference_symbolic_target(head));

	git_reference_free(head);
}
726 727 728 729

void test_repo_init__longpath(void)
{
#ifdef GIT_WIN32
730
	size_t padding = CONST_STRLEN("objects/pack/pack-.pack.lock") + GIT_OID_MAX_HEXSIZE;
731
	size_t max, i;
732
	git_str path = GIT_STR_INIT;
733 734 735 736 737 738
	git_repository *one = NULL, *two = NULL;

	/*
	 * Files within repositories need to fit within MAX_PATH;
	 * that means a repo path must be at most (MAX_PATH - 18).
	 */
739 740
	cl_git_pass(git_str_puts(&path, clar_sandbox_path()));
	cl_git_pass(git_str_putc(&path, '/'));
741 742 743 744

	max = ((MAX_PATH) - path.size) - padding;

	for (i = 0; i < max - 1; i++)
745
		cl_git_pass(git_str_putc(&path, 'a'));
746 747 748 749

	cl_git_pass(git_repository_init(&one, path.ptr, 1));

	/* Paths longer than this are rejected */
750
	cl_git_pass(git_str_putc(&path, 'z'));
751 752 753 754
	cl_git_fail(git_repository_init(&two, path.ptr, 1));

	git_repository_free(one);
	git_repository_free(two);
755
	git_str_dispose(&path);
756 757
#endif
}