repository.c 39.5 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

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

26
#define GIT_FILE_CONTENT_PREFIX "gitdir:"
27

28 29
#define GIT_BRANCH_MASTER "master"

30
#define GIT_REPO_VERSION 0
31

32 33
#define GIT_TEMPLATE_DIR "/usr/share/git-core/templates"

34 35 36 37 38 39
static void drop_odb(git_repository *repo)
{
	if (repo->_odb != NULL) {
		GIT_REFCOUNT_OWN(repo->_odb, NULL);
		git_odb_free(repo->_odb);
		repo->_odb = NULL;
40
	}
41
}
42

43 44 45 46 47 48 49 50 51
static void drop_refdb(git_repository *repo)
{
	if (repo->_refdb != NULL) {
		GIT_REFCOUNT_OWN(repo->_refdb, NULL);
		git_refdb_free(repo->_refdb);
		repo->_refdb = NULL;
	}
}

52 53 54 55 56 57
static void drop_config(git_repository *repo)
{
	if (repo->_config != NULL) {
		GIT_REFCOUNT_OWN(repo->_config, NULL);
		git_config_free(repo->_config);
		repo->_config = NULL;
58
	}
59 60

	git_repository__cvar_cache_clear(repo);
61 62
}

63
static void drop_index(git_repository *repo)
64
{
65 66 67 68 69
	if (repo->_index != NULL) {
		GIT_REFCOUNT_OWN(repo->_index, NULL);
		git_index_free(repo->_index);
		repo->_index = NULL;
	}
70 71
}

72
void git_repository_free(git_repository *repo)
73
{
74 75
	if (repo == NULL)
		return;
76

77
	git_cache_free(&repo->objects);
78
	git_attr_cache_flush(repo);
79
	git_submodule_config_free(repo);
80

81 82
	git__free(repo->path_repository);
	git__free(repo->workdir);
83

84 85 86
	drop_config(repo);
	drop_index(repo);
	drop_odb(repo);
87
	drop_refdb(repo);
88 89

	git__free(repo);
90 91
}

92 93 94 95 96
/*
 * Git repository open methods
 *
 * Open a repository object from its path
 */
97
static bool valid_repository_path(git_buf *repository_path)
98
{
99
	/* Check OBJECTS_DIR first, since it will generate the longest path name */
100
	if (git_path_contains_dir(repository_path, GIT_OBJECTS_DIR) == false)
101
		return false;
102

103
	/* Ensure HEAD file exists */
104
	if (git_path_contains_file(repository_path, GIT_HEAD_FILE) == false)
105
		return false;
106

107
	if (git_path_contains_dir(repository_path, GIT_REFS_DIR)  == false)
108
		return false;
109

110
	return true;
111 112
}

113
static git_repository *repository_alloc(void)
114 115 116 117 118 119 120
{
	git_repository *repo = git__malloc(sizeof(git_repository));
	if (!repo)
		return NULL;

	memset(repo, 0x0, sizeof(git_repository));

121
	if (git_cache_init(&repo->objects, GIT_DEFAULT_CACHE_SIZE, &git_object__free) < 0) {
122
		git__free(repo);
123 124
		return NULL;
	}
125

126 127 128
	/* set all the entries in the cvar cache to `unset` */
	git_repository__cvar_cache_clear(repo);

129 130 131
	return repo;
}

132
static int load_config_data(git_repository *repo)
133
{
134
	int is_bare;
135
	git_config *config;
136

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

140
	/* Try to figure out if it's bare, default to non-bare if it's not set */
141
	if (git_config_get_bool(&is_bare, config, "core.bare") < 0)
142 143 144
		repo->is_bare = 0;
	else
		repo->is_bare = is_bare;
145

146
	return 0;
147
}
148

149
static int load_workdir(git_repository *repo, git_buf *parent_path)
150
{
151 152 153 154
	int         error;
	git_config *config;
	const char *worktree;
	git_buf     worktree_buf = GIT_BUF_INIT;
155

156
	if (repo->is_bare)
157
		return 0;
158

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

162
	error = git_config_get_string(&worktree, config, "core.worktree");
163 164 165 166 167 168 169
	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);
	}
170
	else if (error != GIT_ENOTFOUND)
171 172 173 174 175 176 177 178 179 180 181 182 183 184
		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);
185

186
	return 0;
187 188
}

189 190 191 192 193 194 195 196 197 198 199 200 201 202 203
/*
 * 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;
204
	size_t len, max_len = 0, min_len;
205 206 207

	assert(path);

208
	min_len = (size_t)(git_path_root(path) + 1);
209 210

	if (ceiling_directories == NULL || min_len == 0)
211
		return (int)min_len;
212 213 214 215 216

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

217
		if (len == 0 || len >= sizeof(buf) || git_path_root(ceil) == -1)
218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
			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) &&
			path[len] == '/' &&
			len > max_len)
		{
			max_len = len;
		}
	}

238
	return (int)(max_len <= min_len ? min_len : max_len);
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258
}

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

259 260
	if (git_buf_len(&file) <= prefix_len ||
		memcmp(git_buf_cstr(&file), GIT_FILE_CONTENT_PREFIX, prefix_len) != 0)
261 262 263 264 265
	{
		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) {
266
		const char *gitlink = git_buf_cstr(&file) + prefix_len;
267
		while (*gitlink && git__isspace(*gitlink)) gitlink++;
268 269
		error = git_path_prettify_dir(
			path_out, gitlink, git_buf_cstr(path_out));
270 271 272 273 274 275 276 277 278 279 280 281
	}

	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)
282
{
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299
	int error;
	git_buf path = GIT_BUF_INIT;
	struct stat st;
	dev_t initial_device = 0;
	bool try_with_dot_git = false;
	int ceiling_offset;

	git_buf_free(repo_path);

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

	ceiling_offset = find_ceiling_dir_offset(path.ptr, ceiling_dirs);

	if ((error = git_buf_joinpath(&path, path.ptr, DOT_GIT)) < 0)
		return error;

nulltoken committed
300
	while (!error && !git_buf_len(repo_path)) {
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321
		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);
322 323

					git_buf_free(&repo_link);
324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349
					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;
	}

	if (!error && parent_path != NULL) {
nulltoken committed
350
		if (!git_buf_len(repo_path))
351 352 353 354 355 356 357 358 359 360 361
			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
362
	if (!git_buf_len(repo_path) && !error) {
363
		giterr_set(GITERR_REPOSITORY,
364
			"Could not find repository from '%s'", start_path);
365
		error = GIT_ENOTFOUND;
366
	}
367

368 369 370 371 372 373
	return error;
}

int git_repository_open_ext(
	git_repository **repo_ptr,
	const char *start_path,
374
	unsigned int flags,
375 376 377 378 379 380
	const char *ceiling_dirs)
{
	int error;
	git_buf path = GIT_BUF_INIT, parent = GIT_BUF_INIT;
	git_repository *repo;

381 382
	if (repo_ptr)
		*repo_ptr = NULL;
383

384 385
	error = find_repo(&path, &parent, start_path, flags, ceiling_dirs);
	if (error < 0 || !repo_ptr)
386 387
		return error;

388
	repo = repository_alloc();
389
	GITERR_CHECK_ALLOC(repo);
390

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

394 395 396 397 398 399
	if ((error = load_config_data(repo)) < 0 ||
		(error = load_workdir(repo, &parent)) < 0)
	{
		git_repository_free(repo);
		return error;
	}
400

401
	git_buf_free(&parent);
402
	*repo_ptr = repo;
403
	return 0;
404
}
405

406 407 408 409 410 411
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);
}

412 413 414 415 416 417 418 419 420 421 422 423 424
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;
}

425 426 427 428 429 430 431 432 433
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;
434
	int error;
435 436 437 438 439

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

	*repository_path = '\0';

440
	if ((error = find_repo(&path, NULL, start_path, flags, ceiling_dirs)) < 0)
441
		return error != GIT_ENOTFOUND ? -1 : error;
442 443 444

	if (size < (size_t)(path.size + 1)) {
		giterr_set(GITERR_REPOSITORY,
445
			"The given buffer is too small to store the discovered path");
446 447 448 449 450 451 452 453
		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;
454 455
}

456
static int load_config(
457 458 459
	git_config **out,
	git_repository *repo,
	const char *global_config_path,
Sven Strickroth committed
460
	const char *xdg_config_path,
461
	const char *system_config_path)
462
{
463
	int error;
464
	git_buf config_path = GIT_BUF_INIT;
465
	git_config *cfg = NULL;
466

467
	assert(repo && out);
468

469 470
	if ((error = git_config_new(&cfg)) < 0)
		return error;
471

472 473 474
	error = git_buf_joinpath(
		&config_path, repo->path_repository, GIT_CONFIG_FILENAME_INREPO);
	if (error < 0)
475
		goto on_error;
476

477 478 479
	if ((error = git_config_add_file_ondisk(
			cfg, config_path.ptr, GIT_CONFIG_LEVEL_LOCAL, 0)) < 0 &&
		error != GIT_ENOTFOUND)
480 481 482
		goto on_error;

	git_buf_free(&config_path);
483

484 485 486 487 488
	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;
489

490 491 492 493 494
	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;
495

496 497 498 499 500
	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;
501

502 503
	giterr_clear(); /* clear any lingering ENOTFOUND errors */

504
	*out = cfg;
505
	return 0;
506

507 508
on_error:
	git_buf_free(&config_path);
509 510
	git_config_free(cfg);
	*out = NULL;
511
	return error;
512 513
}

514
int git_repository_config__weakptr(git_config **out, git_repository *repo)
515
{
516
	if (repo->_config == NULL) {
Sven Strickroth committed
517
		git_buf global_buf = GIT_BUF_INIT, xdg_buf = GIT_BUF_INIT, system_buf = GIT_BUF_INIT;
518
		int res;
519 520

		const char *global_config_path = NULL;
Sven Strickroth committed
521
		const char *xdg_config_path = NULL;
522
		const char *system_config_path = NULL;
523

524
		if (git_config_find_global_r(&global_buf) == 0)
525
			global_config_path = global_buf.ptr;
526

Sven Strickroth committed
527 528
		if (git_config_find_xdg_r(&xdg_buf) == 0)
			xdg_config_path = xdg_buf.ptr;
529

530
		if (git_config_find_system_r(&system_buf) == 0)
531
			system_config_path = system_buf.ptr;
532

Sven Strickroth committed
533
		res = load_config(&repo->_config, repo, global_config_path, xdg_config_path, system_config_path);
534 535

		git_buf_free(&global_buf);
536
		git_buf_free(&xdg_buf);
537 538
		git_buf_free(&system_buf);

539 540
		if (res < 0)
			return -1;
541 542 543

		GIT_REFCOUNT_OWN(repo->_config, repo);
	}
544

545
	*out = repo->_config;
546
	return 0;
547 548
}

549
int git_repository_config(git_config **out, git_repository *repo)
550
{
551 552
	if (git_repository_config__weakptr(out, repo) < 0)
		return -1;
553

554 555
	GIT_REFCOUNT_INC(*out);
	return 0;
556 557 558 559 560 561 562 563 564 565
}

void git_repository_set_config(git_repository *repo, git_config *config)
{
	assert(repo && config);

	drop_config(repo);

	repo->_config = config;
	GIT_REFCOUNT_OWN(repo->_config, repo);
566
	GIT_REFCOUNT_INC(repo->_config);
567 568 569 570 571 572 573
}

int git_repository_odb__weakptr(git_odb **out, git_repository *repo)
{
	assert(repo && out);

	if (repo->_odb == NULL) {
574
		git_buf odb_path = GIT_BUF_INIT;
575
		int res;
576

577 578
		if (git_buf_joinpath(&odb_path, repo->path_repository, GIT_OBJECTS_DIR) < 0)
			return -1;
579

580
		res = git_odb_open(&repo->_odb, odb_path.ptr);
581
		git_buf_free(&odb_path); /* done with path */
582 583 584

		if (res < 0)
			return -1;
585 586 587

		GIT_REFCOUNT_OWN(repo->_odb, repo);
	}
588

589
	*out = repo->_odb;
590
	return 0;
591 592
}

593
int git_repository_odb(git_odb **out, git_repository *repo)
594
{
595 596
	if (git_repository_odb__weakptr(out, repo) < 0)
		return -1;
Vicent Marti committed
597

598 599
	GIT_REFCOUNT_INC(*out);
	return 0;
600
}
601

602 603 604
void git_repository_set_odb(git_repository *repo, git_odb *odb)
{
	assert(repo && odb);
605

606
	drop_odb(repo);
Vicent Marti committed
607

608 609
	repo->_odb = odb;
	GIT_REFCOUNT_OWN(repo->_odb, repo);
610
	GIT_REFCOUNT_INC(odb);
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
int git_repository_refdb__weakptr(git_refdb **out, git_repository *repo)
{
	assert(out && repo);

	if (repo->_refdb == NULL) {
		int res;

		res = git_refdb_open(&repo->_refdb, repo);

		if (res < 0)
			return -1;

		GIT_REFCOUNT_OWN(repo->_refdb, repo);
	}

	*out = repo->_refdb;
	return 0;
}

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)
{
	assert (repo && refdb);

	drop_refdb(repo);

	repo->_refdb = refdb;
	GIT_REFCOUNT_OWN(repo->_refdb, repo);
	GIT_REFCOUNT_INC(refdb);
}

652 653 654 655 656
int git_repository_index__weakptr(git_index **out, git_repository *repo)
{
	assert(out && repo);

	if (repo->_index == NULL) {
657
		int res;
658
		git_buf index_path = GIT_BUF_INIT;
659

660 661
		if (git_buf_joinpath(&index_path, repo->path_repository, GIT_INDEX_FILE) < 0)
			return -1;
662

663
		res = git_index_open(&repo->_index, index_path.ptr);
664
		git_buf_free(&index_path); /* done with path */
665 666 667

		if (res < 0)
			return -1;
668 669

		GIT_REFCOUNT_OWN(repo->_index, repo);
670 671 672

		if (git_index_set_caps(repo->_index, GIT_INDEXCAP_FROM_OWNER) < 0)
			return -1;
673 674 675
	}

	*out = repo->_index;
676
	return 0;
677
}
Vicent Marti committed
678

679 680
int git_repository_index(git_index **out, git_repository *repo)
{
681 682
	if (git_repository_index__weakptr(out, repo) < 0)
		return -1;
683

684 685
	GIT_REFCOUNT_INC(*out);
	return 0;
686 687
}

688 689 690 691 692 693 694 695
void git_repository_set_index(git_repository *repo, git_index *index)
{
	assert(repo && index);

	drop_index(repo);

	repo->_index = index;
	GIT_REFCOUNT_OWN(repo->_index, repo);
696
	GIT_REFCOUNT_INC(index);
697 698
}

699
static int check_repositoryformatversion(git_config *config)
700
{
701
	int version;
702

703
	if (git_config_get_int32(&version, config, "core.repositoryformatversion") < 0)
704
		return -1;
705

706
	if (GIT_REPO_VERSION < version) {
707 708
		giterr_set(GITERR_REPOSITORY,
			"Unsupported repository version %d. Only versions up to %d are supported.",
709
			version, GIT_REPO_VERSION);
710 711
		return -1;
	}
712

713
	return 0;
714 715
}

716
static int repo_init_create_head(const char *git_dir, const char *ref_name)
717
{
718
	git_buf ref_path = GIT_BUF_INIT;
719
	git_filebuf ref = GIT_FILEBUF_INIT;
720
	const char *fmt;
721

722
	if (git_buf_joinpath(&ref_path, git_dir, GIT_HEAD_FILE) < 0 ||
723 724 725 726 727 728
		git_filebuf_open(&ref, ref_path.ptr, 0) < 0)
		goto fail;

	if (!ref_name)
		ref_name = GIT_BRANCH_MASTER;

729
	if (git__prefixcmp(ref_name, GIT_REFS_DIR) == 0)
730 731
		fmt = "ref: %s\n";
	else
732
		fmt = "ref: " GIT_REFS_HEADS_DIR "%s\n";
733 734

	if (git_filebuf_printf(&ref, fmt, ref_name) < 0 ||
735
		git_filebuf_commit(&ref, GIT_REFS_FILE_MODE) < 0)
736
		goto fail;
737

738
	git_buf_free(&ref_path);
739
	return 0;
740 741 742 743 744

fail:
	git_buf_free(&ref_path);
	git_filebuf_cleanup(&ref);
	return -1;
745 746
}

747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764
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);
765

766 767 768
	return _is_supported;
}

769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786
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;
}

787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811
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;
}

812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828
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;
}

829
static int repo_init_config(
830 831 832
	const char *repo_dir,
	const char *work_dir,
	git_repository_init_options *opts)
833
{
834
	int error = 0;
835 836
	git_buf cfg_path = GIT_BUF_INIT;
	git_config *config = NULL;
837

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

842
	if (git_buf_joinpath(&cfg_path, repo_dir, GIT_CONFIG_FILENAME_INREPO) < 0)
843
		return -1;
844

845 846 847 848 849 850
	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;
	}

851
	if (git_config_open_ondisk(&config, git_buf_cstr(&cfg_path)) < 0) {
852 853 854
		git_buf_free(&cfg_path);
		return -1;
	}
855

856
	if ((opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) != 0 &&
857 858
		(error = check_repositoryformatversion(config)) < 0)
		goto cleanup;
859

860 861 862 863 864 865 866
	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)));

867
	if (!(opts->flags & GIT_REPOSITORY_INIT_BARE)) {
868 869
		SET_REPO_CONFIG(bool, "core.logallrefupdates", true);

870 871 872 873 874 875 876
		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
877
			if (git_config_delete_entry(config, "core.worktree") < 0)
878
				giterr_clear();
879 880 881 882 883 884
		}
	} else {
		if (!are_symlinks_supported(repo_dir))
			SET_REPO_CONFIG(bool, "core.symlinks", false);
	}

885
	if (!(opts->flags & GIT_REPOSITORY_INIT__IS_REINIT) &&
886
		is_filesystem_case_insensitive(repo_dir))
887
		SET_REPO_CONFIG(bool, "core.ignorecase", true);
888

889
	if (opts->mode == GIT_REPOSITORY_INIT_SHARED_GROUP) {
890 891
		SET_REPO_CONFIG(int32, "core.sharedrepository", 1);
		SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
892 893
	}
	else if (opts->mode == GIT_REPOSITORY_INIT_SHARED_ALL) {
894 895 896
		SET_REPO_CONFIG(int32, "core.sharedrepository", 2);
		SET_REPO_CONFIG(bool, "receive.denyNonFastforwards", true);
	}
897

898
cleanup:
899
	git_buf_free(&cfg_path);
900
	git_config_free(config);
901

902
	return error;
903
}
904

905
static int repo_write_template(
906 907 908 909
	const char *git_dir,
	bool allow_overwrite,
	const char *file,
	mode_t mode,
910
	bool hidden,
911
	const char *content)
912 913
{
	git_buf path = GIT_BUF_INIT;
914
	int fd, error = 0, flags;
915 916 917 918

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

919 920 921 922 923 924
	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);
925

926 927
	if (fd >= 0) {
		error = p_write(fd, content, strlen(content));
928

929 930 931 932
		p_close(fd);
	}
	else if (errno != EEXIST)
		error = fd;
933

934 935 936 937 938 939 940 941 942
#ifdef GIT_WIN32
	if (!error && hidden) {
		if (p_hide_directory__w32(path.ptr) < 0)
			error = -1;
	}
#else
	GIT_UNUSED(hidden);
#endif

943
	git_buf_free(&path);
944 945 946 947 948 949

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

	return error;
950 951
}

952 953
static int repo_write_gitlink(
	const char *in_dir, const char *to_repo)
954
{
955 956 957 958 959 960 961
	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))
962
		return -1;
963

964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986
	/* 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)
987
		error = repo_write_template(in_dir, true, DOT_GIT, 0666, true, buf.ptr);
988 989 990 991 992 993

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

994 995 996
static mode_t pick_dir_mode(git_repository_init_options *opts)
{
	if (opts->mode == GIT_REPOSITORY_INIT_SHARED_UMASK)
997
		return 0777;
998 999 1000 1001 1002 1003 1004
	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;
}

1005 1006 1007 1008 1009 1010 1011
#include "repo_template.h"

static int repo_init_structure(
	const char *repo_dir,
	const char *work_dir,
	git_repository_init_options *opts)
{
1012
	int error = 0;
1013
	repo_template_item *tpl;
1014 1015 1016
	bool external_tpl =
		((opts->flags & GIT_REPOSITORY_INIT_EXTERNAL_TEMPLATE) != 0);
	mode_t dmode = pick_dir_mode(opts);
1017 1018

	/* Hide the ".git" directory */
1019
#ifdef GIT_WIN32
1020
	if ((opts->flags & GIT_REPOSITORY_INIT__HAS_DOTGIT) != 0) {
1021
		if (p_hide_directory__w32(repo_dir) < 0) {
1022 1023 1024 1025
			giterr_set(GITERR_REPOSITORY,
				"Failed to mark Git repository folder as hidden");
			return -1;
		}
1026
	}
1027 1028 1029 1030 1031 1032
#endif

	/* Create the .git gitlink if appropriate */
	if ((opts->flags & GIT_REPOSITORY_INIT_BARE) == 0 &&
		(opts->flags & GIT_REPOSITORY_INIT__NATURAL_WD) == 0)
	{
1033
		if (repo_write_gitlink(work_dir, repo_dir) < 0)
1034
			return -1;
1035
	}
1036

1037 1038 1039 1040
	/* Copy external template if requested */
	if (external_tpl) {
		git_config *cfg;
		const char *tdir;
1041

1042 1043
		if (opts->template_path)
			tdir = opts->template_path;
1044
		else if ((error = git_config_open_default(&cfg)) < 0)
1045 1046 1047
			return error;
		else {
			error = git_config_get_string(&tdir, cfg, "init.templatedir");
1048

1049
			git_config_free(cfg);
1050

1051 1052 1053 1054 1055
			if (error && error != GIT_ENOTFOUND)
				return error;

			giterr_clear();
			tdir = GIT_TEMPLATE_DIR;
1056
		}
1057 1058

		error = git_futils_cp_r(tdir, repo_dir,
1059 1060
			GIT_CPDIR_COPY_SYMLINKS | GIT_CPDIR_CHMOD_DIRS |
			GIT_CPDIR_SIMPLE_TO_MODE, dmode);
1061 1062 1063 1064 1065 1066 1067 1068

		if (error < 0) {
			if (strcmp(tdir, GIT_TEMPLATE_DIR) != 0)
				return error;

			/* if template was default, ignore error and use internal */
			giterr_clear();
			external_tpl = false;
1069
			error = 0;
1070 1071 1072 1073 1074 1075 1076 1077 1078 1079 1080 1081
		}
	}

	/* 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) {
1082 1083 1084 1085 1086
			const char *content = tpl->content;

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

1087 1088
			error = repo_write_template(
				repo_dir, false, tpl->path, tpl->mode, false, content);
1089
		}
1090 1091
	}

1092
	return error;
1093 1094
}

1095 1096
static int mkdir_parent(git_buf *buf, uint32_t mode, bool skip2)
{
1097 1098 1099
	/* When making parent directories during repository initialization
	 * don't try to set gid or grant world write access
	 */
1100
	return git_futils_mkdir(
1101
		buf->ptr, NULL, mode & ~(S_ISGID | 0002),
1102 1103 1104 1105
		GIT_MKDIR_PATH | GIT_MKDIR_VERIFY_DIR |
		(skip2 ? GIT_MKDIR_SKIP_LAST2 : GIT_MKDIR_SKIP_LAST));
}

1106 1107 1108 1109 1110
static int repo_init_directories(
	git_buf *repo_path,
	git_buf *wd_path,
	const char *given_repo,
	git_repository_init_options *opts)
1111
{
1112
	int error = 0;
1113
	bool is_bare, add_dotgit, has_dotgit, natural_wd;
1114
	mode_t dirmode;
1115

1116 1117 1118 1119 1120 1121 1122 1123 1124 1125 1126 1127 1128 1129 1130 1131 1132 1133 1134 1135
	/* 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
	 */

1136
	/* set up repo path */
1137

1138 1139
	is_bare = ((opts->flags & GIT_REPOSITORY_INIT_BARE) != 0);

1140 1141
	add_dotgit =
		(opts->flags & GIT_REPOSITORY_INIT_NO_DOTGIT_DIR) == 0 &&
1142
		!is_bare &&
1143 1144
		git__suffixcmp(given_repo, "/" DOT_GIT) != 0 &&
		git__suffixcmp(given_repo, "/" GIT_DIR) != 0;
1145

1146 1147
	if (git_buf_joinpath(repo_path, given_repo, add_dotgit ? GIT_DIR : "") < 0)
		return -1;
1148

1149 1150 1151 1152 1153 1154
	has_dotgit = (git__suffixcmp(repo_path->ptr, "/" GIT_DIR) == 0);
	if (has_dotgit)
		opts->flags |= GIT_REPOSITORY_INIT__HAS_DOTGIT;

	/* set up workdir path */

1155
	if (!is_bare) {
1156
		if (opts->workdir_path) {
1157 1158 1159
			if (git_path_join_unrooted(
					wd_path, opts->workdir_path, repo_path->ptr, NULL) < 0)
				return -1;
1160 1161 1162 1163 1164 1165 1166 1167
		} 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;
		}
1168

1169 1170 1171 1172 1173 1174 1175 1176 1177 1178 1179 1180 1181 1182 1183 1184
		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 */

1185
	dirmode = pick_dir_mode(opts);
1186

1187 1188 1189 1190 1191 1192 1193 1194 1195 1196 1197 1198 1199 1200 1201 1202 1203 1204 1205 1206 1207 1208 1209 1210 1211 1212 1213 1214
	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;
1215 1216
	}

1217 1218 1219 1220
	if ((opts->flags & GIT_REPOSITORY_INIT_MKDIR) != 0 ||
		(opts->flags & GIT_REPOSITORY_INIT_MKPATH) != 0 ||
		has_dotgit)
	{
1221
		/* create path #1 */
1222 1223
		error = git_futils_mkdir(repo_path->ptr, NULL, dirmode,
			GIT_MKDIR_VERIFY_DIR | ((dirmode & S_ISGID) ? GIT_MKDIR_CHMOD : 0));
1224
	}
1225

1226 1227 1228 1229 1230 1231 1232 1233 1234 1235 1236 1237 1238 1239 1240 1241 1242
	/* 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
1243
	if (!(error = git_remote_create(&remote, repo, GIT_REMOTE_ORIGIN, url))) {
1244 1245 1246 1247 1248 1249 1250 1251 1252
		git_remote_free(remote);
	}

	return error;
}

int git_repository_init(
	git_repository **repo_out, const char *path, unsigned is_bare)
{
1253
	git_repository_init_options opts = GIT_REPOSITORY_INIT_OPTIONS_INIT;
1254 1255 1256 1257 1258 1259 1260 1261 1262

	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(
1263
	git_repository **out,
1264 1265 1266 1267 1268 1269
	const char *given_repo,
	git_repository_init_options *opts)
{
	int error;
	git_buf repo_path = GIT_BUF_INIT, wd_path = GIT_BUF_INIT;

1270
	assert(out && given_repo && opts);
1271

Ben Straub committed
1272
	GITERR_CHECK_VERSION(opts, GIT_REPOSITORY_INIT_OPTIONS_VERSION, "git_repository_init_options");
1273

1274 1275
	error = repo_init_directories(&repo_path, &wd_path, given_repo, opts);
	if (error < 0)
1276
		goto cleanup;
1277 1278 1279 1280 1281 1282 1283 1284 1285 1286 1287 1288

	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;

1289 1290
		error = repo_init_config(
			git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts);
1291 1292 1293 1294 1295 1296

		/* TODO: reinitialize the templates */
	}
	else {
		if (!(error = repo_init_structure(
				git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)) &&
1297 1298
			!(error = repo_init_config(
				git_buf_cstr(&repo_path), git_buf_cstr(&wd_path), opts)))
1299 1300
			error = repo_init_create_head(
				git_buf_cstr(&repo_path), opts->initial_head);
1301
	}
1302 1303 1304
	if (error < 0)
		goto cleanup;

1305
	error = git_repository_open(out, git_buf_cstr(&repo_path));
1306

1307
	if (!error && opts->origin_url)
1308
		error = repo_init_create_origin(*out, opts->origin_url);
1309 1310

cleanup:
1311 1312 1313 1314
	git_buf_free(&repo_path);
	git_buf_free(&wd_path);

	return error;
1315
}
1316

1317
int git_repository_head_detached(git_repository *repo)
1318 1319
{
	git_reference *ref;
1320
	git_odb *odb = NULL;
1321
	int exists;
1322

1323 1324
	if (git_repository_odb__weakptr(&odb, repo) < 0)
		return -1;
1325

1326 1327
	if (git_reference_lookup(&ref, repo, GIT_HEAD_FILE) < 0)
		return -1;
1328

1329 1330
	if (git_reference_type(ref) == GIT_REF_SYMBOLIC) {
		git_reference_free(ref);
1331
		return 0;
1332
	}
1333

1334
	exists = git_odb_exists(odb, git_reference_target(ref));
1335 1336

	git_reference_free(ref);
1337
	return exists;
1338 1339
}

1340
int git_repository_head(git_reference **head_out, git_repository *repo)
1341
{
1342
	git_reference *head;
1343 1344
	int error;

1345 1346 1347 1348 1349 1350 1351 1352
	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;
	}

1353
	error = git_reference_lookup_resolved(head_out, repo, git_reference_symbolic_target(head), -1);
1354
	git_reference_free(head);
1355 1356

	return error == GIT_ENOTFOUND ? GIT_EORPHANEDHEAD : error;
1357 1358 1359 1360
}

int git_repository_head_orphan(git_repository *repo)
{
1361
	git_reference *ref = NULL;
1362 1363 1364
	int error;

	error = git_repository_head(&ref, repo);
1365
	git_reference_free(ref);
1366

1367
	if (error == GIT_EORPHANEDHEAD)
1368
		return 1;
1369

1370 1371 1372 1373
	if (error < 0)
		return -1;

	return 0;
1374
}
1375

1376
static int at_least_one_cb(const char *refname, void *payload)
1377
{
1378 1379
	GIT_UNUSED(refname);
	GIT_UNUSED(payload);
1380

1381 1382
	return GIT_EUSER;
}
1383

1384 1385 1386 1387 1388
static int repo_contains_no_reference(git_repository *repo)
{
	int error;
	
	error = git_reference_foreach(repo, GIT_REF_LISTALL, at_least_one_cb, NULL);
1389

1390
	if (error == GIT_EUSER)
1391
		return 0;
1392

1393 1394
	return error == 0 ? 1 : error;
}
1395

1396 1397 1398
int git_repository_is_empty(git_repository *repo)
{
	git_reference *head = NULL;
1399
	int error;
1400

1401
	if (git_reference_lookup(&head, repo, GIT_HEAD_FILE) < 0)
1402 1403
		return -1;

1404 1405 1406 1407
	if (!(error = git_reference_type(head) == GIT_REF_SYMBOLIC))
		goto cleanup;

	if (!(error = strcmp(
1408
		git_reference_symbolic_target(head),
1409 1410 1411 1412 1413 1414 1415 1416
		GIT_REFS_HEADS_DIR "master") == 0))
			goto cleanup;

	error = repo_contains_no_reference(repo);

cleanup:
	git_reference_free(head);
	return error < 0 ? -1 : error;
1417 1418
}

1419
const char *git_repository_path(git_repository *repo)
1420 1421
{
	assert(repo);
1422 1423
	return repo->path_repository;
}
1424

1425 1426 1427
const char *git_repository_workdir(git_repository *repo)
{
	assert(repo);
1428

1429 1430
	if (repo->is_bare)
		return NULL;
1431

1432 1433
	return repo->workdir;
}
1434

1435 1436
int git_repository_set_workdir(
	git_repository *repo, const char *workdir, int update_gitlink)
1437
{
1438
	int error = 0;
1439 1440
	git_buf path = GIT_BUF_INIT;

1441
	assert(repo && workdir);
1442

1443 1444
	if (git_path_prettify_dir(&path, workdir, NULL) < 0)
		return -1;
1445

1446 1447
	if (repo->workdir && strcmp(repo->workdir, path.ptr) == 0)
		return 0;
1448

1449 1450 1451 1452 1453 1454
	if (update_gitlink) {
		git_config *config;

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

1455
		error = repo_write_gitlink(path.ptr, git_repository_path(repo));
1456 1457 1458

		/* passthrough error means gitlink is unnecessary */
		if (error == GIT_PASSTHROUGH)
Ben Straub committed
1459
			error = git_config_delete_entry(config, "core.worktree");
1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476
		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;
1477
}
1478 1479 1480 1481 1482 1483

int git_repository_is_bare(git_repository *repo)
{
	assert(repo);
	return repo->is_bare;
}
1484 1485 1486

int git_repository_head_tree(git_tree **tree, git_repository *repo)
{
1487 1488 1489
	git_reference *head;
	git_object *obj;
	int error;
1490

1491 1492
	if ((error = git_repository_head(&head, repo)) < 0)
		return error;
1493

1494 1495
	if ((error = git_reference_peel(&obj, head, GIT_OBJ_TREE)) < 0)
		goto cleanup;
1496 1497

	*tree = (git_tree *)obj;
1498 1499 1500 1501

cleanup:
	git_reference_free(head);
	return error;
1502
}
1503 1504 1505

int git_repository_message(char *buffer, size_t len, git_repository *repo)
{
Vicent Marti committed
1506 1507 1508
	git_buf buf = GIT_BUF_INIT, path = GIT_BUF_INIT;
	struct stat st;
	int error;
1509

Edward Thomson committed
1510
	if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0)
Vicent Marti committed
1511
		return -1;
1512

1513
	if ((error = p_stat(git_buf_cstr(&path), &st)) < 0) {
1514 1515
		if (errno == ENOENT)
			error = GIT_ENOTFOUND;
Vicent Marti committed
1516
	}
1517 1518 1519
	else if (buffer != NULL) {
		error = git_futils_readbuffer(&buf, git_buf_cstr(&path));
		git_buf_copy_cstr(buffer, len, &buf);
Vicent Marti committed
1520
	}
1521

Vicent Marti committed
1522 1523
	git_buf_free(&path);
	git_buf_free(&buf);
1524

1525 1526 1527 1528
	if (!error)
		error = (int)st.st_size + 1; /* add 1 for NUL byte */

	return error;
1529 1530 1531 1532
}

int git_repository_message_remove(git_repository *repo)
{
Vicent Marti committed
1533 1534
	git_buf path = GIT_BUF_INIT;
	int error;
1535

Edward Thomson committed
1536
	if (git_buf_joinpath(&path, repo->path_repository, GIT_MERGE_MSG_FILE) < 0)
Vicent Marti committed
1537
		return -1;
1538 1539 1540 1541 1542 1543

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

	return error;
}
1544 1545 1546 1547 1548 1549 1550 1551 1552 1553

int git_repository_hashfile(
    git_oid *out,
    git_repository *repo,
    const char *path,
    git_otype type,
    const char *as_path)
{
	int error;
	git_vector filters = GIT_VECTOR_INIT;
1554
	git_file fd = -1;
1555 1556 1557
	git_off_t len;
	git_buf full_path = GIT_BUF_INIT;

1558 1559 1560 1561 1562 1563
	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.
	 */
1564 1565 1566 1567 1568 1569 1570 1571 1572 1573 1574 1575 1576 1577 1578 1579 1580 1581 1582 1583 1584 1585 1586 1587 1588 1589 1590 1591

	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) {
		error = git_filters_load(&filters, repo, as_path, GIT_FILTER_TO_ODB);
		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) {
1592
		error = (int)len;
1593 1594 1595 1596 1597 1598 1599 1600 1601
		goto cleanup;
	}

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

1602
	error = git_odb__hashfd_filtered(out, fd, (size_t)len, type, &filters);
1603 1604

cleanup:
1605 1606
	if (fd >= 0)
		p_close(fd);
1607 1608 1609 1610 1611 1612
	git_filters_free(&filters);
	git_buf_free(&full_path);

	return error;
}

1613 1614 1615 1616 1617 1618 1619 1620 1621 1622 1623 1624 1625 1626 1627 1628 1629 1630 1631 1632 1633
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))
1634
			error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, git_reference_name(ref), 1);
1635
		else
1636
			error = git_repository_set_head_detached(repo, git_reference_target(ref));
1637
	} else if (looks_like_a_branch(refname))
1638
		error = git_reference_symbolic_create(&new_head, repo, GIT_HEAD_FILE, refname, 1);
1639 1640 1641 1642 1643 1644

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

1645 1646 1647 1648 1649 1650 1651 1652 1653 1654 1655 1656 1657 1658 1659 1660 1661
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;

1662
	error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_object_id(peeled), 1);
1663 1664 1665 1666 1667 1668 1669 1670

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

1671 1672 1673 1674 1675 1676
int git_repository_detach_head(
	git_repository* repo)
{
	git_reference *old_head = NULL,
		*new_head = NULL;
	git_object *object = NULL;
1677
	int error;
1678 1679 1680

	assert(repo);

1681 1682
	if ((error = git_repository_head(&old_head, repo)) < 0)
		return error;
1683

1684
	if ((error = git_object_lookup(&object, repo, git_reference_target(old_head), GIT_OBJ_COMMIT)) < 0)
1685 1686
		goto cleanup;

1687
	error = git_reference_create(&new_head, repo, GIT_HEAD_FILE, git_reference_target(old_head), 1);
1688 1689 1690 1691 1692 1693 1694

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

1696 1697 1698 1699
/**
 * Loosely ported from git.git
 * https://github.com/git/git/blob/master/contrib/completion/git-prompt.sh#L198-289
 */
Edward Thomson committed
1700 1701 1702 1703 1704 1705 1706 1707 1708 1709
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;

1710 1711 1712 1713 1714 1715 1716 1717 1718 1719 1720
	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
1721 1722 1723 1724 1725
		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;
1726 1727
	else if(git_path_contains_file(&repo_path, GIT_BISECT_LOG_FILE))
		state = GIT_REPOSITORY_STATE_BISECT;
Edward Thomson committed
1728 1729 1730 1731

	git_buf_free(&repo_path);
	return state;
}