repository.c 68.3 KB
Newer Older
1
/*
Edward Thomson committed
2
 * Copyright (C) the libgit2 contributors. All rights reserved.
3
 *
Vicent Marti committed
4 5
 * This file is part of libgit2, distributed under the GNU GPL v2 with
 * a Linking Exception. For full terms see the included COPYING file.
6
 */
7 8 9

#include "repository.h"

10
#include <ctype.h>
11

12
#include "git2/object.h"
13
#include "git2/sys/repository.h"
14

15 16 17
#include "common.h"
#include "commit.h"
#include "tag.h"
18
#include "blob.h"
19
#include "fileops.h"
20
#include "sysdir.h"
21 22
#include "filebuf.h"
#include "index.h"
23
#include "config.h"
24
#include "refs.h"
25 26
#include "filter.h"
#include "odb.h"
27
#include "refdb.h"
28
#include "remote.h"
Edward Thomson committed
29
#include "merge.h"
30
#include "diff_driver.h"
31
#include "annotated_commit.h"
32
#include "submodule.h"
33
#include "worktree.h"
34 35

#include "strmap.h"
36

37 38 39 40
#ifdef GIT_WIN32
# include "win32/w32_util.h"
#endif

41 42
bool git_repository__fsync_gitdir = false;

43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
static const struct {
    git_repository_item_t parent;
    const char *name;
    bool directory;
} items[] = {
	{ GIT_REPOSITORY_ITEM_GITDIR, NULL, true },
	{ GIT_REPOSITORY_ITEM_WORKDIR, NULL, true },
	{ GIT_REPOSITORY_ITEM_COMMONDIR, NULL, true },
	{ GIT_REPOSITORY_ITEM_GITDIR, "index", false },
	{ GIT_REPOSITORY_ITEM_COMMONDIR, "objects", true },
	{ GIT_REPOSITORY_ITEM_COMMONDIR, "refs", true },
	{ GIT_REPOSITORY_ITEM_COMMONDIR, "packed-refs", false },
	{ GIT_REPOSITORY_ITEM_COMMONDIR, "remotes", true },
	{ GIT_REPOSITORY_ITEM_COMMONDIR, "config", false },
	{ GIT_REPOSITORY_ITEM_COMMONDIR, "info", true },
	{ GIT_REPOSITORY_ITEM_COMMONDIR, "hooks", true },
	{ GIT_REPOSITORY_ITEM_COMMONDIR, "logs", true },
	{ GIT_REPOSITORY_ITEM_GITDIR, "modules", true },
	{ GIT_REPOSITORY_ITEM_COMMONDIR, "worktrees", true }
};

64 65
static int check_repositoryformatversion(git_config *config);

66
#define GIT_COMMONDIR_FILE "commondir"
67
#define GIT_GITDIR_FILE "gitdir"
68

69
#define GIT_FILE_CONTENT_PREFIX "gitdir:"
70

71 72
#define GIT_BRANCH_MASTER "master"

73
#define GIT_REPO_VERSION 0
74

75 76 77 78 79 80 81 82 83 84
git_buf git_repository__reserved_names_win32[] = {
	{ DOT_GIT, 0, CONST_STRLEN(DOT_GIT) },
	{ GIT_DIR_SHORTNAME, 0, CONST_STRLEN(GIT_DIR_SHORTNAME) }
};
size_t git_repository__reserved_names_win32_len = 2;

git_buf git_repository__reserved_names_posix[] = {
	{ DOT_GIT, 0, CONST_STRLEN(DOT_GIT) },
};
size_t git_repository__reserved_names_posix_len = 1;
85

86
static void set_odb(git_repository *repo, git_odb *odb)
87
{
88 89 90 91 92 93 94 95
	if (odb) {
		GIT_REFCOUNT_OWN(odb, repo);
		GIT_REFCOUNT_INC(odb);
	}

	if ((odb = git__swap(repo->_odb, odb)) != NULL) {
		GIT_REFCOUNT_OWN(odb, NULL);
		git_odb_free(odb);
96
	}
97
}
98

99
static void set_refdb(git_repository *repo, git_refdb *refdb)
100
{
101 102 103 104 105 106 107 108
	if (refdb) {
		GIT_REFCOUNT_OWN(refdb, repo);
		GIT_REFCOUNT_INC(refdb);
	}

	if ((refdb = git__swap(repo->_refdb, refdb)) != NULL) {
		GIT_REFCOUNT_OWN(refdb, NULL);
		git_refdb_free(refdb);
109 110 111
	}
}

112
static void set_config(git_repository *repo, git_config *config)
113
{
114 115 116 117 118 119 120 121
	if (config) {
		GIT_REFCOUNT_OWN(config, repo);
		GIT_REFCOUNT_INC(config);
	}

	if ((config = git__swap(repo->_config, config)) != NULL) {
		GIT_REFCOUNT_OWN(config, NULL);
		git_config_free(config);
122
	}
123 124

	git_repository__cvar_cache_clear(repo);
125 126
}

127
static void set_index(git_repository *repo, git_index *index)
128
{
129 130 131 132 133 134 135 136
	if (index) {
		GIT_REFCOUNT_OWN(index, repo);
		GIT_REFCOUNT_INC(index);
	}

	if ((index = git__swap(repo->_index, index)) != NULL) {
		GIT_REFCOUNT_OWN(index, NULL);
		git_index_free(index);
137
	}
138 139
}

140
void git_repository__cleanup(git_repository *repo)
141
{
142
	assert(repo);
143

144
	git_repository_submodule_cache_clear(repo);
145
	git_cache_clear(&repo->objects);
146
	git_attr_cache_flush(repo);
147

148 149 150 151
	set_config(repo, NULL);
	set_index(repo, NULL);
	set_odb(repo, NULL);
	set_refdb(repo, NULL);
152 153 154 155
}

void git_repository_free(git_repository *repo)
{
156 157
	size_t i;

158 159 160 161 162 163
	if (repo == NULL)
		return;

	git_repository__cleanup(repo);

	git_cache_free(&repo->objects);
164

165
	git_diff_driver_registry_free(repo->diff_drivers);
166
	repo->diff_drivers = NULL;
167

168
	for (i = 0; i < repo->reserved_names.size; i++)
169
		git_buf_dispose(git_array_get(repo->reserved_names, i));
Edward Thomson committed
170
	git_array_clear(repo->reserved_names);
171

172 173
	git__free(repo->gitlink);
	git__free(repo->gitdir);
174
	git__free(repo->commondir);
Russell Belfer committed
175
	git__free(repo->workdir);
Vicent Marti committed
176
	git__free(repo->namespace);
177 178
	git__free(repo->ident_name);
	git__free(repo->ident_email);
Russell Belfer committed
179

180
	git__memzero(repo, sizeof(*repo));
181
	git__free(repo);
182 183
}

184 185 186 187 188
/*
 * Git repository open methods
 *
 * Open a repository object from its path
 */
189
static bool valid_repository_path(git_buf *repository_path, git_buf *common_path)
190
{
191 192 193 194 195 196 197 198 199 200 201 202 203 204 205
	/* Check if we have a separate commondir (e.g. we have a
	 * worktree) */
	if (git_path_contains_file(repository_path, GIT_COMMONDIR_FILE)) {
		git_buf common_link  = GIT_BUF_INIT;
		git_buf_joinpath(&common_link, repository_path->ptr, GIT_COMMONDIR_FILE);

		git_futils_readbuffer(&common_link, common_link.ptr);
		git_buf_rtrim(&common_link);

		if (git_path_is_relative(common_link.ptr)) {
			git_buf_joinpath(common_path, repository_path->ptr, common_link.ptr);
		} else {
			git_buf_swap(common_path, &common_link);
		}

206
		git_buf_dispose(&common_link);
207 208 209 210 211 212 213 214
	}
	else {
		git_buf_set(common_path, repository_path->ptr, repository_path->size);
	}

	/* Make sure the commondir path always has a trailing * slash */
	if (git_buf_rfind(common_path, '/') != (ssize_t)common_path->size - 1)
		git_buf_putc(common_path, '/');
215

216
	/* Ensure HEAD file exists */
217
	if (git_path_contains_file(repository_path, GIT_HEAD_FILE) == false)
218
		return false;
219

220 221 222 223
	/* Check files in common dir */
	if (git_path_contains_dir(common_path, GIT_OBJECTS_DIR) == false)
		return false;
	if (git_path_contains_dir(common_path, GIT_REFS_DIR) == false)
224
		return false;
225

226
	return true;
227 228
}

229
static git_repository *repository_alloc(void)
230
{
231
	git_repository *repo = git__calloc(1, sizeof(git_repository));
232

233 234 235 236 237 238 239
	if (repo == NULL ||
		git_cache_init(&repo->objects) < 0)
		goto on_error;

	git_array_init_to_size(repo->reserved_names, 4);
	if (!repo->reserved_names.ptr)
		goto on_error;
240

241 242 243
	/* set all the entries in the cvar cache to `unset` */
	git_repository__cvar_cache_clear(repo);

244
	return repo;
245 246 247 248 249 250 251

on_error:
	if (repo)
		git_cache_free(&repo->objects);

	git__free(repo);
	return NULL;
252 253
}

254 255
int git_repository_new(git_repository **out)
{
256 257 258 259 260 261
	git_repository *repo;

	*out = repo = repository_alloc();
	GITERR_CHECK_ALLOC(repo);

	repo->is_bare = 1;
262
	repo->is_worktree = 0;
263

264 265 266
	return 0;
}

267
static int load_config_data(git_repository *repo, const git_config *config)
268
{
269
	int is_bare;
270

271 272 273 274
	int err = git_config_get_bool(&is_bare, config, "core.bare");
	if (err < 0 && err != GIT_ENOTFOUND)
		return err;

275
	/* Try to figure out if it's bare, default to non-bare if it's not set */
276 277
	if (err != GIT_ENOTFOUND)
		repo->is_bare = is_bare && !repo->is_worktree;
278
	else
279
		repo->is_bare = 0;
280

281
	return 0;
282
}
283

284
static int load_workdir(git_repository *repo, git_config *config, git_buf *parent_path)
285
{
286
	int error;
287
	git_config_entry *ce;
288 289
	git_buf worktree = GIT_BUF_INIT;
	git_buf path = GIT_BUF_INIT;
290

291
	if (repo->is_bare)
292
		return 0;
293

294 295 296
	if ((error = git_config__lookup_entry(
			&ce, config, "core.worktree", false)) < 0)
		return error;
297

298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315
	if (repo->is_worktree) {
		char *gitlink = git_worktree__read_link(repo->gitdir, GIT_GITDIR_FILE);
		if (!gitlink) {
			error = -1;
			goto cleanup;
		}

		git_buf_attach(&worktree, gitlink, 0);

		if ((git_path_dirname_r(&worktree, worktree.ptr)) < 0 ||
		    git_path_to_dir(&worktree) < 0) {
			error = -1;
			goto cleanup;
		}

		repo->workdir = git_buf_detach(&worktree);
	}
	else if (ce && ce->value) {
316
		if ((error = git_path_prettify_dir(
317
				&worktree, ce->value, repo->gitdir)) < 0)
318
			goto cleanup;
319 320

		repo->workdir = git_buf_detach(&worktree);
321
	}
322 323
	else if (parent_path && git_path_isdir(parent_path->ptr))
		repo->workdir = git_buf_detach(parent_path);
324
	else {
325
		if (git_path_dirname_r(&worktree, repo->gitdir) < 0 ||
326 327 328 329
		    git_path_to_dir(&worktree) < 0) {
			error = -1;
			goto cleanup;
		}
330 331

		repo->workdir = git_buf_detach(&worktree);
332 333 334
	}

	GITERR_CHECK_ALLOC(repo->workdir);
335
cleanup:
336
	git_buf_dispose(&path);
337 338
	git_config_entry_free(ce);
	return error;
339 340
}

341 342 343 344 345 346 347 348
/*
 * This function returns furthest offset into path where a ceiling dir
 * is found, so we can stop processing the path at that point.
 *
 * Note: converting this to use git_bufs instead of GIT_PATH_MAX buffers on
 * the stack could remove directories name limits, but at the cost of doing
 * repeated malloc/frees inside the loop below, so let's not do it now.
 */
349
static size_t find_ceiling_dir_offset(
350 351 352 353 354 355
	const char *path,
	const char *ceiling_directories)
{
	char buf[GIT_PATH_MAX + 1];
	char buf2[GIT_PATH_MAX + 1];
	const char *ceil, *sep;
356
	size_t len, max_len = 0, min_len;
357 358 359

	assert(path);

360
	min_len = (size_t)(git_path_root(path) + 1);
361 362

	if (ceiling_directories == NULL || min_len == 0)
363
		return min_len;
364 365 366 367 368

	for (sep = ceil = ceiling_directories; *sep; ceil = sep + 1) {
		for (sep = ceil; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++);
		len = sep - ceil;

369
		if (len == 0 || len >= sizeof(buf) || git_path_root(ceil) == -1)
370 371 372 373 374 375 376 377 378 379 380 381 382
			continue;

		strncpy(buf, ceil, len);
		buf[len] = '\0';

		if (p_realpath(buf, buf2) == NULL)
			continue;

		len = strlen(buf2);
		if (len > 0 && buf2[len-1] == '/')
			buf[--len] = '\0';

		if (!strncmp(path, buf2, len) &&
383
			(path[len] == '/' || !path[len]) &&
384 385 386 387 388 389
			len > max_len)
		{
			max_len = len;
		}
	}

390
	return (max_len <= min_len ? min_len : max_len);
391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
}

/*
 * Read the contents of `file_path` and set `path_out` to the repo dir that
 * it points to.  Before calling, set `path_out` to the base directory that
 * should be used if the contents of `file_path` are a relative path.
 */
static int read_gitfile(git_buf *path_out, const char *file_path)
{
	int     error = 0;
	git_buf file = GIT_BUF_INIT;
	size_t  prefix_len = strlen(GIT_FILE_CONTENT_PREFIX);

	assert(path_out && file_path);

	if (git_futils_readbuffer(&file, file_path) < 0)
		return -1;

	git_buf_rtrim(&file);
410 411
	/* apparently on Windows, some people use backslashes in paths */
	git_path_mkposix(file.ptr);
412

413 414
	if (git_buf_len(&file) <= prefix_len ||
		memcmp(git_buf_cstr(&file), GIT_FILE_CONTENT_PREFIX, prefix_len) != 0)
415
	{
416
		giterr_set(GITERR_REPOSITORY,
417
			"the `.git` file at '%s' is malformed", file_path);
418 419 420
		error = -1;
	}
	else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) {
421
		const char *gitlink = git_buf_cstr(&file) + prefix_len;
422
		while (*gitlink && git__isspace(*gitlink)) gitlink++;
423

424 425
		error = git_path_prettify_dir(
			path_out, gitlink, git_buf_cstr(path_out));
426 427
	}

428
	git_buf_dispose(&file);
429 430 431 432
	return error;
}

static int find_repo(
433 434 435 436
	git_buf *gitdir_path,
	git_buf *workdir_path,
	git_buf *gitlink_path,
	git_buf *commondir_path,
437 438 439
	const char *start_path,
	uint32_t flags,
	const char *ceiling_dirs)
440
{
441 442
	int error;
	git_buf path = GIT_BUF_INIT;
443
	git_buf repo_link = GIT_BUF_INIT;
444
	git_buf common_link = GIT_BUF_INIT;
445 446
	struct stat st;
	dev_t initial_device = 0;
447 448
	int min_iterations;
	bool in_dot_git;
449
	size_t ceiling_offset = 0;
450

451
	git_buf_clear(gitdir_path);
452

453 454
	error = git_path_prettify(&path, start_path, NULL);
	if (error < 0)
455 456
		return error;

457 458
	/* in_dot_git toggles each loop:
	 * /a/b/c/.git, /a/b/c, /a/b/.git, /a/b, /a/.git, /a
459 460 461
	 * With GIT_REPOSITORY_OPEN_BARE or GIT_REPOSITORY_OPEN_NO_DOTGIT, we
	 * assume we started with /a/b/c.git and don't append .git the first
	 * time through.
462 463
	 * min_iterations indicates the number of iterations left before going
	 * further counts as a search. */
464
	if (flags & (GIT_REPOSITORY_OPEN_BARE | GIT_REPOSITORY_OPEN_NO_DOTGIT)) {
465 466 467 468 469 470
		in_dot_git = true;
		min_iterations = 1;
	} else {
		in_dot_git = false;
		min_iterations = 2;
	}
471

472
	for (;;) {
473
		if (!(flags & GIT_REPOSITORY_OPEN_NO_DOTGIT)) {
474 475 476
			if (!in_dot_git) {
				error = git_buf_joinpath(&path, path.ptr, DOT_GIT);
				if (error < 0)
477
					break;
478
			}
479 480
			in_dot_git = !in_dot_git;
		}
481 482 483 484 485 486

		if (p_stat(path.ptr, &st) == 0) {
			/* check that we have not crossed device boundaries */
			if (initial_device == 0)
				initial_device = st.st_dev;
			else if (st.st_dev != initial_device &&
487
				 !(flags & GIT_REPOSITORY_OPEN_CROSS_FS))
488 489 490
				break;

			if (S_ISDIR(st.st_mode)) {
491
				if (valid_repository_path(&path, &common_link)) {
492
					git_path_to_dir(&path);
493
					git_buf_set(gitdir_path, path.ptr, path.size);
494

495 496
					if (gitlink_path)
						git_buf_attach(gitlink_path,
497
							git_worktree__read_link(path.ptr, GIT_GITDIR_FILE), 0);
498 499
					if (commondir_path)
						git_buf_swap(&common_link, commondir_path);
500

501 502 503
					break;
				}
			}
504
			else if (S_ISREG(st.st_mode) && git__suffixcmp(path.ptr, "/" DOT_GIT) == 0) {
505 506
				error = read_gitfile(&repo_link, path.ptr);
				if (error < 0)
507
					break;
508
				if (valid_repository_path(&repo_link, &common_link)) {
509
					git_buf_swap(gitdir_path, &repo_link);
510

511 512 513 514
					if (gitlink_path)
						error = git_buf_put(gitlink_path, path.ptr, path.size);
					if (commondir_path)
						git_buf_swap(&common_link, commondir_path);
515
				}
516
				break;
517 518 519
			}
		}

520 521 522
		/* Move up one directory. If we're in_dot_git, we'll search the
		 * parent itself next. If we're !in_dot_git, we'll search .git
		 * in the parent directory next (added at the top of the loop). */
523 524 525 526 527
		if (git_path_dirname_r(&path, path.ptr) < 0) {
			error = -1;
			break;
		}

528 529 530 531
		/* Once we've checked the directory (and .git if applicable),
		 * find the ceiling for a search. */
		if (min_iterations && (--min_iterations == 0))
			ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);
532 533 534 535 536 537

		/* Check if we should stop searching here. */
		if (min_iterations == 0
		    && (path.ptr[ceiling_offset] == 0
			|| (flags & GIT_REPOSITORY_OPEN_NO_SEARCH)))
			break;
538 539
	}

540 541 542
	if (!error && workdir_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) {
		if (!git_buf_len(gitdir_path))
			git_buf_clear(workdir_path);
543
		else {
544 545
			git_path_dirname_r(workdir_path, path.ptr);
			git_path_to_dir(workdir_path);
546
		}
547
		if (git_buf_oom(workdir_path))
548 549 550
			return -1;
	}

551 552
	/* If we didn't find the repository, and we don't have any other error
	 * to report, report that. */
553
	if (!git_buf_len(gitdir_path) && !error) {
554
		giterr_set(GITERR_REPOSITORY,
555
			"could not find repository from '%s'", start_path);
556
		error = GIT_ENOTFOUND;
557
	}
558

559 560 561
	git_buf_dispose(&path);
	git_buf_dispose(&repo_link);
	git_buf_dispose(&common_link);
562 563 564
	return error;
}

565 566 567 568 569
int git_repository_open_bare(
	git_repository **repo_ptr,
	const char *bare_path)
{
	int error;
570
	git_buf path = GIT_BUF_INIT, common_path = GIT_BUF_INIT;
571 572 573 574 575
	git_repository *repo = NULL;

	if ((error = git_path_prettify_dir(&path, bare_path, NULL)) < 0)
		return error;

576
	if (!valid_repository_path(&path, &common_path)) {
577 578
		git_buf_dispose(&path);
		git_buf_dispose(&common_path);
579
		giterr_set(GITERR_REPOSITORY, "path is not a repository: %s", bare_path);
580 581 582 583 584 585
		return GIT_ENOTFOUND;
	}

	repo = repository_alloc();
	GITERR_CHECK_ALLOC(repo);

586 587
	repo->gitdir = git_buf_detach(&path);
	GITERR_CHECK_ALLOC(repo->gitdir);
588 589
	repo->commondir = git_buf_detach(&common_path);
	GITERR_CHECK_ALLOC(repo->commondir);
590 591 592

	/* of course we're bare! */
	repo->is_bare = 1;
593
	repo->is_worktree = 0;
594 595 596 597 598 599
	repo->workdir = NULL;

	*repo_ptr = repo;
	return 0;
}

600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 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 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713
static int _git_repository_open_ext_from_env(
	git_repository **out,
	const char *start_path)
{
	git_repository *repo = NULL;
	git_index *index = NULL;
	git_odb *odb = NULL;
	git_buf dir_buf = GIT_BUF_INIT;
	git_buf ceiling_dirs_buf = GIT_BUF_INIT;
	git_buf across_fs_buf = GIT_BUF_INIT;
	git_buf index_file_buf = GIT_BUF_INIT;
	git_buf namespace_buf = GIT_BUF_INIT;
	git_buf object_dir_buf = GIT_BUF_INIT;
	git_buf alts_buf = GIT_BUF_INIT;
	git_buf work_tree_buf = GIT_BUF_INIT;
	git_buf common_dir_buf = GIT_BUF_INIT;
	const char *ceiling_dirs = NULL;
	unsigned flags = 0;
	int error;

	if (!start_path) {
		error = git__getenv(&dir_buf, "GIT_DIR");
		if (error == GIT_ENOTFOUND) {
			giterr_clear();
			start_path = ".";
		} else if (error < 0)
			goto error;
		else {
			start_path = git_buf_cstr(&dir_buf);
			flags |= GIT_REPOSITORY_OPEN_NO_SEARCH;
			flags |= GIT_REPOSITORY_OPEN_NO_DOTGIT;
		}
	}

	error = git__getenv(&ceiling_dirs_buf, "GIT_CEILING_DIRECTORIES");
	if (error == GIT_ENOTFOUND)
		giterr_clear();
	else if (error < 0)
		goto error;
	else
		ceiling_dirs = git_buf_cstr(&ceiling_dirs_buf);

	error = git__getenv(&across_fs_buf, "GIT_DISCOVERY_ACROSS_FILESYSTEM");
	if (error == GIT_ENOTFOUND)
		giterr_clear();
	else if (error < 0)
		goto error;
	else {
		int across_fs = 0;
		error = git_config_parse_bool(&across_fs, git_buf_cstr(&across_fs_buf));
		if (error < 0)
			goto error;
		if (across_fs)
			flags |= GIT_REPOSITORY_OPEN_CROSS_FS;
	}

	error = git__getenv(&index_file_buf, "GIT_INDEX_FILE");
	if (error == GIT_ENOTFOUND)
		giterr_clear();
	else if (error < 0)
		goto error;
	else {
		error = git_index_open(&index, git_buf_cstr(&index_file_buf));
		if (error < 0)
			goto error;
	}

	error = git__getenv(&namespace_buf, "GIT_NAMESPACE");
	if (error == GIT_ENOTFOUND)
		giterr_clear();
	else if (error < 0)
		goto error;

	error = git__getenv(&object_dir_buf, "GIT_OBJECT_DIRECTORY");
	if (error == GIT_ENOTFOUND)
		giterr_clear();
	else if (error < 0)
		goto error;
	else {
		error = git_odb_open(&odb, git_buf_cstr(&object_dir_buf));
		if (error < 0)
			goto error;
	}

	error = git__getenv(&work_tree_buf, "GIT_WORK_TREE");
	if (error == GIT_ENOTFOUND)
		giterr_clear();
	else if (error < 0)
		goto error;
	else {
		giterr_set(GITERR_INVALID, "GIT_WORK_TREE unimplemented");
		error = GIT_ERROR;
		goto error;
	}

	error = git__getenv(&work_tree_buf, "GIT_COMMON_DIR");
	if (error == GIT_ENOTFOUND)
		giterr_clear();
	else if (error < 0)
		goto error;
	else {
		giterr_set(GITERR_INVALID, "GIT_COMMON_DIR unimplemented");
		error = GIT_ERROR;
		goto error;
	}

	error = git_repository_open_ext(&repo, start_path, flags, ceiling_dirs);
	if (error < 0)
		goto error;

	if (odb)
		git_repository_set_odb(repo, odb);

	error = git__getenv(&alts_buf, "GIT_ALTERNATE_OBJECT_DIRECTORIES");
714
	if (error == GIT_ENOTFOUND) {
715
		giterr_clear();
716 717
		error = 0;
	} else if (error < 0)
718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739
		goto error;
        else {
		const char *end;
		char *alt, *sep;
		if (!odb) {
			error = git_repository_odb(&odb, repo);
			if (error < 0)
				goto error;
		}

		end = git_buf_cstr(&alts_buf) + git_buf_len(&alts_buf);
		for (sep = alt = alts_buf.ptr; sep != end; alt = sep+1) {
			for (sep = alt; *sep && *sep != GIT_PATH_LIST_SEPARATOR; sep++)
				;
			if (*sep)
				*sep = '\0';
			error = git_odb_add_disk_alternate(odb, alt);
			if (error < 0)
				goto error;
		}
	}

740 741 742 743 744
	if (git_buf_len(&namespace_buf)) {
		error = git_repository_set_namespace(repo, git_buf_cstr(&namespace_buf));
		if (error < 0)
			goto error;
	}
745 746 747 748 749 750 751 752 753 754 755 756

	git_repository_set_index(repo, index);

	if (out) {
		*out = repo;
		goto success;
	}
error:
	git_repository_free(repo);
success:
	git_odb_free(odb);
	git_index_free(index);
757 758 759 760 761 762 763 764 765
	git_buf_dispose(&common_dir_buf);
	git_buf_dispose(&work_tree_buf);
	git_buf_dispose(&alts_buf);
	git_buf_dispose(&object_dir_buf);
	git_buf_dispose(&namespace_buf);
	git_buf_dispose(&index_file_buf);
	git_buf_dispose(&across_fs_buf);
	git_buf_dispose(&ceiling_dirs_buf);
	git_buf_dispose(&dir_buf);
766 767 768
	return error;
}

769 770 771 772 773
static int repo_is_worktree(unsigned *out, const git_repository *repo)
{
	git_buf gitdir_link = GIT_BUF_INIT;
	int error;

774 775 776 777 778 779 780
	/* Worktrees cannot have the same commondir and gitdir */
	if (repo->commondir && repo->gitdir
	    && !strcmp(repo->commondir, repo->gitdir)) {
		*out = 0;
		return 0;
	}

781 782 783 784 785 786 787
	if ((error = git_buf_joinpath(&gitdir_link, repo->gitdir, "gitdir")) < 0)
		return -1;

	/* A 'gitdir' file inside a git directory is currently
	 * only used when the repository is a working tree. */
	*out = !!git_path_exists(gitdir_link.ptr);

788
	git_buf_dispose(&gitdir_link);
789 790 791
	return error;
}

792 793 794
int git_repository_open_ext(
	git_repository **repo_ptr,
	const char *start_path,
795
	unsigned int flags,
796 797 798
	const char *ceiling_dirs)
{
	int error;
799
	unsigned is_worktree;
800 801
	git_buf gitdir = GIT_BUF_INIT, workdir = GIT_BUF_INIT,
		gitlink = GIT_BUF_INIT, commondir = GIT_BUF_INIT;
802
	git_repository *repo;
803
	git_config *config = NULL;
804

805 806 807
	if (flags & GIT_REPOSITORY_OPEN_FROM_ENV)
		return _git_repository_open_ext_from_env(repo_ptr, start_path);

808 809
	if (repo_ptr)
		*repo_ptr = NULL;
810

811
	error = find_repo(
812
		&gitdir, &workdir, &gitlink, &commondir, start_path, flags, ceiling_dirs);
813

814
	if (error < 0 || !repo_ptr)
815 816
		return error;

817
	repo = repository_alloc();
818
	GITERR_CHECK_ALLOC(repo);
819

820
	repo->gitdir = git_buf_detach(&gitdir);
821
	GITERR_CHECK_ALLOC(repo->gitdir);
822

823 824
	if (gitlink.size) {
		repo->gitlink = git_buf_detach(&gitlink);
825
		GITERR_CHECK_ALLOC(repo->gitlink);
826
	}
827 828
	if (commondir.size) {
		repo->commondir = git_buf_detach(&commondir);
829 830
		GITERR_CHECK_ALLOC(repo->commondir);
	}
831

832
	if ((error = repo_is_worktree(&is_worktree, repo)) < 0)
833
		goto cleanup;
834
	repo->is_worktree = is_worktree;
835

836 837 838 839 840 841 842 843 844 845 846 847
	/*
	 * We'd like to have the config, but git doesn't particularly
	 * care if it's not there, so we need to deal with that.
	 */

	error = git_repository_config_snapshot(&config, repo);
	if (error < 0 && error != GIT_ENOTFOUND)
		goto cleanup;

	if (config && (error = check_repositoryformatversion(config)) < 0)
		goto cleanup;

848 849
	if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0)
		repo->is_bare = 1;
850 851
	else {

852 853
		if (config &&
		    ((error = load_config_data(repo, config)) < 0 ||
854
		     (error = load_workdir(repo, config, &workdir)) < 0))
855
			goto cleanup;
856
	}
857

858
cleanup:
859 860
	git_buf_dispose(&gitdir);
	git_buf_dispose(&workdir);
861 862 863 864 865 866
	git_config_free(config);

	if (error < 0)
		git_repository_free(repo);
	else
		*repo_ptr = repo;
867 868

	return error;
869
}
870

871 872 873 874 875 876
int git_repository_open(git_repository **repo_out, const char *path)
{
	return git_repository_open_ext(
		repo_out, path, GIT_REPOSITORY_OPEN_NO_SEARCH, NULL);
}

877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901
int git_repository_open_from_worktree(git_repository **repo_out, git_worktree *wt)
{
	git_buf path = GIT_BUF_INIT;
	git_repository *repo = NULL;
	int len, err;

	assert(repo_out && wt);

	*repo_out = NULL;
	len = strlen(wt->gitlink_path);

	if (len <= 4 || strcasecmp(wt->gitlink_path + len - 4, ".git")) {
		err = -1;
		goto out;
	}

	if ((err = git_buf_set(&path, wt->gitlink_path, len - 4)) < 0)
		goto out;

	if ((err = git_repository_open(&repo, path.ptr)) < 0)
		goto out;

	*repo_out = repo;

out:
902
	git_buf_dispose(&path);
903 904 905 906

	return err;
}

907 908 909 910 911 912 913 914 915 916 917 918 919
int git_repository_wrap_odb(git_repository **repo_out, git_odb *odb)
{
	git_repository *repo;

	repo = repository_alloc();
	GITERR_CHECK_ALLOC(repo);

	git_repository_set_odb(repo, odb);
	*repo_out = repo;

	return 0;
}

920
int git_repository_discover(
921
	git_buf *out,
922 923 924 925 926 927
	const char *start_path,
	int across_fs,
	const char *ceiling_dirs)
{
	uint32_t flags = across_fs ? GIT_REPOSITORY_OPEN_CROSS_FS : 0;

928
	assert(start_path);
929

930
	git_buf_sanitize(out);
931

932
	return find_repo(out, NULL, NULL, NULL, start_path, flags, ceiling_dirs);
933 934
}

935
static int load_config(
936 937 938
	git_config **out,
	git_repository *repo,
	const char *global_config_path,
Sven Strickroth committed
939
	const char *xdg_config_path,
940 941
	const char *system_config_path,
	const char *programdata_path)
942
{
943
	int error;
944
	git_buf config_path = GIT_BUF_INIT;
945
	git_config *cfg = NULL;
946

947
	assert(repo && out);
948

949 950
	if ((error = git_config_new(&cfg)) < 0)
		return error;
951

952
	if ((error = git_repository_item_path(&config_path, repo, GIT_REPOSITORY_ITEM_CONFIG)) == 0)
953
		error = git_config_add_file_ondisk(cfg, config_path.ptr, GIT_CONFIG_LEVEL_LOCAL, repo, 0);
954

955
	if (error && error != GIT_ENOTFOUND)
956 957
		goto on_error;

958
	git_buf_dispose(&config_path);
959

960 961
	if (global_config_path != NULL &&
		(error = git_config_add_file_ondisk(
962
			cfg, global_config_path, GIT_CONFIG_LEVEL_GLOBAL, repo, 0)) < 0 &&
963 964
		error != GIT_ENOTFOUND)
		goto on_error;
965

966 967
	if (xdg_config_path != NULL &&
		(error = git_config_add_file_ondisk(
968
			cfg, xdg_config_path, GIT_CONFIG_LEVEL_XDG, repo, 0)) < 0 &&
969 970
		error != GIT_ENOTFOUND)
		goto on_error;
971

972 973
	if (system_config_path != NULL &&
		(error = git_config_add_file_ondisk(
974
			cfg, system_config_path, GIT_CONFIG_LEVEL_SYSTEM, repo, 0)) < 0 &&
975 976
		error != GIT_ENOTFOUND)
		goto on_error;
977

978 979
	if (programdata_path != NULL &&
		(error = git_config_add_file_ondisk(
980
			cfg, programdata_path, GIT_CONFIG_LEVEL_PROGRAMDATA, repo, 0)) < 0 &&
981 982 983
		error != GIT_ENOTFOUND)
		goto on_error;

984 985
	giterr_clear(); /* clear any lingering ENOTFOUND errors */

986
	*out = cfg;
987
	return 0;
988

989
on_error:
990
	git_buf_dispose(&config_path);
991 992
	git_config_free(cfg);
	*out = NULL;
993
	return error;
994 995
}

Russell Belfer committed
996
static const char *path_unless_empty(git_buf *buf)
997
{
Russell Belfer committed
998 999
	return git_buf_len(buf) > 0 ? git_buf_cstr(buf) : NULL;
}
1000

Russell Belfer committed
1001 1002 1003
int git_repository_config__weakptr(git_config **out, git_repository *repo)
{
	int error = 0;
1004

Russell Belfer committed
1005 1006 1007 1008
	if (repo->_config == NULL) {
		git_buf global_buf = GIT_BUF_INIT;
		git_buf xdg_buf = GIT_BUF_INIT;
		git_buf system_buf = GIT_BUF_INIT;
1009
		git_buf programdata_buf = GIT_BUF_INIT;
Russell Belfer committed
1010 1011
		git_config *config;

1012 1013 1014
		git_config_find_global(&global_buf);
		git_config_find_xdg(&xdg_buf);
		git_config_find_system(&system_buf);
1015
		git_config_find_programdata(&programdata_buf);
Russell Belfer committed
1016

1017 1018 1019 1020
		/* If there is no global file, open a backend for it anyway */
		if (git_buf_len(&global_buf) == 0)
			git_config__global_location(&global_buf);

Russell Belfer committed
1021 1022 1023 1024
		error = load_config(
			&config, repo,
			path_unless_empty(&global_buf),
			path_unless_empty(&xdg_buf),
1025 1026
			path_unless_empty(&system_buf),
			path_unless_empty(&programdata_buf));
Russell Belfer committed
1027 1028 1029
		if (!error) {
			GIT_REFCOUNT_OWN(config, repo);

1030
			config = git__compare_and_swap(&repo->_config, NULL, config);
Russell Belfer committed
1031 1032 1033 1034 1035
			if (config != NULL) {
				GIT_REFCOUNT_OWN(config, NULL);
				git_config_free(config);
			}
		}
1036

1037 1038 1039 1040
		git_buf_dispose(&global_buf);
		git_buf_dispose(&xdg_buf);
		git_buf_dispose(&system_buf);
		git_buf_dispose(&programdata_buf);
1041
	}
1042

1043
	*out = repo->_config;
Russell Belfer committed
1044
	return error;
1045 1046
}

1047
int git_repository_config(git_config **out, git_repository *repo)
1048
{
1049 1050
	if (git_repository_config__weakptr(out, repo) < 0)
		return -1;
1051

1052 1053
	GIT_REFCOUNT_INC(*out);
	return 0;
1054 1055
}

1056 1057
int git_repository_config_snapshot(git_config **out, git_repository *repo)
{
1058
	int error;
1059 1060
	git_config *weak;

1061 1062
	if ((error = git_repository_config__weakptr(&weak, repo)) < 0)
		return error;
1063 1064 1065 1066

	return git_config_snapshot(out, weak);
}

1067 1068 1069
void git_repository_set_config(git_repository *repo, git_config *config)
{
	assert(repo && config);
1070
	set_config(repo, config);
1071 1072 1073 1074
}

int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
{
Russell Belfer committed
1075 1076
	int error = 0;

1077 1078 1079
	assert(repo && out);

	if (repo->_odb == NULL) {
1080
		git_buf odb_path = GIT_BUF_INIT;
Russell Belfer committed
1081
		git_odb *odb;
1082

1083
		if ((error = git_repository_item_path(&odb_path, repo,
1084 1085
				GIT_REPOSITORY_ITEM_OBJECTS)) < 0 ||
			(error = git_odb_new(&odb)) < 0)
1086
			return error;
1087

1088
		GIT_REFCOUNT_OWN(odb, repo);
1089

1090 1091 1092 1093 1094 1095 1096 1097 1098 1099
		if ((error = git_odb__set_caps(odb, GIT_ODB_CAP_FROM_OWNER)) < 0 ||
			(error = git_odb__add_default_backends(odb, odb_path.ptr, 0, 0)) < 0) {
			git_odb_free(odb);
			return error;
		}

		odb = git__compare_and_swap(&repo->_odb, NULL, odb);
		if (odb != NULL) {
			GIT_REFCOUNT_OWN(odb, NULL);
			git_odb_free(odb);
Russell Belfer committed
1100
		}
1101

1102
		git_buf_dispose(&odb_path);
1103
	}
1104

1105
	*out = repo->_odb;
Russell Belfer committed
1106
	return error;
1107 1108
}

1109
int git_repository_odb(git_odb **out, git_repository *repo)
1110
{
1111 1112
	if (git_repository_odb__weakptr(out, repo) < 0)
		return -1;
Vicent Marti committed
1113

1114 1115
	GIT_REFCOUNT_INC(*out);
	return 0;
1116
}
1117

1118 1119 1120
void git_repository_set_odb(git_repository *repo, git_odb *odb)
{
	assert(repo && odb);
1121
	set_odb(repo, odb);
1122 1123
}

1124 1125
int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo)
{
Russell Belfer committed
1126 1127
	int error = 0;

1128 1129 1130
	assert(out && repo);

	if (repo->_refdb == NULL) {
Russell Belfer committed
1131
		git_refdb *refdb;
1132

Russell Belfer committed
1133 1134 1135
		error = git_refdb_open(&refdb, repo);
		if (!error) {
			GIT_REFCOUNT_OWN(refdb, repo);
1136

1137
			refdb = git__compare_and_swap(&repo->_refdb, NULL, refdb);
Russell Belfer committed
1138 1139 1140 1141 1142
			if (refdb != NULL) {
				GIT_REFCOUNT_OWN(refdb, NULL);
				git_refdb_free(refdb);
			}
		}
1143 1144 1145
	}

	*out = repo->_refdb;
Russell Belfer committed
1146
	return error;
1147 1148 1149 1150 1151 1152 1153 1154 1155 1156 1157 1158 1159
}

int git_repository_refdb(git_refdb **out, git_repository *repo)
{
	if (git_repository_refdb__weakptr(out, repo) < 0)
		return -1;

	GIT_REFCOUNT_INC(*out);
	return 0;
}

void git_repository_set_refdb(git_repository *repo, git_refdb *refdb)
{
Russell Belfer committed
1160
	assert(repo && refdb);
1161
	set_refdb(repo, refdb);
1162 1163
}

1164 1165
int git_repository_index__weakptr(git_index **out, git_repository *repo)
{
Russell Belfer committed
1166 1167
	int error = 0;

1168 1169 1170
	assert(out && repo);

	if (repo->_index == NULL) {
1171
		git_buf index_path = GIT_BUF_INIT;
Russell Belfer committed
1172
		git_index *index;
1173

1174
		if ((error = git_buf_joinpath(&index_path, repo->gitdir, GIT_INDEX_FILE)) < 0)
1175
			return error;
1176

Russell Belfer committed
1177 1178 1179
		error = git_index_open(&index, index_path.ptr);
		if (!error) {
			GIT_REFCOUNT_OWN(index, repo);
1180

1181
			index = git__compare_and_swap(&repo->_index, NULL, index);
Russell Belfer committed
1182 1183 1184 1185
			if (index != NULL) {
				GIT_REFCOUNT_OWN(index, NULL);
				git_index_free(index);
			}
1186

Russell Belfer committed
1187 1188
			error = git_index_set_caps(repo->_index, GIT_INDEXCAP_FROM_OWNER);
		}
1189

1190
		git_buf_dispose(&index_path);
1191 1192 1193
	}

	*out = repo->_index;
Russell Belfer committed
1194
	return error;
1195
}
Vicent Marti committed
1196

1197 1198
int git_repository_index(git_index **out, git_repository *repo)
{
1199 1200
	if (git_repository_index__weakptr(out, repo) < 0)
		return -1;
1201

1202 1203
	GIT_REFCOUNT_INC(*out);
	return 0;
1204 1205
}

1206 1207
void git_repository_set_index(git_repository *repo, git_index *index)
{
1208
	assert(repo);
1209
	set_index(repo, index);
1210 1211
}

Vicent Marti committed
1212 1213 1214 1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228
int git_repository_set_namespace(git_repository *repo, const char *namespace)
{
	git__free(repo->namespace);

	if (namespace == NULL) {
		repo->namespace = NULL;
		return 0;
	}

	return (repo->namespace = git__strdup(namespace)) ? 0 : -1;
}

const char *git_repository_get_namespace(git_repository *repo)
{
	return repo->namespace;
}

1229 1230
#ifdef GIT_WIN32
static int reserved_names_add8dot3(git_repository *repo, const char *path)
1231
{
1232 1233
	char *name = git_win32_path_8dot3_name(path);
	const char *def = GIT_DIR_SHORTNAME;
1234
	const char *def_dot_git = DOT_GIT;
1235
	size_t name_len, def_len = CONST_STRLEN(GIT_DIR_SHORTNAME);
1236
	size_t def_dot_git_len = CONST_STRLEN(DOT_GIT);
1237
	git_buf *buf;
1238

1239 1240 1241 1242 1243
	if (!name)
		return 0;

	name_len = strlen(name);

1244
	if ((name_len == def_len && memcmp(name, def, def_len) == 0) ||
1245
		(name_len == def_dot_git_len && memcmp(name, def_dot_git, def_dot_git_len) == 0)) {
1246 1247 1248 1249 1250 1251 1252 1253 1254 1255 1256 1257 1258 1259 1260 1261 1262 1263 1264 1265 1266 1267 1268 1269 1270 1271 1272 1273 1274
		git__free(name);
		return 0;
	}

	if ((buf = git_array_alloc(repo->reserved_names)) == NULL)
		return -1;

	git_buf_attach(buf, name, name_len);
	return true;
}

bool git_repository__reserved_names(
	git_buf **out, size_t *outlen, git_repository *repo, bool include_ntfs)
{
	GIT_UNUSED(include_ntfs);

	if (repo->reserved_names.size == 0) {
		git_buf *buf;
		size_t i;

		/* Add the static defaults */
		for (i = 0; i < git_repository__reserved_names_win32_len; i++) {
			if ((buf = git_array_alloc(repo->reserved_names)) == NULL)
				goto on_error;

			buf->ptr = git_repository__reserved_names_win32[i].ptr;
			buf->size = git_repository__reserved_names_win32[i].size;
		}

1275 1276 1277 1278 1279 1280
		/* Try to add any repo-specific reserved names - the gitlink file
		 * within a submodule or the repository (if the repository directory
		 * is beneath the workdir).  These are typically `.git`, but should
		 * be protected in case they are not.  Note, repo and workdir paths
		 * are always prettified to end in `/`, so a prefixcmp is safe.
		 */
1281
		if (!repo->is_bare) {
1282 1283
			int (*prefixcmp)(const char *, const char *);
			int error, ignorecase;
1284

1285 1286 1287 1288 1289
			error = git_repository__cvar(
				&ignorecase, repo, GIT_CVAR_IGNORECASE);
			prefixcmp = (error || ignorecase) ? git__prefixcmp_icase :
				git__prefixcmp;

1290 1291
			if (repo->gitlink &&
				reserved_names_add8dot3(repo, repo->gitlink) < 0)
1292 1293
				goto on_error;

1294 1295 1296
			if (repo->gitdir &&
				prefixcmp(repo->gitdir, repo->workdir) == 0 &&
				reserved_names_add8dot3(repo, repo->gitdir) < 0)
1297
				goto on_error;
1298 1299 1300
		}
	}

1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311
	*out = repo->reserved_names.ptr;
	*outlen = repo->reserved_names.size;

	return true;

	/* Always give good defaults, even on OOM */
on_error:
	*out = git_repository__reserved_names_win32;
	*outlen = git_repository__reserved_names_win32_len;

	return false;
1312
}
1313 1314 1315 1316 1317 1318 1319 1320 1321 1322 1323 1324 1325 1326 1327 1328 1329
#else
bool git_repository__reserved_names(
	git_buf **out, size_t *outlen, git_repository *repo, bool include_ntfs)
{
	GIT_UNUSED(repo);

	if (include_ntfs) {
		*out = git_repository__reserved_names_win32;
		*outlen = git_repository__reserved_names_win32_len;
	} else {
		*out = git_repository__reserved_names_posix;
		*outlen = git_repository__reserved_names_posix_len;
	}

	return true;
}
#endif
1330

1331
static int check_repositoryformatversion(git_config *config)
1332
{
1333
	int version, error;
1334

1335 1336 1337 1338 1339 1340
	error = git_config_get_int32(&version, config, "core.repositoryformatversion");
	/* git ignores this if the config variable isn't there */
	if (error == GIT_ENOTFOUND)
		return 0;

	if (error < 0)
1341
		return -1;
1342

1343
	if (GIT_REPO_VERSION < version) {
1344
		giterr_set(GITERR_REPOSITORY,
1345
			"unsupported repository version %d. Only versions up to %d are supported.",
1346
			version, GIT_REPO_VERSION);
1347 1348
		return -1;
	}
1349

1350
	return 0;
1351 1352
}

1353
int git_repository_create_head(const char *git_dir, const char *ref_name)
1354
{
1355
	git_buf ref_path = GIT_BUF_INIT;
1356
	git_filebuf ref = GIT_FILEBUF_INIT;
1357
	const char *fmt;
1358

1359
	if (git_buf_joinpath(&ref_path, git_dir, GIT_HEAD_FILE) < 0 ||
1360
		git_filebuf_open(&ref, ref_path.ptr, 0, GIT_REFS_FILE_MODE) < 0)
1361 1362 1363 1364 1365
		goto fail;

	if (!ref_name)
		ref_name = GIT_BRANCH_MASTER;

1366
	if (git__prefixcmp(ref_name, GIT_REFS_DIR) == 0)
1367 1368
		fmt = "ref: %s\n";
	else
1369
		fmt = "ref: " GIT_REFS_HEADS_DIR "%s\n";
1370 1371

	if (git_filebuf_printf(&ref, fmt, ref_name) < 0 ||
1372
		git_filebuf_commit(&ref) < 0)
1373
		goto fail;
1374

1375
	git_buf_dispose(&ref_path);
1376
	return 0;
1377 1378

fail:
1379
	git_buf_dispose(&ref_path);
1380 1381
	git_filebuf_cleanup(&ref);
	return -1;
1382 1383
}

1384 1385 1386 1387 1388 1389 1390 1391 1392 1393 1394 1395 1396
static bool is_chmod_supported(const char *file_path)
{
	struct stat st1, st2;

	if (p_stat(file_path, &st1) < 0)
		return false;

	if (p_chmod(file_path, st1.st_mode ^ S_IXUSR) < 0)
		return false;

	if (p_stat(file_path, &st2) < 0)
		return false;

1397
	return (st1.st_mode != st2.st_mode);
1398 1399
}

1400 1401 1402
static bool is_filesystem_case_insensitive(const char *gitdir_path)
{
	git_buf path = GIT_BUF_INIT;
1403
	int is_insensitive = -1;
1404

1405 1406
	if (!git_buf_joinpath(&path, gitdir_path, "CoNfIg"))
		is_insensitive = git_path_exists(git_buf_cstr(&path));
1407

1408
	git_buf_dispose(&path);
1409
	return is_insensitive;
1410 1411
}

1412 1413 1414 1415 1416
static bool are_symlinks_supported(const char *wd_path)
{
	git_buf path = GIT_BUF_INIT;
	int fd;
	struct stat st;
1417
	int symlinks_supported = -1;
1418

1419
	if ((fd = git_futils_mktmp(&path, wd_path, 0666)) < 0 ||
1420 1421 1422 1423
		p_close(fd) < 0 ||
		p_unlink(path.ptr) < 0 ||
		p_symlink("testing", path.ptr) < 0 ||
		p_lstat(path.ptr, &st) < 0)
1424
		symlinks_supported = false;
1425
	else
1426
		symlinks_supported = (S_ISLNK(st.st_mode) != 0);
1427 1428

	(void)p_unlink(path.ptr);
1429
	git_buf_dispose(&path);
1430

1431 1432 1433
	return symlinks_supported;
}

1434 1435 1436 1437 1438
static int create_empty_file(const char *path, mode_t mode)
{
	int fd;

	if ((fd = p_creat(path, mode)) < 0) {
1439
		giterr_set(GITERR_OS, "error while creating '%s'", path);
1440 1441 1442 1443
		return -1;
	}

	if (p_close(fd) < 0) {
1444
		giterr_set(GITERR_OS, "error while closing '%s'", path);
1445 1446 1447 1448 1449 1450
		return -1;
	}

	return 0;
}

1451 1452 1453 1454 1455
static int repo_local_config(
	git_config **out,
	git_buf *config_dir,
	git_repository *repo,
	const char *repo_dir)
1456
{
1457
	int error = 0;
1458 1459
	git_config *parent;
	const char *cfg_path;
1460

1461
	if (git_buf_joinpath(config_dir, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
1462
		return -1;
1463
	cfg_path = git_buf_cstr(config_dir);
1464

1465
	/* make LOCAL config if missing */
1466 1467
	if (!git_path_isfile(cfg_path) &&
		(error = create_empty_file(cfg_path, GIT_CONFIG_FILE_MODE)) < 0)
1468
		return error;
1469

1470 1471 1472 1473 1474 1475 1476 1477 1478
	/* if no repo, just open that file directly */
	if (!repo)
		return git_config_open_ondisk(out, cfg_path);

	/* otherwise, open parent config and get that level */
	if ((error = git_repository_config__weakptr(&parent, repo)) < 0)
		return error;

	if (git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL) < 0) {
1479 1480 1481
		giterr_clear();

		if (!(error = git_config_add_file_ondisk(
1482
				parent, cfg_path, GIT_CONFIG_LEVEL_LOCAL, repo, false)))
1483
			error = git_config_open_level(out, parent, GIT_CONFIG_LEVEL_LOCAL);
1484
	}
1485

1486 1487 1488 1489 1490 1491 1492 1493 1494 1495 1496 1497 1498 1499 1500 1501 1502 1503 1504 1505 1506 1507 1508 1509 1510 1511
	git_config_free(parent);

	return error;
}

static int repo_init_fs_configs(
	git_config *cfg,
	const char *cfg_path,
	const char *repo_dir,
	const char *work_dir,
	bool update_ignorecase)
{
	int error = 0;

	if (!work_dir)
		work_dir = repo_dir;

	if ((error = git_config_set_bool(
			cfg, "core.filemode", is_chmod_supported(cfg_path))) < 0)
		return error;

	if (!are_symlinks_supported(work_dir)) {
		if ((error = git_config_set_bool(cfg, "core.symlinks", false)) < 0)
			return error;
	} else if (git_config_delete_entry(cfg, "core.symlinks") < 0)
		giterr_clear();
1512

1513 1514 1515 1516 1517 1518 1519
	if (update_ignorecase) {
		if (is_filesystem_case_insensitive(repo_dir)) {
			if ((error = git_config_set_bool(cfg, "core.ignorecase", true)) < 0)
				return error;
		} else if (git_config_delete_entry(cfg, "core.ignorecase") < 0)
			giterr_clear();
	}
1520

1521
#ifdef GIT_USE_ICONV
1522 1523
	if ((error = git_config_set_bool(
			cfg, "core.precomposeunicode",
1524
			git_path_does_fs_decompose_unicode(work_dir))) < 0)
1525
		return error;
1526
	/* on non-iconv platforms, don't even set core.precomposeunicode */
1527 1528
#endif

1529 1530
	return 0;
}
1531

1532 1533 1534 1535 1536 1537 1538
static int repo_init_config(
	const char *repo_dir,
	const char *work_dir,
	uint32_t flags,
	uint32_t mode)
{
	int error = 0;
1539
	git_buf cfg_path = GIT_BUF_INIT, worktree_path = GIT_BUF_INIT;
1540 1541 1542 1543 1544 1545 1546 1547 1548 1549 1550 1551 1552 1553 1554 1555 1556 1557 1558 1559
	git_config *config = NULL;
	bool is_bare = ((flags & GIT_REPOSITORY_INIT_BARE) != 0);
	bool is_reinit = ((flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0);

	if ((error = repo_local_config(&config, &cfg_path, NULL, repo_dir)) < 0)
		goto cleanup;

	if (is_reinit && (error = check_repositoryformatversion(config)) < 0)
		goto cleanup;

#define SET_REPO_CONFIG(TYPE, NAME, VAL) do { \
	if ((error = git_config_set_##TYPE(config, NAME, VAL)) < 0) \
		goto cleanup; } while (0)

	SET_REPO_CONFIG(bool, "core.bare", is_bare);
	SET_REPO_CONFIG(int32, "core.repositoryformatversion", GIT_REPO_VERSION);

	if ((error = repo_init_fs_configs(
			config, cfg_path.ptr, repo_dir, work_dir, !is_reinit)) < 0)
		goto cleanup;
1560

1561 1562
	if (!is_bare) {
		SET_REPO_CONFIG(bool, "core.logallrefupdates", true);
1563

1564 1565 1566 1567 1568 1569 1570 1571 1572 1573
		if (!(flags & GIT_REPOSITORY_INIT__NATURAL_WD)) {
			if ((error = git_buf_sets(&worktree_path, work_dir)) < 0)
				goto cleanup;

			if ((flags & GIT_REPOSITORY_INIT_RELATIVE_GITLINK))
				if ((error = git_path_make_relative(&worktree_path, repo_dir)) < 0)
					goto cleanup;

			SET_REPO_CONFIG(string, "core.worktree", worktree_path.ptr);
		} else if (is_reinit) {
Ben Straub committed
1574
			if (git_config_delete_entry(config, "core.worktree") < 0)
1575
				giterr_clear();
1576 1577 1578
		}
	}

1579
	if (mode == GIT_REPOSITORY_INIT_SHARED_GROUP) {
1580 1581
		SET_REPO_CONFIG(int32, "core.sharedrepository", 1);
		SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
1582
	}
1583
	else if (mode == GIT_REPOSITORY_INIT_SHARED_ALL) {
1584 1585 1586
		SET_REPO_CONFIG(int32, "core.sharedrepository", 2);
		SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
	}
1587

1588
cleanup:
1589 1590
	git_buf_dispose(&cfg_path);
	git_buf_dispose(&worktree_path);
1591
	git_config_free(config);
1592

1593
	return error;
1594
}
1595

1596
static int repo_reinit_submodule_fs(git_submodule *sm, const char *n, void *p)
1597
{
1598 1599
	git_repository *smrepo = NULL;
	GIT_UNUSED(n); GIT_UNUSED(p);
1600

1601
	if (git_submodule_open(&smrepo, sm) < 0 ||
1602
		git_repository_reinit_filesystem(smrepo, true) < 0)
1603 1604
		giterr_clear();
	git_repository_free(smrepo);
1605

1606 1607
	return 0;
}
1608

1609
int git_repository_reinit_filesystem(git_repository *repo, int recurse)
1610 1611 1612 1613 1614
{
	int error = 0;
	git_buf path = GIT_BUF_INIT;
	git_config *config = NULL;
	const char *repo_dir = git_repository_path(repo);
1615

1616 1617 1618
	if (!(error = repo_local_config(&config, &path, repo, repo_dir)))
		error = repo_init_fs_configs(
			config, path.ptr, repo_dir, git_repository_workdir(repo), true);
1619

1620
	git_config_free(config);
1621
	git_buf_dispose(&path);
1622 1623 1624

	git_repository__cvar_cache_clear(repo);

1625
	if (!repo->is_bare && recurse)
1626
		(void)git_submodule_foreach(repo, repo_reinit_submodule_fs, NULL);
1627

1628 1629 1630
	return error;
}

1631
static int repo_write_template(
1632 1633 1634 1635
	const char *git_dir,
	bool allow_overwrite,
	const char *file,
	mode_t mode,
1636
	bool hidden,
1637
	const char *content)
1638 1639
{
	git_buf path = GIT_BUF_INIT;
1640
	int fd, error = 0, flags;
1641 1642 1643 1644

	if (git_buf_joinpath(&path, git_dir, file) < 0)
		return -1;

1645 1646 1647 1648 1649 1650
	if (allow_overwrite)
		flags = O_WRONLY | O_CREAT | O_TRUNC;
	else
		flags = O_WRONLY | O_CREAT | O_EXCL;

	fd = p_open(git_buf_cstr(&path), flags, mode);
1651

1652 1653
	if (fd >= 0) {
		error = p_write(fd, content, strlen(content));
1654

1655 1656 1657 1658
		p_close(fd);
	}
	else if (errno != EEXIST)
		error = fd;
1659

1660 1661
#ifdef GIT_WIN32
	if (!error && hidden) {
1662
		if (git_win32__set_hidden(path.ptr, true) < 0)
1663 1664 1665 1666 1667 1668
			error = -1;
	}
#else
	GIT_UNUSED(hidden);
#endif

1669
	git_buf_dispose(&path);
1670 1671 1672

	if (error)
		giterr_set(GITERR_OS,
1673
			"failed to initialize repository with template '%s'", file);
1674 1675

	return error;
1676 1677
}

1678
static int repo_write_gitlink(
1679
	const char *in_dir, const char *to_repo, bool use_relative_path)
1680
{
1681 1682
	int error;
	git_buf buf = GIT_BUF_INIT;
1683
	git_buf path_to_repo = GIT_BUF_INIT;
1684 1685 1686 1687 1688
	struct stat st;

	git_path_dirname_r(&buf, to_repo);
	git_path_to_dir(&buf);
	if (git_buf_oom(&buf))
1689
		return -1;
1690

1691 1692 1693 1694 1695 1696 1697 1698 1699 1700 1701 1702 1703
	/* don't write gitlink to natural workdir */
	if (git__suffixcmp(to_repo, "/" DOT_GIT "/") == 0 &&
		strcmp(in_dir, buf.ptr) == 0)
	{
		error = GIT_PASSTHROUGH;
		goto cleanup;
	}

	if ((error = git_buf_joinpath(&buf, in_dir, DOT_GIT)) < 0)
		goto cleanup;

	if (!p_stat(buf.ptr, &st) && !S_ISREG(st.st_mode)) {
		giterr_set(GITERR_REPOSITORY,
1704
			"cannot overwrite gitlink file into path '%s'", in_dir);
1705 1706 1707 1708 1709 1710
		error = GIT_EEXISTS;
		goto cleanup;
	}

	git_buf_clear(&buf);

1711 1712 1713 1714 1715 1716 1717
	error = git_buf_sets(&path_to_repo, to_repo);

	if (!error && use_relative_path)
		error = git_path_make_relative(&path_to_repo, in_dir);

	if (!error)
		error = git_buf_join(&buf, ' ', GIT_FILE_CONTENT_PREFIX, path_to_repo.ptr);
1718 1719

	if (!error)
1720
		error = repo_write_template(in_dir, true, DOT_GIT, 0666, true, buf.ptr);
1721 1722

cleanup:
1723 1724
	git_buf_dispose(&buf);
	git_buf_dispose(&path_to_repo);
1725 1726 1727
	return error;
}

1728 1729 1730
static mode_t pick_dir_mode(git_repository_init_options *opts)
{
	if (opts->mode == GIT_REPOSITORY_INIT_SHARED_UMASK)
1731
		return 0777;
1732 1733 1734 1735 1736 1737 1738
	if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP)
		return (0775 | S_ISGID);
	if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL)
		return (0777 | S_ISGID);
	return opts->mode;
}

1739 1740 1741 1742 1743 1744 1745
#include "repo_template.h"

static int repo_init_structure(
	const char *repo_dir,
	const char *work_dir,
	git_repository_init_options *opts)
{
1746
	int error = 0;
1747
	repo_template_item *tpl;
1748 1749 1750
	bool external_tpl =
		((opts->flags & GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE) != 0);
	mode_t dmode = pick_dir_mode(opts);
1751
	bool chmod = opts->mode != GIT_REPOSITORY_INIT_SHARED_UMASK;
1752 1753

	/* Hide the ".git" directory */
1754
#ifdef GIT_WIN32
1755
	if ((opts->flags & GIT_REPOSITORY_INIT__HAS_DOTGIT) != 0) {
1756
		if (git_win32__set_hidden(repo_dir, true) < 0) {
1757
			giterr_set(GITERR_OS,
1758
				"failed to mark Git repository folder as hidden");
1759 1760
			return -1;
		}
1761
	}
1762 1763 1764 1765 1766 1767
#endif

	/* Create the .git gitlink if appropriate */
	if ((opts->flags & GIT_REPOSITORY_INIT_BARE) == 0 &&
		(opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD) == 0)
	{
1768
		if (repo_write_gitlink(work_dir, repo_dir, opts->flags & GIT_REPOSITORY_INIT_RELATIVE_GITLINK) < 0)
1769
			return -1;
1770
	}
1771

1772 1773
	/* Copy external template if requested */
	if (external_tpl) {
Linquize committed
1774 1775 1776
		git_config *cfg = NULL;
		const char *tdir = NULL;
		bool default_template = false;
1777 1778
		git_buf template_buf = GIT_BUF_INIT;

1779 1780
		if (opts->template_path)
			tdir = opts->template_path;
Linquize committed
1781
		else if ((error = git_config_open_default(&cfg)) >= 0) {
1782 1783
			if (!git_config_get_path(&template_buf, cfg, "init.templatedir"))
				tdir = template_buf.ptr;
1784
			giterr_clear();
Linquize committed
1785 1786 1787
		}

		if (!tdir) {
1788
			if (!(error = git_sysdir_find_template_dir(&template_buf)))
1789
				tdir = template_buf.ptr;
Linquize committed
1790
			default_template = true;
1791
		}
1792

1793 1794 1795 1796 1797 1798 1799
		/*
		 * If tdir was the empty string, treat it like tdir was a path to an
		 * empty directory (so, don't do any copying). This is the behavior
		 * that git(1) exhibits, although it doesn't seem to be officially
		 * documented.
		 */
		if (tdir && git__strcmp(tdir, "") != 0) {
1800 1801 1802
			uint32_t cpflags = GIT_CPDIR_COPY_SYMLINKS |
				GIT_CPDIR_SIMPLE_TO_MODE |
				GIT_CPDIR_COPY_DOTFILES;
1803 1804 1805
			if (opts->mode != GIT_REPOSITORY_INIT_SHARED_UMASK)
					cpflags |= GIT_CPDIR_CHMOD_DIRS;
			error = git_futils_cp_r(tdir, repo_dir, cpflags, dmode);
1806
		}
1807

1808
		git_buf_dispose(&template_buf);
Linquize committed
1809
		git_config_free(cfg);
1810

1811
		if (error < 0) {
Linquize committed
1812
			if (!default_template)
1813 1814 1815 1816 1817
				return error;

			/* if template was default, ignore error and use internal */
			giterr_clear();
			external_tpl = false;
1818
			error = 0;
1819 1820 1821 1822 1823 1824 1825 1826
		}
	}

	/* Copy internal template
	 * - always ensure existence of dirs
	 * - only create files if no external template was specified
	 */
	for (tpl = repo_template; !error && tpl->path; ++tpl) {
1827
		if (!tpl->content) {
1828 1829 1830 1831
			uint32_t mkdir_flags = GIT_MKDIR_PATH;
			if (chmod)
				mkdir_flags |= GIT_MKDIR_CHMOD;

1832 1833
			error = git_futils_mkdir_relative(
				tpl->path, repo_dir, dmode, mkdir_flags, NULL);
1834
		}
1835
		else if (!external_tpl) {
1836 1837 1838 1839 1840
			const char *content = tpl->content;

			if (opts->description && strcmp(tpl->path, GIT_DESC_FILE) == 0)
				content = opts->description;

1841 1842
			error = repo_write_template(
				repo_dir, false, tpl->path, tpl->mode, false, content);
1843
		}
1844 1845
	}

1846
	return error;
1847 1848
}

1849 1850
static int mkdir_parent(git_buf *buf, uint32_t mode, bool skip2)
{
1851 1852 1853
	/* When making parent directories during repository initialization
	 * don't try to set gid or grant world write access
	 */
1854
	return git_futils_mkdir(
1855
		buf->ptr, mode & ~(S_ISGID | 0002),
1856 1857 1858 1859
		GIT_MKDIR_PATH | GIT_MKDIR_VERIFY_DIR |
		(skip2 ? GIT_MKDIR_SKIP_LAST2 : GIT_MKDIR_SKIP_LAST));
}

1860 1861 1862 1863 1864
static int repo_init_directories(
	git_buf *repo_path,
	git_buf *wd_path,
	const char *given_repo,
	git_repository_init_options *opts)
1865
{
1866
	int error = 0;
1867
	bool is_bare, add_dotgit, has_dotgit, natural_wd;
1868
	mode_t dirmode;
1869

1870 1871 1872 1873 1874 1875 1876 1877 1878 1879 1880 1881 1882 1883 1884 1885 1886 1887 1888 1889
	/* There are three possible rules for what we are allowed to create:
	 * - MKPATH means anything we need
	 * - MKDIR means just the .git directory and its parent and the workdir
	 * - Neither means only the .git directory can be created
	 *
	 * There are 5 "segments" of path that we might need to deal with:
	 * 1. The .git directory
	 * 2. The parent of the .git directory
	 * 3. Everything above the parent of the .git directory
	 * 4. The working directory (often the same as #2)
	 * 5. Everything above the working directory (often the same as #3)
	 *
	 * For all directories created, we start with the init_mode value for
	 * permissions and then strip off bits in some cases:
	 *
	 * For MKPATH, we create #3 (and #5) paths without S_ISGID or S_IWOTH
	 * For MKPATH and MKDIR, we create #2 (and #4) without S_ISGID
	 * For all rules, we create #1 using the untouched init_mode
	 */

1890
	/* set up repo path */
1891

1892 1893
	is_bare = ((opts->flags & GIT_REPOSITORY_INIT_BARE) != 0);

1894 1895
	add_dotgit =
		(opts->flags & GIT_REPOSITORY_INIT_NO_DOTGIT_DIR) == 0 &&
1896
		!is_bare &&
1897 1898
		git__suffixcmp(given_repo, "/" DOT_GIT) != 0 &&
		git__suffixcmp(given_repo, "/" GIT_DIR) != 0;
1899

1900 1901
	if (git_buf_joinpath(repo_path, given_repo, add_dotgit ? GIT_DIR : "") < 0)
		return -1;
1902

1903 1904 1905 1906 1907 1908
	has_dotgit = (git__suffixcmp(repo_path->ptr, "/" GIT_DIR) == 0);
	if (has_dotgit)
		opts->flags |= GIT_REPOSITORY_INIT__HAS_DOTGIT;

	/* set up workdir path */

1909
	if (!is_bare) {
1910
		if (opts->workdir_path) {
1911 1912 1913
			if (git_path_join_unrooted(
					wd_path, opts->workdir_path, repo_path->ptr, NULL) < 0)
				return -1;
1914 1915 1916 1917
		} else if (has_dotgit) {
			if (git_path_dirname_r(wd_path, repo_path->ptr) < 0)
				return -1;
		} else {
1918
			giterr_set(GITERR_REPOSITORY, "cannot pick working directory"
1919 1920 1921
				" for non-bare repository that isn't a '.git' directory");
			return -1;
		}
1922

1923 1924 1925 1926 1927 1928 1929 1930 1931 1932 1933 1934 1935 1936 1937 1938
		if (git_path_to_dir(wd_path) < 0)
			return -1;
	} else {
		git_buf_clear(wd_path);
	}

	natural_wd =
		has_dotgit &&
		wd_path->size > 0 &&
		wd_path->size + strlen(GIT_DIR) == repo_path->size &&
		memcmp(repo_path->ptr, wd_path->ptr, wd_path->size) == 0;
	if (natural_wd)
		opts->flags |= GIT_REPOSITORY_INIT__NATURAL_WD;

	/* create directories as needed / requested */

1939
	dirmode = pick_dir_mode(opts);
1940

1941 1942 1943 1944 1945 1946 1947 1948 1949 1950 1951 1952 1953 1954 1955 1956 1957 1958
	if ((opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0) {
		/* create path #5 */
		if (wd_path->size > 0 &&
			(error = mkdir_parent(wd_path, dirmode, false)) < 0)
			return error;

		/* create path #3 (if not the same as #5) */
		if (!natural_wd &&
			(error = mkdir_parent(repo_path, dirmode, has_dotgit)) < 0)
			return error;
	}

	if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 ||
		(opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0)
	{
		/* create path #4 */
		if (wd_path->size > 0 &&
			(error = git_futils_mkdir(
1959
				wd_path->ptr, dirmode & ~S_ISGID,
1960 1961 1962 1963 1964 1965
				GIT_MKDIR_VERIFY_DIR)) < 0)
			return error;

		/* create path #2 (if not the same as #4) */
		if (!natural_wd &&
			(error = git_futils_mkdir(
1966
				repo_path->ptr, dirmode & ~S_ISGID,
1967 1968
				GIT_MKDIR_VERIFY_DIR | GIT_MKDIR_SKIP_LAST)) < 0)
			return error;
1969 1970
	}

1971 1972 1973 1974
	if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 ||
		(opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0 ||
		has_dotgit)
	{
1975
		/* create path #1 */
1976
		error = git_futils_mkdir(repo_path->ptr, dirmode,
1977
			GIT_MKDIR_VERIFY_DIR | ((dirmode & S_ISGID) ? GIT_MKDIR_CHMOD : 0));
1978
	}
1979

1980 1981 1982 1983 1984 1985 1986 1987 1988 1989 1990 1991 1992 1993 1994 1995 1996
	/* prettify both directories now that they are created */

	if (!error) {
		error = git_path_prettify_dir(repo_path, repo_path->ptr, NULL);

		if (!error && wd_path->size > 0)
			error = git_path_prettify_dir(wd_path, wd_path->ptr, NULL);
	}

	return error;
}

static int repo_init_create_origin(git_repository *repo, const char *url)
{
	int error;
	git_remote *remote;

Ben Straub committed
1997
	if (!(error = git_remote_create(&remote, repo, GIT_REMOTE_ORIGIN, url))) {
1998 1999 2000 2001 2002 2003 2004 2005 2006
		git_remote_free(remote);
	}

	return error;
}

int git_repository_init(
	git_repository **repo_out, const char *path, unsigned is_bare)
{
2007
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
2008 2009 2010 2011 2012 2013 2014 2015 2016

	opts.flags = GIT_REPOSITORY_INIT_MKPATH; /* don't love this default */
	if (is_bare)
		opts.flags |= GIT_REPOSITORY_INIT_BARE;

	return git_repository_init_ext(repo_out, path, &opts);
}

int git_repository_init_ext(
2017
	git_repository **out,
2018 2019 2020 2021
	const char *given_repo,
	git_repository_init_options *opts)
{
	int error;
2022 2023
	git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT,
		common_path = GIT_BUF_INIT;
2024
	const char *wd;
2025

2026
	assert(out && given_repo && opts);
2027

Ben Straub committed
2028
	GITERR_CHECK_VERSION(opts, GIT_REPOSITORY_INIT_OPTIONS_VERSION, "git_repository_init_options");
2029

2030 2031
	error = repo_init_directories(&repo_path, &wd_path, given_repo, opts);
	if (error < 0)
2032
		goto cleanup;
2033

2034
	wd = (opts->flags & GIT_REPOSITORY_INIT_BARE) ? NULL : git_buf_cstr(&wd_path);
2035
	if (valid_repository_path(&repo_path, &common_path)) {
2036 2037 2038

		if ((opts->flags & GIT_REPOSITORY_INIT_NO_REINIT) != 0) {
			giterr_set(GITERR_REPOSITORY,
2039
				"attempt to reinitialize '%s'", given_repo);
2040 2041 2042 2043 2044 2045
			error = GIT_EEXISTS;
			goto cleanup;
		}

		opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT;

2046
		error = repo_init_config(
2047
			repo_path.ptr, wd, opts->flags, opts->mode);
2048 2049 2050 2051 2052

		/* TODO: reinitialize the templates */
	}
	else {
		if (!(error = repo_init_structure(
2053
				repo_path.ptr, wd, opts)) &&
2054
			!(error = repo_init_config(
2055
				repo_path.ptr, wd, opts->flags, opts->mode)))
2056
			error = git_repository_create_head(
2057
				repo_path.ptr, opts->initial_head);
2058
	}
2059 2060 2061
	if (error < 0)
		goto cleanup;

2062
	error = git_repository_open(out, repo_path.ptr);
2063

2064
	if (!error && opts->origin_url)
2065
		error = repo_init_create_origin(*out, opts->origin_url);
2066 2067

cleanup:
2068 2069 2070
	git_buf_dispose(&common_path);
	git_buf_dispose(&repo_path);
	git_buf_dispose(&wd_path);
2071 2072

	return error;
2073
}
2074

2075
int git_repository_head_detached(git_repository *repo)
2076 2077
{
	git_reference *ref;
2078
	git_odb *odb = NULL;
2079
	int exists;
2080

2081 2082
	if (git_repository_odb__weakptr(&odb, repo) < 0)
		return -1;
2083

2084 2085
	if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0)
		return -1;
2086

2087 2088
	if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
		git_reference_free(ref);
2089
		return 0;
2090
	}
2091

2092
	exists = git_odb_exists(odb, git_reference_target(ref));
2093 2094

	git_reference_free(ref);
2095
	return exists;
2096 2097
}

2098 2099 2100 2101 2102 2103
static int get_worktree_file_path(git_buf *out, git_repository *repo, const char *worktree, const char *file)
{
	git_buf_clear(out);
	return git_buf_printf(out, "%s/worktrees/%s/%s", repo->commondir, worktree, file);
}

2104 2105
int git_repository_head_detached_for_worktree(git_repository *repo, const char *name)
{
2106 2107
	git_reference *ref = NULL;
	int error;
2108 2109 2110

	assert(repo && name);

2111 2112
	if ((error = git_repository_head_for_worktree(&ref, repo, name)) < 0)
		goto out;
2113

2114 2115 2116
	error = (git_reference_type(ref) != GIT_REF_SYMBOLIC);
out:
	git_reference_free(ref);
2117

2118
	return error;
2119 2120
}

2121
int git_repository_head(git_reference **head_out, git_repository *repo)
2122
{
2123
	git_reference *head;
2124 2125
	int error;

2126 2127 2128 2129 2130 2131 2132 2133
	if ((error = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0)
		return error;

	if (git_reference_type(head) == GIT_REF_OID) {
		*head_out = head;
		return 0;
	}

2134
	error = git_reference_lookup_resolved(head_out, repo, git_reference_symbolic_target(head), -1);
2135
	git_reference_free(head);
2136

2137
	return error == GIT_ENOTFOUND ? GIT_EUNBORNBRANCH : error;
2138 2139
}

2140 2141
int git_repository_head_for_worktree(git_reference **out, git_repository *repo, const char *name)
{
2142 2143 2144
	git_buf path = GIT_BUF_INIT;
	git_reference *head = NULL;
	int error;
2145 2146 2147 2148 2149

	assert(out && repo && name);

	*out = NULL;

2150 2151
	if ((error = get_worktree_file_path(&path, repo, name, GIT_HEAD_FILE)) < 0 ||
	    (error = git_reference__read_head(&head, repo, path.ptr)) < 0)
2152 2153
		goto out;

2154 2155
	if (git_reference_type(head) != GIT_REF_OID) {
		git_reference *resolved;
2156

2157 2158 2159
		error = git_reference_lookup_resolved(&resolved, repo, git_reference_symbolic_target(head), -1);
		git_reference_free(head);
		head = resolved;
2160 2161
	}

2162
	*out = head;
2163 2164

out:
2165 2166
	if (error)
		git_reference_free(head);
2167

2168
	git_buf_dispose(&path);
2169

2170
	return error;
2171 2172
}

2173 2174 2175 2176 2177 2178 2179 2180 2181 2182 2183 2184 2185 2186 2187 2188 2189 2190 2191 2192 2193 2194 2195 2196 2197 2198 2199
int git_repository_foreach_head(git_repository *repo, git_repository_foreach_head_cb cb, void *payload)
{
	git_strarray worktrees = GIT_VECTOR_INIT;
	git_buf path = GIT_BUF_INIT;
	int error;
	size_t i;

	/* Execute callback for HEAD of commondir */
	if ((error = git_buf_joinpath(&path, repo->commondir, GIT_HEAD_FILE)) < 0 ||
	    (error = cb(repo, path.ptr, payload) != 0))
		goto out;

	if ((error = git_worktree_list(&worktrees, repo)) < 0) {
		error = 0;
		goto out;
	}

	/* Execute callback for all worktree HEADs */
	for (i = 0; i < worktrees.count; i++) {
		if (get_worktree_file_path(&path, repo, worktrees.strings[i], GIT_HEAD_FILE) < 0)
			continue;

		if ((error = cb(repo, path.ptr, payload)) != 0)
			goto out;
	}

out:
2200
	git_buf_dispose(&path);
2201 2202 2203 2204
	git_strarray_free(&worktrees);
	return error;
}

2205
int git_repository_head_unborn(git_repository *repo)
2206
{
2207
	git_reference *ref = NULL;
2208 2209 2210
	int error;

	error = git_repository_head(&ref, repo);
2211
	git_reference_free(ref);
2212

2213 2214
	if (error == GIT_EUNBORNBRANCH) {
		giterr_clear();
2215
		return 1;
2216
	}
2217

2218 2219 2220 2221
	if (error < 0)
		return -1;

	return 0;
2222
}
2223

2224
static int at_least_one_cb(const char *refname, void *payload)
2225
{
2226 2227
	GIT_UNUSED(refname);
	GIT_UNUSED(payload);
2228
	return GIT_PASSTHROUGH;
2229
}
2230

2231 2232
static int repo_contains_no_reference(git_repository *repo)
{
2233
	int error = git_reference_foreach_name(repo, &at_least_one_cb, NULL);
2234

2235
	if (error == GIT_PASSTHROUGH)
2236
		return 0;
2237

2238 2239
	if (!error)
		return 1;
2240

2241
	return error;
2242
}
2243

2244 2245 2246
int git_repository_is_empty(git_repository *repo)
{
	git_reference *head = NULL;
2247
	int is_empty = 0;
2248

2249
	if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0)
2250 2251
		return -1;

2252 2253 2254 2255 2256
	if (git_reference_type(head) == GIT_REF_SYMBOLIC)
		is_empty =
			(strcmp(git_reference_symbolic_target(head),
					GIT_REFS_HEADS_DIR "master") == 0) &&
			repo_contains_no_reference(repo);
2257 2258

	git_reference_free(head);
2259 2260

	return is_empty;
2261 2262
}

2263
int git_repository_item_path(git_buf *out, const git_repository *repo, git_repository_item_t item)
2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 2275 2276 2277
{
	const char *parent;

	switch (items[item].parent) {
		case GIT_REPOSITORY_ITEM_GITDIR:
			parent = git_repository_path(repo);
			break;
		case GIT_REPOSITORY_ITEM_WORKDIR:
			parent = git_repository_workdir(repo);
			break;
		case GIT_REPOSITORY_ITEM_COMMONDIR:
			parent = git_repository_commondir(repo);
			break;
		default:
2278
			giterr_set(GITERR_INVALID, "invalid item directory");
2279 2280 2281 2282
			return -1;
	}

	if (parent == NULL) {
2283
		giterr_set(GITERR_INVALID, "path cannot exist in repository");
2284
		return GIT_ENOTFOUND;
2285 2286 2287 2288 2289 2290 2291 2292 2293 2294 2295 2296 2297 2298 2299 2300 2301 2302
	}

	if (git_buf_sets(out, parent) < 0)
		return -1;

	if (items[item].name) {
		if (git_buf_joinpath(out, parent, items[item].name) < 0)
			return -1;
	}

	if (items[item].directory) {
		if (git_path_to_dir(out) < 0)
			return -1;
	}

	return 0;
}

2303
const char *git_repository_path(const git_repository *repo)
2304 2305
{
	assert(repo);
2306
	return repo->gitdir;
2307
}
2308

2309
const char *git_repository_workdir(const git_repository *repo)
2310 2311
{
	assert(repo);
2312

2313 2314
	if (repo->is_bare)
		return NULL;
2315

2316 2317
	return repo->workdir;
}
2318

2319
const char *git_repository_commondir(const git_repository *repo)
2320 2321 2322 2323 2324
{
	assert(repo);
	return repo->commondir;
}

2325 2326
int git_repository_set_workdir(
	git_repository *repo, const char *workdir, int update_gitlink)
2327
{
2328
	int error = 0;
2329 2330
	git_buf path = GIT_BUF_INIT;

2331
	assert(repo && workdir);
2332

2333 2334
	if (git_path_prettify_dir(&path, workdir, NULL) < 0)
		return -1;
2335

2336 2337
	if (repo->workdir && strcmp(repo->workdir, path.ptr) == 0)
		return 0;
2338

2339 2340 2341 2342 2343 2344
	if (update_gitlink) {
		git_config *config;

		if (git_repository_config__weakptr(&config, repo) < 0)
			return -1;

2345
		error = repo_write_gitlink(path.ptr, git_repository_path(repo), false);
2346 2347 2348

		/* passthrough error means gitlink is unnecessary */
		if (error == GIT_PASSTHROUGH)
Ben Straub committed
2349
			error = git_config_delete_entry(config, "core.worktree");
2350 2351 2352 2353 2354 2355 2356 2357 2358 2359 2360 2361 2362 2363 2364 2365 2366
		else if (!error)
			error = git_config_set_string(config, "core.worktree", path.ptr);

		if (!error)
			error = git_config_set_bool(config, "core.bare", false);
	}

	if (!error) {
		char *old_workdir = repo->workdir;

		repo->workdir = git_buf_detach(&path);
		repo->is_bare = 0;

		git__free(old_workdir);
	}

	return error;
2367
}
2368

2369
int git_repository_is_bare(const git_repository *repo)
2370 2371 2372 2373
{
	assert(repo);
	return repo->is_bare;
}
2374

2375
int git_repository_is_worktree(const git_repository *repo)
2376 2377 2378 2379 2380
{
	assert(repo);
	return repo->is_worktree;
}

2381 2382 2383 2384 2385 2386 2387 2388 2389 2390
int git_repository_set_bare(git_repository *repo)
{
	int error;
	git_config *config;

	assert(repo);

	if (repo->is_bare)
		return 0;

2391 2392 2393
	if ((error = git_repository_config__weakptr(&config, repo)) < 0)
		return error;

2394
	if ((error = git_config_set_bool(config, "core.bare", true)) < 0)
2395
		return error;
2396

2397 2398
	if ((error = git_config__update_entry(config, "core.worktree", NULL, true, true)) < 0)
		return error;
2399 2400 2401 2402 2403

	git__free(repo->workdir);
	repo->workdir = NULL;
	repo->is_bare = 1;

2404
	return 0;
2405 2406
}

2407 2408
int git_repository_head_tree(git_tree **tree, git_repository *repo)
{
2409 2410 2411
	git_reference *head;
	git_object *obj;
	int error;
2412

2413 2414
	if ((error = git_repository_head(&head, repo)) < 0)
		return error;
2415

2416 2417
	if ((error = git_reference_peel(&obj, head, GIT_OBJ_TREE)) < 0)
		goto cleanup;
2418 2419

	*tree = (git_tree *)obj;
2420 2421 2422 2423

cleanup:
	git_reference_free(head);
	return error;
2424
}
2425

2426 2427 2428 2429 2430 2431 2432 2433 2434
int git_repository__set_orig_head(git_repository *repo, const git_oid *orig_head)
{
	git_filebuf file = GIT_FILEBUF_INIT;
	git_buf file_path = GIT_BUF_INIT;
	char orig_head_str[GIT_OID_HEXSZ];
	int error = 0;

	git_oid_fmt(orig_head_str, orig_head);

2435
	if ((error = git_buf_joinpath(&file_path, repo->gitdir, GIT_ORIG_HEAD_FILE)) == 0 &&
2436 2437 2438 2439 2440 2441 2442
		(error = git_filebuf_open(&file, file_path.ptr, GIT_FILEBUF_FORCE, GIT_MERGE_FILE_MODE)) == 0 &&
		(error = git_filebuf_printf(&file, "%.*s\n", GIT_OID_HEXSZ, orig_head_str)) == 0)
		error = git_filebuf_commit(&file);

	if (error < 0)
		git_filebuf_cleanup(&file);

2443
	git_buf_dispose(&file_path);
2444 2445 2446 2447

	return error;
}

2448
int git_repository_message(git_buf *out, git_repository *repo)
2449
{
2450
	git_buf path = GIT_BUF_INIT;
Vicent Marti committed
2451 2452
	struct stat st;
	int error;
2453

2454
	git_buf_sanitize(out);
2455

2456
	if (git_buf_joinpath(&path, repo->gitdir, GIT_MERGE_MSG_FILE) < 0)
Vicent Marti committed
2457
		return -1;
2458

2459
	if ((error = p_stat(git_buf_cstr(&path), &st)) < 0) {
2460 2461
		if (errno == ENOENT)
			error = GIT_ENOTFOUND;
2462
		giterr_set(GITERR_OS, "could not access message file");
2463 2464
	} else {
		error = git_futils_readbuffer(out, git_buf_cstr(&path));
Vicent Marti committed
2465
	}
2466

2467
	git_buf_dispose(&path);
2468 2469

	return error;
2470 2471 2472 2473
}

int git_repository_message_remove(git_repository *repo)
{
Vicent Marti committed
2474 2475
	git_buf path = GIT_BUF_INIT;
	int error;
2476

2477
	if (git_buf_joinpath(&path, repo->gitdir, GIT_MERGE_MSG_FILE) < 0)
Vicent Marti committed
2478
		return -1;
2479 2480

	error = p_unlink(git_buf_cstr(&path));
2481
	git_buf_dispose(&path);
2482 2483 2484

	return error;
}
2485 2486

int git_repository_hashfile(
Linquize committed
2487 2488 2489 2490 2491
	git_oid *out,
	git_repository *repo,
	const char *path,
	git_otype type,
	const char *as_path)
2492 2493
{
	int error;
2494
	git_filter_list *fl = NULL;
2495
	git_file fd = -1;
2496 2497 2498
	git_off_t len;
	git_buf full_path = GIT_BUF_INIT;

2499 2500 2501 2502 2503 2504
	assert(out && path && repo); /* as_path can be NULL */

	/* At some point, it would be nice if repo could be NULL to just
	 * apply filter rules defined in system and global files, but for
	 * now that is not possible because git_filters_load() needs it.
	 */
2505 2506

	error = git_path_join_unrooted(
2507
		&full_path, path, git_repository_workdir(repo), NULL);
2508 2509 2510 2511 2512 2513 2514 2515
	if (error < 0)
		return error;

	if (!as_path)
		as_path = path;

	/* passing empty string for "as_path" indicated --no-filters */
	if (strlen(as_path) > 0) {
Russell Belfer committed
2516
		error = git_filter_list_load(
2517
			&fl, repo, NULL, as_path,
2518
			GIT_FILTER_TO_ODB, GIT_FILTER_DEFAULT);
2519 2520 2521 2522 2523 2524 2525 2526 2527 2528 2529 2530 2531 2532 2533 2534
		if (error < 0)
			return error;
	} else {
		error = 0;
	}

	/* at this point, error is a count of the number of loaded filters */

	fd = git_futils_open_ro(full_path.ptr);
	if (fd < 0) {
		error = fd;
		goto cleanup;
	}

	len = git_futils_filesize(fd);
	if (len < 0) {
2535
		error = (int)len;
2536 2537 2538 2539
		goto cleanup;
	}

	if (!git__is_sizet(len)) {
2540
		giterr_set(GITERR_OS, "file size overflow for 32-bit systems");
2541 2542 2543 2544
		error = -1;
		goto cleanup;
	}

2545
	error = git_odb__hashfd_filtered(out, fd, (size_t)len, type, fl);
2546 2547

cleanup:
2548 2549
	if (fd >= 0)
		p_close(fd);
2550
	git_filter_list_free(fl);
2551
	git_buf_dispose(&full_path);
2552 2553 2554 2555

	return error;
}

2556
static int checkout_message(git_buf *out, git_reference *old, const char *new)
2557
{
2558 2559 2560 2561 2562 2563 2564 2565 2566
	git_buf_puts(out, "checkout: moving from ");

	if (git_reference_type(old) == GIT_REF_SYMBOLIC)
		git_buf_puts(out, git_reference__shorthand(git_reference_symbolic_target(old)));
	else
		git_buf_puts(out, git_oid_tostr_s(git_reference_target(old)));

	git_buf_puts(out, " to ");

2567 2568 2569
	if (git_reference__is_branch(new) ||
		git_reference__is_tag(new) ||
		git_reference__is_remote(new))
2570 2571 2572 2573 2574 2575 2576 2577
		git_buf_puts(out, git_reference__shorthand(new));
	else
		git_buf_puts(out, new);

	if (git_buf_oom(out))
		return -1;

	return 0;
2578 2579
}

2580 2581 2582 2583 2584 2585 2586 2587 2588 2589 2590 2591 2592 2593 2594 2595 2596 2597 2598 2599 2600 2601 2602 2603 2604 2605 2606
static int detach(git_repository *repo, const git_oid *id, const char *new)
{
	int error;
	git_buf log_message = GIT_BUF_INIT;
	git_object *object = NULL, *peeled = NULL;
	git_reference *new_head = NULL, *current = NULL;

	assert(repo && id);

	if ((error = git_reference_lookup(&current, repo, GIT_HEAD_FILE)) < 0)
		return error;

	if ((error = git_object_lookup(&object, repo, id, GIT_OBJ_ANY)) < 0)
		goto cleanup;

	if ((error = git_object_peel(&peeled, object, GIT_OBJ_COMMIT)) < 0)
		goto cleanup;

	if (new == NULL)
		new = git_oid_tostr_s(git_object_id(peeled));

	if ((error = checkout_message(&log_message, current, new)) < 0)
		goto cleanup;

	error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), true, git_buf_cstr(&log_message));

cleanup:
2607
	git_buf_dispose(&log_message);
2608 2609 2610 2611 2612 2613 2614
	git_object_free(object);
	git_object_free(peeled);
	git_reference_free(current);
	git_reference_free(new_head);
	return error;
}

2615 2616
int git_repository_set_head(
	git_repository* repo,
2617
	const char* refname)
2618
{
2619 2620
	git_reference *ref = NULL, *current = NULL, *new_head = NULL;
	git_buf log_message = GIT_BUF_INIT;
2621 2622 2623 2624
	int error;

	assert(repo && refname);

2625 2626 2627 2628 2629 2630
	if ((error = git_reference_lookup(&current, repo, GIT_HEAD_FILE)) < 0)
		return error;

	if ((error = checkout_message(&log_message, current, refname)) < 0)
		goto cleanup;

2631 2632
	error = git_reference_lookup(&ref, repo, refname);
	if (error < 0 && error != GIT_ENOTFOUND)
2633
		goto cleanup;
2634

2635 2636
	if (ref && current->type == GIT_REF_SYMBOLIC && git__strcmp(current->target.symbolic, ref->name) &&
	    git_reference_is_branch(ref) && git_branch_is_checked_out(ref)) {
2637 2638
		giterr_set(GITERR_REPOSITORY, "cannot set HEAD to reference '%s' as it is the current HEAD "
			"of a linked repository.", git_reference_name(ref));
2639 2640 2641 2642
		error = -1;
		goto cleanup;
	}

2643
	if (!error) {
2644 2645
		if (git_reference_is_branch(ref)) {
			error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE,
2646
					git_reference_name(ref), true, git_buf_cstr(&log_message));
2647
		} else {
2648
			error = detach(repo, git_reference_target(ref),
2649
				git_reference_is_tag(ref) || git_reference_is_remote(ref) ? refname : NULL);
2650
		}
2651
	} else if (git_reference__is_branch(refname)) {
2652
		error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, refname,
2653
				true, git_buf_cstr(&log_message));
2654
	}
2655

2656
cleanup:
2657
	git_buf_dispose(&log_message);
2658
	git_reference_free(current);
2659 2660 2661 2662 2663
	git_reference_free(ref);
	git_reference_free(new_head);
	return error;
}

2664 2665 2666 2667 2668 2669 2670 2671 2672 2673 2674 2675 2676
int git_repository_set_head_detached(
	git_repository* repo,
	const git_oid* commitish)
{
	return detach(repo, commitish, NULL);
}

int git_repository_set_head_detached_from_annotated(
	git_repository *repo,
	const git_annotated_commit *commitish)
{
	assert(repo && commitish);

2677
	return detach(repo, git_annotated_commit_id(commitish), commitish->description);
2678 2679
}

2680
int git_repository_detach_head(git_repository* repo)
2681
{
2682
	git_reference *old_head = NULL,	*new_head = NULL, *current = NULL;
2683
	git_object *object = NULL;
2684
	git_buf log_message = GIT_BUF_INIT;
2685
	int error;
2686 2687 2688

	assert(repo);

2689
	if ((error = git_reference_lookup(&current, repo, GIT_HEAD_FILE)) < 0)
2690
		return error;
2691

2692 2693 2694
	if ((error = git_repository_head(&old_head, repo)) < 0)
		goto cleanup;

2695
	if ((error = git_object_lookup(&object, repo, git_reference_target(old_head), GIT_OBJ_COMMIT)) < 0)
2696 2697
		goto cleanup;

2698 2699 2700
	if ((error = checkout_message(&log_message, current, git_oid_tostr_s(git_object_id(object)))) < 0)
		goto cleanup;

2701
	error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_reference_target(old_head),
2702
			1, git_buf_cstr(&log_message));
2703 2704

cleanup:
2705
	git_buf_dispose(&log_message);
2706 2707 2708
	git_object_free(object);
	git_reference_free(old_head);
	git_reference_free(new_head);
2709
	git_reference_free(current);
2710 2711
	return error;
}
Edward Thomson committed
2712

2713 2714 2715 2716
/**
 * Loosely ported from git.git
 * https://github.com/git/git/blob/master/contrib/completion/git-prompt.sh#L198-289
 */
Edward Thomson committed
2717 2718 2719 2720 2721 2722 2723
int git_repository_state(git_repository *repo)
{
	git_buf repo_path = GIT_BUF_INIT;
	int state = GIT_REPOSITORY_STATE_NONE;

	assert(repo);

2724
	if (git_buf_puts(&repo_path, repo->gitdir) < 0)
Edward Thomson committed
2725 2726
		return -1;

2727 2728 2729 2730 2731 2732 2733 2734 2735 2736 2737
	if (git_path_contains_file(&repo_path, GIT_REBASE_MERGE_INTERACTIVE_FILE))
		state = GIT_REPOSITORY_STATE_REBASE_INTERACTIVE;
	else if (git_path_contains_dir(&repo_path, GIT_REBASE_MERGE_DIR))
		state = GIT_REPOSITORY_STATE_REBASE_MERGE;
	else if (git_path_contains_file(&repo_path, GIT_REBASE_APPLY_REBASING_FILE))
		state = GIT_REPOSITORY_STATE_REBASE;
	else if (git_path_contains_file(&repo_path, GIT_REBASE_APPLY_APPLYING_FILE))
		state = GIT_REPOSITORY_STATE_APPLY_MAILBOX;
	else if (git_path_contains_dir(&repo_path, GIT_REBASE_APPLY_DIR))
		state = GIT_REPOSITORY_STATE_APPLY_MAILBOX_OR_REBASE;
	else if (git_path_contains_file(&repo_path, GIT_MERGE_HEAD_FILE))
Edward Thomson committed
2738
		state = GIT_REPOSITORY_STATE_MERGE;
2739
	else if (git_path_contains_file(&repo_path, GIT_REVERT_HEAD_FILE)) {
Edward Thomson committed
2740
		state = GIT_REPOSITORY_STATE_REVERT;
2741 2742 2743 2744
		if (git_path_contains_file(&repo_path, GIT_SEQUENCER_TODO_FILE)) {
			state = GIT_REPOSITORY_STATE_REVERT_SEQUENCE;
		}
	} else if (git_path_contains_file(&repo_path, GIT_CHERRYPICK_HEAD_FILE)) {
2745
		state = GIT_REPOSITORY_STATE_CHERRYPICK;
2746 2747 2748 2749
		if (git_path_contains_file(&repo_path, GIT_SEQUENCER_TODO_FILE)) {
			state = GIT_REPOSITORY_STATE_CHERRYPICK_SEQUENCE;
		}
	} else if (git_path_contains_file(&repo_path, GIT_BISECT_LOG_FILE))
2750
		state = GIT_REPOSITORY_STATE_BISECT;
Edward Thomson committed
2751

2752
	git_buf_dispose(&repo_path);
Edward Thomson committed
2753 2754
	return state;
}
Ben Straub committed
2755

2756 2757
int git_repository__cleanup_files(
	git_repository *repo, const char *files[], size_t files_len)
2758
{
2759
	git_buf buf = GIT_BUF_INIT;
2760
	size_t i;
2761
	int error;
2762

2763 2764
	for (error = 0, i = 0; !error && i < files_len; ++i) {
		const char *path;
2765

2766
		if (git_buf_joinpath(&buf, repo->gitdir, files[i]) < 0)
2767
			return -1;
2768

2769 2770 2771 2772 2773 2774 2775 2776
		path = git_buf_cstr(&buf);

		if (git_path_isfile(path)) {
			error = p_unlink(path);
		} else if (git_path_isdir(path)) {
			error = git_futils_rmdir_r(path, NULL,
				GIT_RMDIR_REMOVE_FILES | GIT_RMDIR_REMOVE_BLOCKERS);
		}
2777

2778 2779
		git_buf_clear(&buf);
	}
2780

2781
	git_buf_dispose(&buf);
2782 2783 2784 2785 2786 2787 2788 2789
	return error;
}

static const char *state_files[] = {
	GIT_MERGE_HEAD_FILE,
	GIT_MERGE_MODE_FILE,
	GIT_MERGE_MSG_FILE,
	GIT_REVERT_HEAD_FILE,
2790
	GIT_CHERRYPICK_HEAD_FILE,
2791 2792 2793
	GIT_BISECT_LOG_FILE,
	GIT_REBASE_MERGE_DIR,
	GIT_REBASE_APPLY_DIR,
2794
	GIT_SEQUENCER_DIR,
2795 2796 2797 2798 2799 2800 2801 2802 2803
};

int git_repository_state_cleanup(git_repository *repo)
{
	assert(repo);

	return git_repository__cleanup_files(repo, state_files, ARRAY_SIZE(state_files));
}

Ben Straub committed
2804 2805 2806 2807
int git_repository_is_shallow(git_repository *repo)
{
	git_buf path = GIT_BUF_INIT;
	struct stat st;
Ben Straub committed
2808
	int error;
Ben Straub committed
2809

2810
	if ((error = git_buf_joinpath(&path, repo->gitdir, "shallow")) < 0)
2811 2812
		return error;

Ben Straub committed
2813
	error = git_path_lstat(path.ptr, &st);
2814
	git_buf_dispose(&path);
Ben Straub committed
2815

2816 2817
	if (error == GIT_ENOTFOUND) {
		giterr_clear();
Ben Straub committed
2818
		return 0;
2819 2820
	}

Ben Straub committed
2821 2822 2823
	if (error < 0)
		return error;
	return st.st_size == 0 ? 0 : 1;
Ben Straub committed
2824
}
2825

2826 2827
int git_repository_init_init_options(
	git_repository_init_options *opts, unsigned int version)
2828
{
2829 2830 2831 2832
	GIT_INIT_STRUCTURE_FROM_TEMPLATE(
		opts, version, git_repository_init_options,
		GIT_REPOSITORY_INIT_OPTIONS_INIT);
	return 0;
2833
}
2834 2835 2836 2837 2838 2839 2840 2841 2842 2843 2844 2845 2846 2847 2848 2849 2850 2851 2852 2853 2854 2855 2856 2857 2858 2859 2860 2861 2862 2863 2864

int git_repository_ident(const char **name, const char **email, const git_repository *repo)
{
	*name = repo->ident_name;
	*email = repo->ident_email;

	return 0;
}

int git_repository_set_ident(git_repository *repo, const char *name, const char *email)
{
	char *tmp_name = NULL, *tmp_email = NULL;

	if (name) {
		tmp_name = git__strdup(name);
		GITERR_CHECK_ALLOC(tmp_name);
	}

	if (email) {
		tmp_email = git__strdup(email);
		GITERR_CHECK_ALLOC(tmp_email);
	}

	tmp_name = git__swap(repo->ident_name, tmp_name);
	tmp_email = git__swap(repo->ident_email, tmp_email);

	git__free(tmp_name);
	git__free(tmp_email);

	return 0;
}
2865 2866 2867 2868 2869 2870 2871 2872 2873 2874 2875 2876 2877 2878 2879 2880 2881 2882 2883 2884 2885 2886 2887 2888 2889 2890 2891 2892

int git_repository_submodule_cache_all(git_repository *repo)
{
	int error;

	assert(repo);

	if ((error = git_strmap_alloc(&repo->submodule_cache)))
		return error;

	error = git_submodule__map(repo, repo->submodule_cache);
	return error;
}

int git_repository_submodule_cache_clear(git_repository *repo)
{
	git_submodule *sm;
	assert(repo);
	if (repo->submodule_cache == NULL) {
		return 0;
	}
	git_strmap_foreach_value(repo->submodule_cache, sm, {
		git_submodule_free(sm);
	});
	git_strmap_free(repo->submodule_cache);
	repo->submodule_cache = 0;
	return 0;
}