repository.c 42.1 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
#include <stdarg.h>
8
#include <ctype.h>
9

10
#include "git2/object.h"
11
#include "git2/refdb.h"
12
#include "git2/sys/repository.h"
13

14 15 16 17
#include "common.h"
#include "repository.h"
#include "commit.h"
#include "tag.h"
18
#include "blob.h"
19
#include "fileops.h"
20 21
#include "filebuf.h"
#include "index.h"
22
#include "config.h"
23
#include "refs.h"
24 25
#include "filter.h"
#include "odb.h"
26
#include "remote.h"
Edward Thomson committed
27
#include "merge.h"
28
#include "diff_driver.h"
29

30
#define GIT_FILE_CONTENT_PREFIX "gitdir:"
31

32 33
#define GIT_BRANCH_MASTER "master"

34
#define GIT_REPO_VERSION 0
35

36
static void set_odb(git_repository *repo, git_odb *odb)
37
{
38 39 40 41 42 43 44 45
	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);
46
	}
47
}
48

49
static void set_refdb(git_repository *repo, git_refdb *refdb)
50
{
51 52 53 54 55 56 57 58
	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);
59 60 61
	}
}

62
static void set_config(git_repository *repo, git_config *config)
63
{
64 65 66 67 68 69 70 71
	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);
72
	}
73 74

	git_repository__cvar_cache_clear(repo);
75 76
}

77
static void set_index(git_repository *repo, git_index *index)
78
{
79 80 81 82 83 84 85 86
	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);
87
	}
88 89
}

90
void git_repository__cleanup(git_repository *repo)
91
{
92
	assert(repo);
93

94
	git_cache_clear(&repo->objects);
95
	git_attr_cache_flush(repo);
96

97 98 99 100
	set_config(repo, NULL);
	set_index(repo, NULL);
	set_odb(repo, NULL);
	set_refdb(repo, NULL);
101 102 103 104 105 106 107 108 109 110 111
}

void git_repository_free(git_repository *repo)
{
	if (repo == NULL)
		return;

	git_repository__cleanup(repo);

	git_cache_free(&repo->objects);
	git_submodule_config_free(repo);
112

113
	git_diff_driver_registry_free(repo->diff_drivers);
114
	repo->diff_drivers = NULL;
115

Russell Belfer committed
116 117
	git__free(repo->path_repository);
	git__free(repo->workdir);
Vicent Marti committed
118
	git__free(repo->namespace);
Russell Belfer committed
119

120
	git__memzero(repo, sizeof(*repo));
121
	git__free(repo);
122 123
}

124 125 126 127 128
/*
 * Git repository open methods
 *
 * Open a repository object from its path
 */
129
static bool valid_repository_path(git_buf *repository_path)
130
{
131
	/* Check OBJECTS_DIR first, since it will generate the longest path name */
132
	if (git_path_contains_dir(repository_path, GIT_OBJECTS_DIR) == false)
133
		return false;
134

135
	/* Ensure HEAD file exists */
136
	if (git_path_contains_file(repository_path, GIT_HEAD_FILE) == false)
137
		return false;
138

139
	if (git_path_contains_dir(repository_path, GIT_REFS_DIR)  == false)
140
		return false;
141

142
	return true;
143 144
}

145
static git_repository *repository_alloc(void)
146
{
147
	git_repository *repo = git__calloc(1, sizeof(git_repository));
148 149 150
	if (!repo)
		return NULL;

151
	if (git_cache_init(&repo->objects) < 0) {
152
		git__free(repo);
153 154
		return NULL;
	}
155

156 157 158
	/* set all the entries in the cvar cache to `unset` */
	git_repository__cvar_cache_clear(repo);

159 160 161
	return repo;
}

162 163 164 165 166 167
int git_repository_new(git_repository **out)
{
	*out = repository_alloc();
	return 0;
}

168
static int load_config_data(git_repository *repo)
169
{
170
	int is_bare;
171
	git_config *config;
172

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

176
	/* Try to figure out if it's bare, default to non-bare if it's not set */
177
	if (git_config_get_bool(&is_bare, config, "core.bare") < 0)
178 179 180
		repo->is_bare = 0;
	else
		repo->is_bare = is_bare;
181

182
	return 0;
183
}
184

185
static int load_workdir(git_repository *repo, git_buf *parent_path)
186
{
187 188 189 190
	int         error;
	git_config *config;
	const char *worktree;
	git_buf     worktree_buf = GIT_BUF_INIT;
191

192
	if (repo->is_bare)
193
		return 0;
194

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

198
	error = git_config_get_string(&worktree, config, "core.worktree");
199 200 201 202 203 204 205
	if (!error && worktree != NULL) {
		error = git_path_prettify_dir(
			&worktree_buf, worktree, repo->path_repository);
		if (error < 0)
			return error;
		repo->workdir = git_buf_detach(&worktree_buf);
	}
206
	else if (error != GIT_ENOTFOUND)
207 208 209 210 211 212 213 214 215 216 217 218 219 220
		return error;
	else {
		giterr_clear();

		if (parent_path && git_path_isdir(parent_path->ptr))
			repo->workdir = git_buf_detach(parent_path);
		else {
			git_path_dirname_r(&worktree_buf, repo->path_repository);
			git_path_to_dir(&worktree_buf);
			repo->workdir = git_buf_detach(&worktree_buf);
		}
	}

	GITERR_CHECK_ALLOC(repo->workdir);
221

222
	return 0;
223 224
}

225 226 227 228 229 230 231 232 233 234 235 236 237 238 239
/*
 * 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.
 */
static int find_ceiling_dir_offset(
	const char *path,
	const char *ceiling_directories)
{
	char buf[GIT_PATH_MAX + 1];
	char buf2[GIT_PATH_MAX + 1];
	const char *ceil, *sep;
240
	size_t len, max_len = 0, min_len;
241 242 243

	assert(path);

244
	min_len = (size_t)(git_path_root(path) + 1);
245 246

	if (ceiling_directories == NULL || min_len == 0)
247
		return (int)min_len;
248 249 250 251 252

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

253
		if (len == 0 || len >= sizeof(buf) || git_path_root(ceil) == -1)
254 255 256 257 258 259 260 261 262 263 264 265 266
			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) &&
267
			(path[len] == '/' || !path[len]) &&
268 269 270 271 272 273
			len > max_len)
		{
			max_len = len;
		}
	}

274
	return (int)(max_len <= min_len ? min_len : max_len);
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
}

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

295 296
	if (git_buf_len(&file) <= prefix_len ||
		memcmp(git_buf_cstr(&file), GIT_FILE_CONTENT_PREFIX, prefix_len) != 0)
297 298 299 300 301
	{
		giterr_set(GITERR_REPOSITORY, "The `.git` file at '%s' is malformed", file_path);
		error = -1;
	}
	else if ((error = git_path_dirname_r(path_out, file_path)) >= 0) {
302
		const char *gitlink = git_buf_cstr(&file) + prefix_len;
303
		while (*gitlink && git__isspace(*gitlink)) gitlink++;
304 305
		error = git_path_prettify_dir(
			path_out, gitlink, git_buf_cstr(path_out));
306 307 308 309 310 311 312 313 314 315 316 317
	}

	git_buf_free(&file);
	return error;
}

static int find_repo(
	git_buf *repo_path,
	git_buf *parent_path,
	const char *start_path,
	uint32_t flags,
	const char *ceiling_dirs)
318
{
319 320 321 322
	int error;
	git_buf path = GIT_BUF_INIT;
	struct stat st;
	dev_t initial_device = 0;
323
	bool try_with_dot_git = ((flags & GIT_REPOSITORY_OPEN_BARE) != 0);
324 325 326 327
	int ceiling_offset;

	git_buf_free(repo_path);

328
	if ((error = git_path_prettify(&path, start_path, NULL)) < 0)
329 330 331 332
		return error;

	ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);

333 334
	if (!try_with_dot_git &&
		(error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0)
335 336
		return error;

nulltoken committed
337
	while (!error && !git_buf_len(repo_path)) {
338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358
		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 &&
				(flags & GIT_REPOSITORY_OPEN_CROSS_FS) == 0)
				break;

			if (S_ISDIR(st.st_mode)) {
				if (valid_repository_path(&path)) {
					git_path_to_dir(&path);
					git_buf_set(repo_path, path.ptr, path.size);
					break;
				}
			}
			else if (S_ISREG(st.st_mode)) {
				git_buf repo_link = GIT_BUF_INIT;

				if (!(error = read_gitfile(&repo_link, path.ptr))) {
					if (valid_repository_path(&repo_link))
						git_buf_swap(repo_path, &repo_link);
359 360

					git_buf_free(&repo_link);
361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
					break;
				}
				git_buf_free(&repo_link);
			}
		}

		/* move up one directory level */
		if (git_path_dirname_r(&path, path.ptr) < 0) {
			error = -1;
			break;
		}

		if (try_with_dot_git) {
			/* if we tried original dir with and without .git AND either hit
			 * directory ceiling or NO_SEARCH was requested, then be done.
			 */
			if (path.ptr[ceiling_offset] == '\0' ||
				(flags & GIT_REPOSITORY_OPEN_NO_SEARCH) != 0)
				break;
			/* otherwise look first for .git item */
			error = git_buf_joinpath(&path, path.ptr, DOT_GIT);
		}
		try_with_dot_git = !try_with_dot_git;
	}

386
	if (!error && parent_path && !(flags & GIT_REPOSITORY_OPEN_BARE)) {
nulltoken committed
387
		if (!git_buf_len(repo_path))
388 389 390 391 392 393 394 395 396 397 398
			git_buf_clear(parent_path);
		else {
			git_path_dirname_r(parent_path, path.ptr);
			git_path_to_dir(parent_path);
		}
		if (git_buf_oom(parent_path))
			return -1;
	}

	git_buf_free(&path);

nulltoken committed
399
	if (!git_buf_len(repo_path) && !error) {
400
		giterr_set(GITERR_REPOSITORY,
401
			"Could not find repository from '%s'", start_path);
402
		error = GIT_ENOTFOUND;
403
	}
404

405 406 407
	return error;
}

408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
int git_repository_open_bare(
	git_repository **repo_ptr,
	const char *bare_path)
{
	int error;
	git_buf path = GIT_BUF_INIT;
	git_repository *repo = NULL;

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

	if (!valid_repository_path(&path)) {
		git_buf_free(&path);
		giterr_set(GITERR_REPOSITORY, "Path is not a repository: %s", bare_path);
		return GIT_ENOTFOUND;
	}

	repo = repository_alloc();
	GITERR_CHECK_ALLOC(repo);

	repo->path_repository = git_buf_detach(&path);
	GITERR_CHECK_ALLOC(repo->path_repository);

	/* of course we're bare! */
	repo->is_bare = 1;
	repo->workdir = NULL;

	*repo_ptr = repo;
	return 0;
}

439 440 441
int git_repository_open_ext(
	git_repository **repo_ptr,
	const char *start_path,
442
	unsigned int flags,
443 444 445 446 447 448
	const char *ceiling_dirs)
{
	int error;
	git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT;
	git_repository *repo;

449 450
	if (repo_ptr)
		*repo_ptr = NULL;
451

452 453
	error = find_repo(&path, &parent, start_path, flags, ceiling_dirs);
	if (error < 0 || !repo_ptr)
454 455
		return error;

456
	repo = repository_alloc();
457
	GITERR_CHECK_ALLOC(repo);
458

459
	repo->path_repository = git_buf_detach(&path);
460
	GITERR_CHECK_ALLOC(repo->path_repository);
461

462 463 464
	if ((flags & GIT_REPOSITORY_OPEN_BARE) != 0)
		repo->is_bare = 1;
	else if ((error = load_config_data(repo)) < 0 ||
465 466 467 468 469
		(error = load_workdir(repo, &parent)) < 0)
	{
		git_repository_free(repo);
		return error;
	}
470

471
	git_buf_free(&parent);
472
	*repo_ptr = repo;
473
	return 0;
474
}
475

476 477 478 479 480 481
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);
}

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

495 496 497 498 499 500 501 502 503
int git_repository_discover(
	char *repository_path,
	size_t size,
	const char *start_path,
	int across_fs,
	const char *ceiling_dirs)
{
	git_buf path = GIT_BUF_INIT;
	uint32_t flags = across_fs ? GIT_REPOSITORY_OPEN_CROSS_FS : 0;
504
	int error;
505 506 507 508 509

	assert(start_path && repository_path && size > 0);

	*repository_path = '\0';

510
	if ((error = find_repo(&path, NULL, start_path, flags, ceiling_dirs)) < 0)
511
		return error != GIT_ENOTFOUND ? -1 : error;
512 513 514

	if (size < (size_t)(path.size + 1)) {
		giterr_set(GITERR_REPOSITORY,
515
			"The given buffer is too small to store the discovered path");
516 517 518 519 520 521 522 523
		git_buf_free(&path);
		return -1;
	}

	/* success: we discovered a repository */
	git_buf_copy_cstr(repository_path, size, &path);
	git_buf_free(&path);
	return 0;
524 525
}

526
static int load_config(
527 528 529
	git_config **out,
	git_repository *repo,
	const char *global_config_path,
Sven Strickroth committed
530
	const char *xdg_config_path,
531
	const char *system_config_path)
532
{
533
	int error;
534
	git_buf config_path = GIT_BUF_INIT;
535
	git_config *cfg = NULL;
536

537
	assert(repo && out);
538

539 540
	if ((error = git_config_new(&cfg)) < 0)
		return error;
541

542 543 544
	error = git_buf_joinpath(
		&config_path, repo->path_repository, GIT_CONFIG_FILENAME_INREPO);
	if (error < 0)
545
		goto on_error;
546

547 548 549
	if ((error = git_config_add_file_ondisk(
			cfg, config_path.ptr, GIT_CONFIG_LEVEL_LOCAL, 0)) < 0 &&
		error != GIT_ENOTFOUND)
550 551 552
		goto on_error;

	git_buf_free(&config_path);
553

554 555 556 557 558
	if (global_config_path != NULL &&
		(error = git_config_add_file_ondisk(
			cfg, global_config_path, GIT_CONFIG_LEVEL_GLOBAL, 0)) < 0 &&
		error != GIT_ENOTFOUND)
		goto on_error;
559

560 561 562 563 564
	if (xdg_config_path != NULL &&
		(error = git_config_add_file_ondisk(
			cfg, xdg_config_path, GIT_CONFIG_LEVEL_XDG, 0)) < 0 &&
		error != GIT_ENOTFOUND)
		goto on_error;
565

566 567 568 569 570
	if (system_config_path != NULL &&
		(error = git_config_add_file_ondisk(
			cfg, system_config_path, GIT_CONFIG_LEVEL_SYSTEM, 0)) < 0 &&
		error != GIT_ENOTFOUND)
		goto on_error;
571

572 573
	giterr_clear(); /* clear any lingering ENOTFOUND errors */

574
	*out = cfg;
575
	return 0;
576

577 578
on_error:
	git_buf_free(&config_path);
579 580
	git_config_free(cfg);
	*out = NULL;
581
	return error;
582 583
}

Russell Belfer committed
584
static const char *path_unless_empty(git_buf *buf)
585
{
Russell Belfer committed
586 587
	return git_buf_len(buf) > 0 ? git_buf_cstr(buf) : NULL;
}
588

Russell Belfer committed
589 590 591
int git_repository_config__weakptr(git_config **out, git_repository *repo)
{
	int error = 0;
592

Russell Belfer committed
593 594 595 596 597 598 599 600 601 602
	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;
		git_config *config;

		git_config_find_global_r(&global_buf);
		git_config_find_xdg_r(&xdg_buf);
		git_config_find_system_r(&system_buf);

603 604 605 606
		/* 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
607 608 609 610 611 612 613 614
		error = load_config(
			&config, repo,
			path_unless_empty(&global_buf),
			path_unless_empty(&xdg_buf),
			path_unless_empty(&system_buf));
		if (!error) {
			GIT_REFCOUNT_OWN(config, repo);

615
			config = git__compare_and_swap(&repo->_config, NULL, config);
Russell Belfer committed
616 617 618 619 620
			if (config != NULL) {
				GIT_REFCOUNT_OWN(config, NULL);
				git_config_free(config);
			}
		}
621 622

		git_buf_free(&global_buf);
623
		git_buf_free(&xdg_buf);
624
		git_buf_free(&system_buf);
625
	}
626

627
	*out = repo->_config;
Russell Belfer committed
628
	return error;
629 630
}

631
int git_repository_config(git_config **out, git_repository *repo)
632
{
633 634
	if (git_repository_config__weakptr(out, repo) < 0)
		return -1;
635

636 637
	GIT_REFCOUNT_INC(*out);
	return 0;
638 639 640 641 642
}

void git_repository_set_config(git_repository *repo, git_config *config)
{
	assert(repo && config);
643
	set_config(repo, config);
644 645 646 647
}

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

650 651 652
	assert(repo && out);

	if (repo->_odb == NULL) {
653
		git_buf odb_path = GIT_BUF_INIT;
Russell Belfer committed
654
		git_odb *odb;
655

Russell Belfer committed
656
		git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR);
657

Russell Belfer committed
658 659 660
		error = git_odb_open(&odb, odb_path.ptr);
		if (!error) {
			GIT_REFCOUNT_OWN(odb, repo);
661

662
			odb = git__compare_and_swap(&repo->_odb, NULL, odb);
Russell Belfer committed
663 664 665 666 667
			if (odb != NULL) {
				GIT_REFCOUNT_OWN(odb, NULL);
				git_odb_free(odb);
			}
		}
668

Russell Belfer committed
669
		git_buf_free(&odb_path);
670
	}
671

672
	*out = repo->_odb;
Russell Belfer committed
673
	return error;
674 675
}

676
int git_repository_odb(git_odb **out, git_repository *repo)
677
{
678 679
	if (git_repository_odb__weakptr(out, repo) < 0)
		return -1;
Vicent Marti committed
680

681 682
	GIT_REFCOUNT_INC(*out);
	return 0;
683
}
684

685 686 687
void git_repository_set_odb(git_repository *repo, git_odb *odb)
{
	assert(repo && odb);
688
	set_odb(repo, odb);
689 690
}

691 692
int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo)
{
Russell Belfer committed
693 694
	int error = 0;

695 696 697
	assert(out && repo);

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

Russell Belfer committed
700 701 702
		error = git_refdb_open(&refdb, repo);
		if (!error) {
			GIT_REFCOUNT_OWN(refdb, repo);
703

704
			refdb = git__compare_and_swap(&repo->_refdb, NULL, refdb);
Russell Belfer committed
705 706 707 708 709
			if (refdb != NULL) {
				GIT_REFCOUNT_OWN(refdb, NULL);
				git_refdb_free(refdb);
			}
		}
710 711 712
	}

	*out = repo->_refdb;
Russell Belfer committed
713
	return error;
714 715 716 717 718 719 720 721 722 723 724 725 726
}

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
727
	assert(repo && refdb);
728
	set_refdb(repo, refdb);
729 730
}

731 732
int git_repository_index__weakptr(git_index **out, git_repository *repo)
{
Russell Belfer committed
733 734
	int error = 0;

735 736 737
	assert(out && repo);

	if (repo->_index == NULL) {
738
		git_buf index_path = GIT_BUF_INIT;
Russell Belfer committed
739
		git_index *index;
740

Russell Belfer committed
741
		git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE);
742

Russell Belfer committed
743 744 745
		error = git_index_open(&index, index_path.ptr);
		if (!error) {
			GIT_REFCOUNT_OWN(index, repo);
746

747
			index = git__compare_and_swap(&repo->_index, NULL, index);
Russell Belfer committed
748 749 750 751
			if (index != NULL) {
				GIT_REFCOUNT_OWN(index, NULL);
				git_index_free(index);
			}
752

Russell Belfer committed
753 754
			error = git_index_set_caps(repo->_index, GIT_INDEXCAP_FROM_OWNER);
		}
755

Russell Belfer committed
756
		git_buf_free(&index_path);
757 758 759
	}

	*out = repo->_index;
Russell Belfer committed
760
	return error;
761
}
Vicent Marti committed
762

763 764
int git_repository_index(git_index **out, git_repository *repo)
{
765 766
	if (git_repository_index__weakptr(out, repo) < 0)
		return -1;
767

768 769
	GIT_REFCOUNT_INC(*out);
	return 0;
770 771
}

772 773 774
void git_repository_set_index(git_repository *repo, git_index *index)
{
	assert(repo && index);
775
	set_index(repo, index);
776 777
}

Vicent Marti committed
778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794
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;
}

795
static int check_repositoryformatversion(git_config *config)
796
{
797
	int version;
798

799
	if (git_config_get_int32(&version, config, "core.repositoryformatversion") < 0)
800
		return -1;
801

802
	if (GIT_REPO_VERSION < version) {
803 804
		giterr_set(GITERR_REPOSITORY,
			"Unsupported repository version %d. Only versions up to %d are supported.",
805
			version, GIT_REPO_VERSION);
806 807
		return -1;
	}
808

809
	return 0;
810 811
}

812
static int repo_init_create_head(const char *git_dir, const char *ref_name)
813
{
814
	git_buf ref_path = GIT_BUF_INIT;
815
	git_filebuf ref = GIT_FILEBUF_INIT;
816
	const char *fmt;
817

818
	if (git_buf_joinpath(&ref_path, git_dir, GIT_HEAD_FILE) < 0 ||
819 820 821 822 823 824
		git_filebuf_open(&ref, ref_path.ptr, 0) < 0)
		goto fail;

	if (!ref_name)
		ref_name = GIT_BRANCH_MASTER;

825
	if (git__prefixcmp(ref_name, GIT_REFS_DIR) == 0)
826 827
		fmt = "ref: %s\n";
	else
828
		fmt = "ref: " GIT_REFS_HEADS_DIR "%s\n";
829 830

	if (git_filebuf_printf(&ref, fmt, ref_name) < 0 ||
831
		git_filebuf_commit(&ref, GIT_REFS_FILE_MODE) < 0)
832
		goto fail;
833

834
	git_buf_free(&ref_path);
835
	return 0;
836 837 838 839 840

fail:
	git_buf_free(&ref_path);
	git_filebuf_cleanup(&ref);
	return -1;
841 842
}

843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860
static bool is_chmod_supported(const char *file_path)
{
	struct stat st1, st2;
	static int _is_supported = -1;

	if (_is_supported > -1)
		return _is_supported;

	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;

	_is_supported = (st1.st_mode != st2.st_mode);
861

862 863 864
	return _is_supported;
}

865 866 867 868 869 870 871 872 873 874 875 876 877 878 879 880 881 882
static bool is_filesystem_case_insensitive(const char *gitdir_path)
{
	git_buf path = GIT_BUF_INIT;
	static int _is_insensitive = -1;

	if (_is_insensitive > -1)
		return _is_insensitive;

	if (git_buf_joinpath(&path, gitdir_path, "CoNfIg") < 0)
		goto cleanup;

	_is_insensitive = git_path_exists(git_buf_cstr(&path));

cleanup:
	git_buf_free(&path);
	return _is_insensitive;
}

883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907
static bool are_symlinks_supported(const char *wd_path)
{
	git_buf path = GIT_BUF_INIT;
	int fd;
	struct stat st;
	static int _symlinks_supported = -1;

	if (_symlinks_supported > -1)
		return _symlinks_supported;

	if ((fd = git_futils_mktmp(&path, wd_path)) < 0 ||
		p_close(fd) < 0 ||
		p_unlink(path.ptr) < 0 ||
		p_symlink("testing", path.ptr) < 0 ||
		p_lstat(path.ptr, &st) < 0)
		_symlinks_supported = false;
	else
		_symlinks_supported = (S_ISLNK(st.st_mode) != 0);

	(void)p_unlink(path.ptr);
	git_buf_free(&path);

	return _symlinks_supported;
}

908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924
static int create_empty_file(const char *path, mode_t mode)
{
	int fd;

	if ((fd = p_creat(path, mode)) < 0) {
		giterr_set(GITERR_OS, "Error while creating '%s'", path);
		return -1;
	}

	if (p_close(fd) < 0) {
		giterr_set(GITERR_OS, "Error while closing '%s'", path);
		return -1;
	}

	return 0;
}

925
static int repo_init_config(
926 927 928
	const char *repo_dir,
	const char *work_dir,
	git_repository_init_options *opts)
929
{
930
	int error = 0;
931 932
	git_buf cfg_path = GIT_BUF_INIT;
	git_config *config = NULL;
933

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

938
	if (git_buf_joinpath(&cfg_path, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
939
		return -1;
940

941 942 943 944 945 946
	if (!git_path_isfile(git_buf_cstr(&cfg_path)) &&
		create_empty_file(git_buf_cstr(&cfg_path), GIT_CONFIG_FILE_MODE) < 0) {
			git_buf_free(&cfg_path);
			return -1;
	}

947
	if (git_config_open_ondisk(&config, git_buf_cstr(&cfg_path)) < 0) {
948 949 950
		git_buf_free(&cfg_path);
		return -1;
	}
951

952
	if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0 &&
953 954
		(error = check_repositoryformatversion(config)) < 0)
		goto cleanup;
955

956 957 958 959 960 961 962
	SET_REPO_CONFIG(
		bool, "core.bare", (opts->flags & GIT_REPOSITORY_INIT_BARE) != 0);
	SET_REPO_CONFIG(
		int32, "core.repositoryformatversion", GIT_REPO_VERSION);
	SET_REPO_CONFIG(
		bool, "core.filemode", is_chmod_supported(git_buf_cstr(&cfg_path)));

963
	if (!(opts->flags & GIT_REPOSITORY_INIT_BARE)) {
964 965
		SET_REPO_CONFIG(bool, "core.logallrefupdates", true);

966 967 968 969 970 971 972
		if (!are_symlinks_supported(work_dir))
			SET_REPO_CONFIG(bool, "core.symlinks", false);

		if (!(opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD)) {
			SET_REPO_CONFIG(string, "core.worktree", work_dir);
		}
		else if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0) {
Ben Straub committed
973
			if (git_config_delete_entry(config, "core.worktree") < 0)
974
				giterr_clear();
975 976 977 978 979 980
		}
	} else {
		if (!are_symlinks_supported(repo_dir))
			SET_REPO_CONFIG(bool, "core.symlinks", false);
	}

981
	if (!(opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) &&
982
		is_filesystem_case_insensitive(repo_dir))
983
		SET_REPO_CONFIG(bool, "core.ignorecase", true);
984

985
	if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP) {
986 987
		SET_REPO_CONFIG(int32, "core.sharedrepository", 1);
		SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
988 989
	}
	else if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL) {
990 991 992
		SET_REPO_CONFIG(int32, "core.sharedrepository", 2);
		SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
	}
993

994
cleanup:
995
	git_buf_free(&cfg_path);
996
	git_config_free(config);
997

998
	return error;
999
}
1000

1001
static int repo_write_template(
1002 1003 1004 1005
	const char *git_dir,
	bool allow_overwrite,
	const char *file,
	mode_t mode,
1006
	bool hidden,
1007
	const char *content)
1008 1009
{
	git_buf path = GIT_BUF_INIT;
1010
	int fd, error = 0, flags;
1011 1012 1013 1014

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

1015 1016 1017 1018 1019 1020
	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);
1021

1022 1023
	if (fd >= 0) {
		error = p_write(fd, content, strlen(content));
1024

1025 1026 1027 1028
		p_close(fd);
	}
	else if (errno != EEXIST)
		error = fd;
1029

1030 1031 1032 1033 1034 1035 1036 1037 1038
#ifdef GIT_WIN32
	if (!error && hidden) {
		if (p_hide_directory__w32(path.ptr) < 0)
			error = -1;
	}
#else
	GIT_UNUSED(hidden);
#endif

1039
	git_buf_free(&path);
1040 1041 1042 1043 1044 1045

	if (error)
		giterr_set(GITERR_OS,
			"Failed to initialize repository with template '%s'", file);

	return error;
1046 1047
}

1048 1049
static int repo_write_gitlink(
	const char *in_dir, const char *to_repo)
1050
{
1051 1052 1053 1054 1055 1056 1057
	int error;
	git_buf buf = GIT_BUF_INIT;
	struct stat st;

	git_path_dirname_r(&buf, to_repo);
	git_path_to_dir(&buf);
	if (git_buf_oom(&buf))
1058
		return -1;
1059

1060 1061 1062 1063 1064 1065 1066 1067 1068 1069 1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081 1082
	/* 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,
			"Cannot overwrite gitlink file into path '%s'", in_dir);
		error = GIT_EEXISTS;
		goto cleanup;
	}

	git_buf_clear(&buf);

	error = git_buf_printf(&buf, "%s %s", GIT_FILE_CONTENT_PREFIX, to_repo);

	if (!error)
1083
		error = repo_write_template(in_dir, true, DOT_GIT, 0666, true, buf.ptr);
1084 1085 1086 1087 1088 1089

cleanup:
	git_buf_free(&buf);
	return error;
}

1090 1091 1092
static mode_t pick_dir_mode(git_repository_init_options *opts)
{
	if (opts->mode == GIT_REPOSITORY_INIT_SHARED_UMASK)
1093
		return 0777;
1094 1095 1096 1097 1098 1099 1100
	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;
}

1101 1102 1103 1104 1105 1106 1107
#include "repo_template.h"

static int repo_init_structure(
	const char *repo_dir,
	const char *work_dir,
	git_repository_init_options *opts)
{
1108
	int error = 0;
1109
	repo_template_item *tpl;
1110 1111 1112
	bool external_tpl =
		((opts->flags & GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE) != 0);
	mode_t dmode = pick_dir_mode(opts);
1113 1114

	/* Hide the ".git" directory */
1115
#ifdef GIT_WIN32
1116
	if ((opts->flags & GIT_REPOSITORY_INIT__HAS_DOTGIT) != 0) {
1117
		if (p_hide_directory__w32(repo_dir) < 0) {
1118 1119 1120 1121
			giterr_set(GITERR_REPOSITORY,
				"Failed to mark Git repository folder as hidden");
			return -1;
		}
1122
	}
1123 1124 1125 1126 1127 1128
#endif

	/* Create the .git gitlink if appropriate */
	if ((opts->flags & GIT_REPOSITORY_INIT_BARE) == 0 &&
		(opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD) == 0)
	{
1129
		if (repo_write_gitlink(work_dir, repo_dir) < 0)
1130
			return -1;
1131
	}
1132

1133 1134
	/* Copy external template if requested */
	if (external_tpl) {
Linquize committed
1135 1136 1137
		git_config *cfg = NULL;
		const char *tdir = NULL;
		bool default_template = false;
1138 1139
		git_buf template_buf = GIT_BUF_INIT;

1140 1141
		if (opts->template_path)
			tdir = opts->template_path;
Linquize committed
1142
		else if ((error = git_config_open_default(&cfg)) >= 0) {
1143 1144
			error = git_config_get_string(&tdir, cfg, "init.templatedir");
			giterr_clear();
Linquize committed
1145 1146 1147
		}

		if (!tdir) {
1148
			if (!(error = git_futils_find_template_dir(&template_buf)))
1149
				tdir = template_buf.ptr;
Linquize committed
1150
			default_template = true;
1151
		}
1152

1153 1154 1155 1156
		if (tdir)
			error = git_futils_cp_r(tdir, repo_dir,
				GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD_DIRS |
				GIT_CPDIR_SIMPLE_TO_MODE, dmode);
1157

Linquize committed
1158 1159
		git_buf_free(&template_buf);
		git_config_free(cfg);
1160

1161
		if (error < 0) {
Linquize committed
1162
			if (!default_template)
1163 1164 1165 1166 1167
				return error;

			/* if template was default, ignore error and use internal */
			giterr_clear();
			external_tpl = false;
1168
			error = 0;
1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180
		}
	}

	/* 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) {
		if (!tpl->content)
			error = git_futils_mkdir(
				tpl->path, repo_dir, dmode, GIT_MKDIR_PATH | GIT_MKDIR_CHMOD);
		else if (!external_tpl) {
1181 1182 1183 1184 1185
			const char *content = tpl->content;

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

1186 1187
			error = repo_write_template(
				repo_dir, false, tpl->path, tpl->mode, false, content);
1188
		}
1189 1190
	}

1191
	return error;
1192 1193
}

1194 1195
static int mkdir_parent(git_buf *buf, uint32_t mode, bool skip2)
{
1196 1197 1198
	/* When making parent directories during repository initialization
	 * don't try to set gid or grant world write access
	 */
1199
	return git_futils_mkdir(
1200
		buf->ptr, NULL, mode & ~(S_ISGID | 0002),
1201 1202 1203 1204
		GIT_MKDIR_PATH | GIT_MKDIR_VERIFY_DIR |
		(skip2 ? GIT_MKDIR_SKIP_LAST2 : GIT_MKDIR_SKIP_LAST));
}

1205 1206 1207 1208 1209
static int repo_init_directories(
	git_buf *repo_path,
	git_buf *wd_path,
	const char *given_repo,
	git_repository_init_options *opts)
1210
{
1211
	int error = 0;
1212
	bool is_bare, add_dotgit, has_dotgit, natural_wd;
1213
	mode_t dirmode;
1214

1215 1216 1217 1218 1219 1220 1221 1222 1223 1224 1225 1226 1227 1228 1229 1230 1231 1232 1233 1234
	/* 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
	 */

1235
	/* set up repo path */
1236

1237 1238
	is_bare = ((opts->flags & GIT_REPOSITORY_INIT_BARE) != 0);

1239 1240
	add_dotgit =
		(opts->flags & GIT_REPOSITORY_INIT_NO_DOTGIT_DIR) == 0 &&
1241
		!is_bare &&
1242 1243
		git__suffixcmp(given_repo, "/" DOT_GIT) != 0 &&
		git__suffixcmp(given_repo, "/" GIT_DIR) != 0;
1244

1245 1246
	if (git_buf_joinpath(repo_path, given_repo, add_dotgit ? GIT_DIR : "") < 0)
		return -1;
1247

1248 1249 1250 1251 1252 1253
	has_dotgit = (git__suffixcmp(repo_path->ptr, "/" GIT_DIR) == 0);
	if (has_dotgit)
		opts->flags |= GIT_REPOSITORY_INIT__HAS_DOTGIT;

	/* set up workdir path */

1254
	if (!is_bare) {
1255
		if (opts->workdir_path) {
1256 1257 1258
			if (git_path_join_unrooted(
					wd_path, opts->workdir_path, repo_path->ptr, NULL) < 0)
				return -1;
1259 1260 1261 1262 1263 1264 1265 1266
		} else if (has_dotgit) {
			if (git_path_dirname_r(wd_path, repo_path->ptr) < 0)
				return -1;
		} else {
			giterr_set(GITERR_REPOSITORY, "Cannot pick working directory"
				" for non-bare repository that isn't a '.git' directory");
			return -1;
		}
1267

1268 1269 1270 1271 1272 1273 1274 1275 1276 1277 1278 1279 1280 1281 1282 1283
		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 */

1284
	dirmode = pick_dir_mode(opts);
1285

1286 1287 1288 1289 1290 1291 1292 1293 1294 1295 1296 1297 1298 1299 1300 1301 1302 1303 1304 1305 1306 1307 1308 1309 1310 1311 1312 1313
	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(
				wd_path->ptr, NULL, dirmode & ~S_ISGID,
				GIT_MKDIR_VERIFY_DIR)) < 0)
			return error;

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

1316 1317 1318 1319
	if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 ||
		(opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0 ||
		has_dotgit)
	{
1320
		/* create path #1 */
1321 1322
		error = git_futils_mkdir(repo_path->ptr, NULL, dirmode,
			GIT_MKDIR_VERIFY_DIR | ((dirmode & S_ISGID) ? GIT_MKDIR_CHMOD : 0));
1323
	}
1324

1325 1326 1327 1328 1329 1330 1331 1332 1333 1334 1335 1336 1337 1338 1339 1340 1341
	/* 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
1342
	if (!(error = git_remote_create(&remote, repo, GIT_REMOTE_ORIGIN, url))) {
1343 1344 1345 1346 1347 1348 1349 1350 1351
		git_remote_free(remote);
	}

	return error;
}

int git_repository_init(
	git_repository **repo_out, const char *path, unsigned is_bare)
{
1352
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
1353 1354 1355 1356 1357 1358 1359 1360 1361

	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(
1362
	git_repository **out,
1363 1364 1365 1366 1367 1368
	const char *given_repo,
	git_repository_init_options *opts)
{
	int error;
	git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT;

1369
	assert(out && given_repo && opts);
1370

Ben Straub committed
1371
	GITERR_CHECK_VERSION(opts, GIT_REPOSITORY_INIT_OPTIONS_VERSION, "git_repository_init_options");
1372

1373 1374
	error = repo_init_directories(&repo_path, &wd_path, given_repo, opts);
	if (error < 0)
1375
		goto cleanup;
1376 1377 1378 1379 1380 1381 1382 1383 1384 1385 1386 1387

	if (valid_repository_path(&repo_path)) {

		if ((opts->flags & GIT_REPOSITORY_INIT_NO_REINIT) != 0) {
			giterr_set(GITERR_REPOSITORY,
				"Attempt to reinitialize '%s'", given_repo);
			error = GIT_EEXISTS;
			goto cleanup;
		}

		opts->flags |= GIT_REPOSITORY_INIT__IS_REINIT;

1388 1389
		error = repo_init_config(
			git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts);
1390 1391 1392 1393 1394 1395

		/* TODO: reinitialize the templates */
	}
	else {
		if (!(error = repo_init_structure(
				git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)) &&
1396 1397
			!(error = repo_init_config(
				git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)))
1398 1399
			error = repo_init_create_head(
				git_buf_cstr(&repo_path), opts->initial_head);
1400
	}
1401 1402 1403
	if (error < 0)
		goto cleanup;

1404
	error = git_repository_open(out, git_buf_cstr(&repo_path));
1405

1406
	if (!error && opts->origin_url)
1407
		error = repo_init_create_origin(*out, opts->origin_url);
1408 1409

cleanup:
1410 1411 1412 1413
	git_buf_free(&repo_path);
	git_buf_free(&wd_path);

	return error;
1414
}
1415

1416
int git_repository_head_detached(git_repository *repo)
1417 1418
{
	git_reference *ref;
1419
	git_odb *odb = NULL;
1420
	int exists;
1421

1422 1423
	if (git_repository_odb__weakptr(&odb, repo) < 0)
		return -1;
1424

1425 1426
	if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0)
		return -1;
1427

1428 1429
	if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
		git_reference_free(ref);
1430
		return 0;
1431
	}
1432

1433
	exists = git_odb_exists(odb, git_reference_target(ref));
1434 1435

	git_reference_free(ref);
1436
	return exists;
1437 1438
}

1439
int git_repository_head(git_reference **head_out, git_repository *repo)
1440
{
1441
	git_reference *head;
1442 1443
	int error;

1444 1445 1446 1447 1448 1449 1450 1451
	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;
	}

1452
	error = git_reference_lookup_resolved(head_out, repo, git_reference_symbolic_target(head), -1);
1453
	git_reference_free(head);
1454

1455
	return error == GIT_ENOTFOUND ? GIT_EUNBORNBRANCH : error;
1456 1457
}

1458
int git_repository_head_unborn(git_repository *repo)
1459
{
1460
	git_reference *ref = NULL;
1461 1462 1463
	int error;

	error = git_repository_head(&ref, repo);
1464
	git_reference_free(ref);
1465

1466
	if (error == GIT_EUNBORNBRANCH)
1467
		return 1;
1468

1469 1470 1471 1472
	if (error < 0)
		return -1;

	return 0;
1473
}
1474

1475
static int at_least_one_cb(const char *refname, void *payload)
1476
{
1477 1478
	GIT_UNUSED(refname);
	GIT_UNUSED(payload);
1479

1480 1481
	return GIT_EUSER;
}
1482

1483 1484
static int repo_contains_no_reference(git_repository *repo)
{
1485
	int error = git_reference_foreach_name(repo, &at_least_one_cb, NULL);
1486

1487
	if (error == GIT_EUSER)
1488
		return 0;
1489

1490 1491
	if (!error)
		return 1;
1492

1493
	return error;
1494
}
1495

1496 1497 1498
int git_repository_is_empty(git_repository *repo)
{
	git_reference *head = NULL;
1499
	int is_empty = 0;
1500

1501
	if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0)
1502 1503
		return -1;

1504 1505 1506 1507 1508
	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);
1509 1510

	git_reference_free(head);
1511 1512

	return is_empty;
1513 1514
}

1515
const char *git_repository_path(git_repository *repo)
1516 1517
{
	assert(repo);
1518 1519
	return repo->path_repository;
}
1520

1521 1522 1523
const char *git_repository_workdir(git_repository *repo)
{
	assert(repo);
1524

1525 1526
	if (repo->is_bare)
		return NULL;
1527

1528 1529
	return repo->workdir;
}
1530

1531 1532
int git_repository_set_workdir(
	git_repository *repo, const char *workdir, int update_gitlink)
1533
{
1534
	int error = 0;
1535 1536
	git_buf path = GIT_BUF_INIT;

1537
	assert(repo && workdir);
1538

1539 1540
	if (git_path_prettify_dir(&path, workdir, NULL) < 0)
		return -1;
1541

1542 1543
	if (repo->workdir && strcmp(repo->workdir, path.ptr) == 0)
		return 0;
1544

1545 1546 1547 1548 1549 1550
	if (update_gitlink) {
		git_config *config;

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

1551
		error = repo_write_gitlink(path.ptr, git_repository_path(repo));
1552 1553 1554

		/* passthrough error means gitlink is unnecessary */
		if (error == GIT_PASSTHROUGH)
Ben Straub committed
1555
			error = git_config_delete_entry(config, "core.worktree");
1556 1557 1558 1559 1560 1561 1562 1563 1564 1565 1566 1567 1568 1569 1570 1571 1572
		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;
1573
}
1574 1575 1576 1577 1578 1579

int git_repository_is_bare(git_repository *repo)
{
	assert(repo);
	return repo->is_bare;
}
1580 1581 1582

int git_repository_head_tree(git_tree **tree, git_repository *repo)
{
1583 1584 1585
	git_reference *head;
	git_object *obj;
	int error;
1586

1587 1588
	if ((error = git_repository_head(&head, repo)) < 0)
		return error;
1589

1590 1591
	if ((error = git_reference_peel(&obj, head, GIT_OBJ_TREE)) < 0)
		goto cleanup;
1592 1593

	*tree = (git_tree *)obj;
1594 1595 1596 1597

cleanup:
	git_reference_free(head);
	return error;
1598
}
1599 1600 1601

int git_repository_message(char *buffer, size_t len, git_repository *repo)
{
Vicent Marti committed
1602 1603 1604
	git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
	struct stat st;
	int error;
1605

1606 1607 1608
	if (buffer != NULL)
		*buffer = '\0';

Edward Thomson committed
1609
	if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0)
Vicent Marti committed
1610
		return -1;
1611

1612
	if ((error = p_stat(git_buf_cstr(&path), &st)) < 0) {
1613 1614
		if (errno == ENOENT)
			error = GIT_ENOTFOUND;
1615
		giterr_set(GITERR_OS, "Could not access message file");
Vicent Marti committed
1616
	}
1617 1618 1619
	else if (buffer != NULL) {
		error = git_futils_readbuffer(&buf, git_buf_cstr(&path));
		git_buf_copy_cstr(buffer, len, &buf);
Vicent Marti committed
1620
	}
1621

Vicent Marti committed
1622 1623
	git_buf_free(&path);
	git_buf_free(&buf);
1624

1625 1626 1627 1628
	if (!error)
		error = (int)st.st_size + 1; /* add 1 for NUL byte */

	return error;
1629 1630 1631 1632
}

int git_repository_message_remove(git_repository *repo)
{
Vicent Marti committed
1633 1634
	git_buf path = GIT_BUF_INIT;
	int error;
1635

Edward Thomson committed
1636
	if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0)
Vicent Marti committed
1637
		return -1;
1638 1639 1640 1641 1642 1643

	error = p_unlink(git_buf_cstr(&path));
	git_buf_free(&path);

	return error;
}
1644 1645

int git_repository_hashfile(
Linquize committed
1646 1647 1648 1649 1650
	git_oid *out,
	git_repository *repo,
	const char *path,
	git_otype type,
	const char *as_path)
1651 1652
{
	int error;
1653
	git_filter_list *fl = NULL;
1654
	git_file fd = -1;
1655 1656 1657
	git_off_t len;
	git_buf full_path = GIT_BUF_INIT;

1658 1659 1660 1661 1662 1663
	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.
	 */
1664 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674

	error = git_path_join_unrooted(
		&full_path, path, repo ? git_repository_workdir(repo) : NULL, NULL);
	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
1675 1676
		error = git_filter_list_load(
			&fl, repo, NULL, as_path, GIT_FILTER_TO_ODB);
1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692
		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) {
1693
		error = (int)len;
1694 1695 1696 1697 1698 1699 1700 1701 1702
		goto cleanup;
	}

	if (!git__is_sizet(len)) {
		giterr_set(GITERR_OS, "File size overflow for 32-bit systems");
		error = -1;
		goto cleanup;
	}

1703
	error = git_odb__hashfd_filtered(out, fd, (size_t)len, type, fl);
1704 1705

cleanup:
1706 1707
	if (fd >= 0)
		p_close(fd);
1708
	git_filter_list_free(fl);
1709 1710 1711 1712 1713
	git_buf_free(&full_path);

	return error;
}

1714 1715 1716 1717 1718 1719 1720 1721 1722 1723 1724 1725 1726 1727 1728 1729 1730 1731 1732 1733 1734
static bool looks_like_a_branch(const char *refname)
{
	return git__prefixcmp(refname, GIT_REFS_HEADS_DIR) == 0;
}

int git_repository_set_head(
	git_repository* repo,
	const char* refname)
{
	git_reference *ref,
		*new_head = NULL;
	int error;

	assert(repo && refname);

	error = git_reference_lookup(&ref, repo, refname);
	if (error < 0 && error != GIT_ENOTFOUND)
		return error;

	if (!error) {
		if (git_reference_is_branch(ref))
1735
			error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, git_reference_name(ref), 1);
1736
		else
1737
			error = git_repository_set_head_detached(repo, git_reference_target(ref));
1738
	} else if (looks_like_a_branch(refname))
1739
		error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, refname, 1);
1740 1741 1742 1743 1744 1745

	git_reference_free(ref);
	git_reference_free(new_head);
	return error;
}

1746 1747 1748 1749 1750 1751 1752 1753 1754 1755 1756 1757 1758 1759 1760 1761 1762
int git_repository_set_head_detached(
	git_repository* repo,
	const git_oid* commitish)
{
	int error;
	git_object *object,
		*peeled = NULL;
	git_reference *new_head = NULL;

	assert(repo && commitish);

	if ((error = git_object_lookup(&object, repo, commitish, GIT_OBJ_ANY)) < 0)
		return error;

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

1763
	error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), 1);
1764 1765 1766 1767 1768 1769 1770 1771

cleanup:
	git_object_free(object);
	git_object_free(peeled);
	git_reference_free(new_head);
	return error;
}

1772 1773 1774 1775 1776 1777
int git_repository_detach_head(
	git_repository* repo)
{
	git_reference *old_head = NULL,
		*new_head = NULL;
	git_object *object = NULL;
1778
	int error;
1779 1780 1781

	assert(repo);

1782 1783
	if ((error = git_repository_head(&old_head, repo)) < 0)
		return error;
1784

1785
	if ((error = git_object_lookup(&object, repo, git_reference_target(old_head), GIT_OBJ_COMMIT)) < 0)
1786 1787
		goto cleanup;

1788
	error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_reference_target(old_head), 1);
1789 1790 1791 1792 1793 1794 1795

cleanup:
	git_object_free(object);
	git_reference_free(old_head);
	git_reference_free(new_head);
	return error;
}
Edward Thomson committed
1796

1797 1798 1799 1800
/**
 * Loosely ported from git.git
 * https://github.com/git/git/blob/master/contrib/completion/git-prompt.sh#L198-289
 */
Edward Thomson committed
1801 1802 1803 1804 1805 1806 1807 1808 1809 1810
int git_repository_state(git_repository *repo)
{
	git_buf repo_path = GIT_BUF_INIT;
	int state = GIT_REPOSITORY_STATE_NONE;

	assert(repo);

	if (git_buf_puts(&repo_path, repo->path_repository) < 0)
		return -1;

1811 1812 1813 1814 1815 1816 1817 1818 1819 1820 1821
	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
1822 1823 1824 1825 1826
		state = GIT_REPOSITORY_STATE_MERGE;
	else if(git_path_contains_file(&repo_path, GIT_REVERT_HEAD_FILE))
		state = GIT_REPOSITORY_STATE_REVERT;
	else if(git_path_contains_file(&repo_path, GIT_CHERRY_PICK_HEAD_FILE))
		state = GIT_REPOSITORY_STATE_CHERRY_PICK;
1827 1828
	else if(git_path_contains_file(&repo_path, GIT_BISECT_LOG_FILE))
		state = GIT_REPOSITORY_STATE_BISECT;
Edward Thomson committed
1829 1830 1831 1832

	git_buf_free(&repo_path);
	return state;
}
Ben Straub committed
1833 1834 1835 1836 1837

int git_repository_is_shallow(git_repository *repo)
{
	git_buf path = GIT_BUF_INIT;
	struct stat st;
Ben Straub committed
1838
	int error;
Ben Straub committed
1839 1840

	git_buf_joinpath(&path, repo->path_repository, "shallow");
Ben Straub committed
1841 1842
	error = git_path_lstat(path.ptr, &st);
	git_buf_free(&path);
Ben Straub committed
1843

Ben Straub committed
1844
	if (error == GIT_ENOTFOUND)
Ben Straub committed
1845
		return 0;
Ben Straub committed
1846 1847
	if (error < 0)
		return -1;
Ben Straub committed
1848 1849
	return st.st_size == 0 ? 0 : 1;
}