setup.c 15.8 KB
Newer Older
1
#include "cache.h"
2 3 4 5
#include "dir.h"

static int inside_git_dir = -1;
static int inside_work_tree = -1;
6

7 8 9 10
const char *prefix_path(const char *prefix, int len, const char *path)
{
	const char *orig = path;
	char *sanitized = xmalloc(len + strlen(path) + 1);
11
	if (is_absolute_path(orig))
12 13 14 15 16
		strcpy(sanitized, path);
	else {
		if (len)
			memcpy(sanitized, prefix, len);
		strcpy(sanitized + len, path);
17
	}
18
	if (normalize_path_copy(sanitized, sanitized))
19
		goto error_out;
20
	if (is_absolute_path(orig)) {
21
		size_t root_len, len, total;
22
		const char *work_tree = get_git_work_tree();
23 24 25
		if (!work_tree)
			goto error_out;
		len = strlen(work_tree);
26
		root_len = offset_1st_component(work_tree);
27
		total = strlen(sanitized) + 1;
28
		if (strncmp(sanitized, work_tree, len) ||
29
		    (len > root_len && sanitized[len] != '\0' && sanitized[len] != '/')) {
30
		error_out:
31
			die("'%s' is outside repository", orig);
32 33 34 35 36 37
		}
		if (sanitized[len] == '/')
			len++;
		memmove(sanitized, sanitized + len, total - len);
	}
	return sanitized;
38 39
}

J
Junio C Hamano 已提交
40
/*
41 42 43 44 45 46 47
 * Unlike prefix_path, this should be used if the named file does
 * not have to interact with index entry; i.e. name of a random file
 * on the filesystem.
 */
const char *prefix_filename(const char *pfx, int pfx_len, const char *arg)
{
	static char path[PATH_MAX];
F
Frank Li 已提交
48
#ifndef WIN32
49
	if (!pfx || !*pfx || is_absolute_path(arg))
50 51 52
		return arg;
	memcpy(path, pfx, pfx_len);
	strcpy(path + pfx_len, arg);
53 54 55 56 57 58 59 60 61 62 63 64
#else
	char *p;
	/* don't add prefix to absolute paths, but still replace '\' by '/' */
	if (is_absolute_path(arg))
		pfx_len = 0;
	else
		memcpy(path, pfx, pfx_len);
	strcpy(path + pfx_len, arg);
	for (p = path + pfx_len; *p; p++)
		if (*p == '\\')
			*p = '/';
#endif
65 66 67
	return path;
}

68 69 70 71 72 73 74 75 76 77 78 79 80
int check_filename(const char *prefix, const char *arg)
{
	const char *name;
	struct stat st;

	name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
	if (!lstat(name, &st))
		return 1; /* file exists */
	if (errno == ENOENT || errno == ENOTDIR)
		return 0; /* file does not exist */
	die_errno("failed to stat '%s'", arg);
}

81 82 83 84 85 86 87 88 89 90 91 92
static void NORETURN die_verify_filename(const char *prefix, const char *arg)
{
	unsigned char sha1[20];
	unsigned mode;
	/* try a detailed diagnostic ... */
	get_sha1_with_mode_1(arg, sha1, &mode, 0, prefix);
	/* ... or fall back the most general message. */
	die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
	    "Use '--' to separate paths from revisions", arg);

}

93 94 95 96 97 98 99 100 101 102 103
/*
 * Verify a filename that we got as an argument for a pathspec
 * entry. Note that a filename that begins with "-" never verifies
 * as true, because even if such a filename were to exist, we want
 * it to be preceded by the "--" marker (or we want the user to
 * use a format like "./-filename")
 */
void verify_filename(const char *prefix, const char *arg)
{
	if (*arg == '-')
		die("bad flag '%s' used after filename", arg);
104
	if (check_filename(prefix, arg))
105
		return;
106
	die_verify_filename(prefix, arg);
107 108
}

109 110 111 112 113 114 115
/*
 * Opposite of the above: the command line did not have -- marker
 * and we parsed the arg as a refname.  It should not be interpretable
 * as a filename.
 */
void verify_non_filename(const char *prefix, const char *arg)
{
116
	if (!is_inside_work_tree() || is_inside_git_dir())
117
		return;
118 119
	if (*arg == '-')
		return; /* flag */
120 121 122 123
	if (!check_filename(prefix, arg))
		return;
	die("ambiguous argument '%s': both revision and filename\n"
	    "Use '--' to separate filenames from revisions", arg);
124 125
}

J
Junio C Hamano 已提交
126
const char **get_pathspec(const char *prefix, const char **pathspec)
127
{
J
Junio C Hamano 已提交
128
	const char *entry = *pathspec;
129
	const char **src, **dst;
130 131
	int prefixlen;

132 133
	if (!prefix && !entry)
		return NULL;
134 135 136 137 138 139 140 141 142

	if (!entry) {
		static const char *spec[2];
		spec[0] = prefix;
		spec[1] = NULL;
		return spec;
	}

	/* Otherwise we have to re-write the entries.. */
143 144
	src = pathspec;
	dst = pathspec;
145
	prefixlen = prefix ? strlen(prefix) : 0;
146 147
	while (*src) {
		const char *p = prefix_path(prefix, prefixlen, *src);
148
		*(dst++) = p;
149 150 151 152 153 154
		src++;
	}
	*dst = NULL;
	if (!*pathspec)
		return NULL;
	return pathspec;
155 156
}

157
/*
158
 * Test if it looks like we're at a git directory.
159
 * We want to see:
160
 *
161
 *  - either an objects/ directory _or_ the proper
162
 *    GIT_OBJECT_DIRECTORY environment variable
163
 *  - a refs/ directory
J
Junio C Hamano 已提交
164
 *  - either a HEAD symlink or a HEAD file that is formatted as
J
Junio C Hamano 已提交
165 166
 *    a proper "ref:", or a regular file HEAD that has a properly
 *    formatted sha1 object name.
167
 */
168
static int is_git_directory(const char *suspect)
169
{
170 171 172
	char path[PATH_MAX];
	size_t len = strlen(suspect);

173 174
	if (PATH_MAX <= len + strlen("/objects"))
		die("Too long path: %.*s", 60, suspect);
175 176 177 178 179 180 181 182 183 184 185 186 187
	strcpy(path, suspect);
	if (getenv(DB_ENVIRONMENT)) {
		if (access(getenv(DB_ENVIRONMENT), X_OK))
			return 0;
	}
	else {
		strcpy(path + len, "/objects");
		if (access(path, X_OK))
			return 0;
	}

	strcpy(path + len, "/refs");
	if (access(path, X_OK))
J
Junio C Hamano 已提交
188
		return 0;
189 190

	strcpy(path + len, "/HEAD");
J
Junio C Hamano 已提交
191
	if (validate_headref(path))
192 193
		return 0;

J
Junio C Hamano 已提交
194
	return 1;
195 196
}

197 198
int is_inside_git_dir(void)
{
199 200 201
	if (inside_git_dir < 0)
		inside_git_dir = is_inside_dir(get_git_dir());
	return inside_git_dir;
202 203 204 205
}

int is_inside_work_tree(void)
{
206 207 208
	if (inside_work_tree < 0)
		inside_work_tree = is_inside_dir(get_git_work_tree());
	return inside_work_tree;
209 210
}

211
/*
212
 * set_work_tree() is only ever called if you set GIT_DIR explicitly.
213 214 215
 * The old behaviour (which we retain here) is to set the work tree root
 * to the cwd, unless overridden by the config, the command line, or
 * GIT_WORK_TREE.
216
 */
217
static const char *set_work_tree(const char *dir)
218
{
219
	char buffer[PATH_MAX + 1];
220

221 222 223
	if (!getcwd(buffer, sizeof(buffer)))
		die ("Could not get the current working directory");
	git_work_tree_cfg = xstrdup(buffer);
224 225
	inside_work_tree = 1;

226
	return NULL;
227 228
}

229 230
void setup_work_tree(void)
{
231 232 233 234 235 236 237
	const char *work_tree, *git_dir;
	static int initialized = 0;

	if (initialized)
		return;
	work_tree = get_git_work_tree();
	git_dir = get_git_dir();
M
Mike Hommey 已提交
238
	if (!is_absolute_path(git_dir))
239
		git_dir = make_absolute_path(git_dir);
M
Mike Hommey 已提交
240 241
	if (!work_tree || chdir(work_tree))
		die("This operation must be run in a work tree");
242
	set_git_dir(make_relative_path(git_dir, work_tree));
243
	initialized = 1;
M
Mike Hommey 已提交
244 245
}

246 247
static int check_repository_format_gently(int *nongit_ok)
{
248
	git_config(check_repository_format_version, NULL);
249 250 251 252 253 254 255 256 257 258 259 260 261
	if (GIT_REPO_VERSION < repository_format_version) {
		if (!nongit_ok)
			die ("Expected git repo version <= %d, found %d",
			     GIT_REPO_VERSION, repository_format_version);
		warning("Expected git repo version <= %d, found %d",
			GIT_REPO_VERSION, repository_format_version);
		warning("Please upgrade Git");
		*nongit_ok = -1;
		return -1;
	}
	return 0;
}

262 263 264 265 266 267 268
/*
 * Try to read the location of the git directory from the .git file,
 * return path to git directory if found.
 */
const char *read_gitfile_gently(const char *path)
{
	char *buf;
269 270
	char *dir;
	const char *slash;
271 272 273 274 275 276 277 278 279 280
	struct stat st;
	int fd;
	size_t len;

	if (stat(path, &st))
		return NULL;
	if (!S_ISREG(st.st_mode))
		return NULL;
	fd = open(path, O_RDONLY);
	if (fd < 0)
281
		die_errno("Error opening '%s'", path);
282 283 284 285 286 287 288 289 290 291 292 293 294
	buf = xmalloc(st.st_size + 1);
	len = read_in_full(fd, buf, st.st_size);
	close(fd);
	if (len != st.st_size)
		die("Error reading %s", path);
	buf[len] = '\0';
	if (prefixcmp(buf, "gitdir: "))
		die("Invalid gitfile format: %s", path);
	while (buf[len - 1] == '\n' || buf[len - 1] == '\r')
		len--;
	if (len < 9)
		die("No path in gitfile: %s", path);
	buf[len] = '\0';
295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
	dir = buf + 8;

	if (!is_absolute_path(dir) && (slash = strrchr(path, '/'))) {
		size_t pathlen = slash+1 - path;
		size_t dirlen = pathlen + len - 8;
		dir = xmalloc(dirlen + 1);
		strncpy(dir, path, pathlen);
		strncpy(dir + pathlen, buf + 8, len - 8);
		dir[dirlen] = '\0';
		free(buf);
		buf = dir;
	}

	if (!is_git_directory(dir))
		die("Not a git repository: %s", dir);
	path = make_absolute_path(dir);

312 313 314 315
	free(buf);
	return path;
}

316 317 318 319 320 321 322 323 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 350
static const char *setup_explicit_git_dir(const char *gitdirenv,
				const char *work_tree_env, int *nongit_ok)
{
	static char buffer[1024 + 1];
	const char *retval;

	if (PATH_MAX - 40 < strlen(gitdirenv))
		die("'$%s' too big", GIT_DIR_ENVIRONMENT);
	if (!is_git_directory(gitdirenv)) {
		if (nongit_ok) {
			*nongit_ok = 1;
			return NULL;
		}
		die("Not a git repository: '%s'", gitdirenv);
	}
	if (!work_tree_env) {
		retval = set_work_tree(gitdirenv);
		/* config may override worktree */
		if (check_repository_format_gently(nongit_ok))
			return NULL;
		return retval;
	}
	if (check_repository_format_gently(nongit_ok))
		return NULL;
	retval = get_relative_cwd(buffer, sizeof(buffer) - 1,
			get_git_work_tree());
	if (!retval || !*retval)
		return NULL;
	set_git_dir(make_absolute_path(gitdirenv));
	if (chdir(work_tree_env) < 0)
		die_errno ("Could not chdir to '%s'", work_tree_env);
	strcat(buffer, "/");
	return retval;
}

351 352 353 354 355 356 357 358 359 360 361 362
static int cwd_contains_git_dir(const char **gitfile_dirp)
{
	const char *gitfile_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
	*gitfile_dirp = gitfile_dir;
	if (gitfile_dir) {
		if (set_git_dir(gitfile_dir))
			die("Repository setup failed");
		return 1;
	}
	return is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT);
}

363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384
static const char *setup_discovered_git_dir(const char *work_tree_env,
		int offset, int len, char *cwd, int *nongit_ok)
{
	int root_len;

	inside_git_dir = 0;
	if (!work_tree_env)
		inside_work_tree = 1;
	root_len = offset_1st_component(cwd);
	git_work_tree_cfg = xstrndup(cwd, offset > root_len ? offset : root_len);
	if (check_repository_format_gently(nongit_ok))
		return NULL;
	if (offset == len)
		return NULL;

	/* Make "offset" point to past the '/', and add a '/' at the end */
	offset++;
	cwd[len++] = '/';
	cwd[len] = 0;
	return cwd + offset;
}

385 386 387 388 389 390 391 392 393
static const char *setup_bare_git_dir(const char *work_tree_env,
		int offset, int len, char *cwd, int *nongit_ok)
{
	int root_len;

	inside_git_dir = 1;
	if (!work_tree_env)
		inside_work_tree = 0;
	if (offset != len) {
394 395
		if (chdir(cwd))
			die_errno("Cannot come back to cwd");
396 397 398 399 400 401 402 403 404
		root_len = offset_1st_component(cwd);
		cwd[offset > root_len ? offset : root_len] = '\0';
		set_git_dir(cwd);
	} else
		set_git_dir(".");
	check_repository_format_gently(nongit_ok);
	return NULL;
}

405 406 407 408 409 410 411 412 413 414
static const char *setup_nongit(const char *cwd, int *nongit_ok)
{
	if (!nongit_ok)
		die("Not a git repository (or any of the parent directories): %s", DEFAULT_GIT_DIR_ENVIRONMENT);
	if (chdir(cwd))
		die_errno("Cannot come back to cwd");
	*nongit_ok = 1;
	return NULL;
}

415 416 417 418 419 420 421 422 423 424
static dev_t get_device_or_die(const char *path, const char *prefix)
{
	struct stat buf;
	if (stat(path, &buf))
		die_errno("failed to stat '%s%s%s'",
				prefix ? prefix : "",
				prefix ? "/" : "", path);
	return buf.st_dev;
}

425 426 427 428
/*
 * We cannot decide in this function whether we are in the work tree or
 * not, since the config can only be read _after_ this function was called.
 */
429
static const char *setup_git_directory_gently_1(int *nongit_ok)
430
{
431
	const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
432
	const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
433
	static char cwd[PATH_MAX+1];
434
	const char *gitdirenv;
435
	const char *gitfile_dir;
436
	int len, offset, ceil_offset;
437 438
	dev_t current_device = 0;
	int one_filesystem = 1;
439

440 441 442 443 444 445 446 447
	/*
	 * Let's assume that we are in a git repository.
	 * If it turns out later that we are somewhere else, the value will be
	 * updated accordingly.
	 */
	if (nongit_ok)
		*nongit_ok = 0;

448 449 450 451 452
	/*
	 * If GIT_DIR is set explicitly, we're not going
	 * to do any discovery, but we still do repository
	 * validation.
	 */
453
	gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
454 455
	if (gitdirenv)
		return setup_explicit_git_dir(gitdirenv, work_tree_env, nongit_ok);
456

457
	if (!getcwd(cwd, sizeof(cwd)-1))
458
		die_errno("Unable to read current working directory");
459

460
	ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs);
J
Junio C Hamano 已提交
461 462
	if (ceil_offset < 0 && has_dos_drive_prefix(cwd))
		ceil_offset = 1;
463

464
	/*
465
	 * Test in the following order (relative to the cwd):
466
	 * - .git (file containing "gitdir: <path>")
467 468
	 * - .git/
	 * - ./ (bare)
469
	 * - ../.git
470 471 472 473
	 * - ../.git/
	 * - ../ (bare)
	 * - ../../.git/
	 *   etc.
474
	 */
475
	offset = len = strlen(cwd);
476
	one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0);
477 478
	if (one_filesystem)
		current_device = get_device_or_die(".", NULL);
479
	for (;;) {
480
		if (cwd_contains_git_dir(&gitfile_dir))
481 482
			return setup_discovered_git_dir(work_tree_env, offset,
							len, cwd, nongit_ok);
483 484 485
		if (is_git_directory("."))
			return setup_bare_git_dir(work_tree_env, offset,
							len, cwd, nongit_ok);
486
		while (--offset > ceil_offset && cwd[offset] != '/');
487 488
		if (offset <= ceil_offset)
			return setup_nongit(cwd, nongit_ok);
489
		if (one_filesystem) {
490 491
			dev_t parent_device = get_device_or_die("..", cwd);
			if (parent_device != current_device) {
492 493 494 495 496 497 498 499
				if (nongit_ok) {
					if (chdir(cwd))
						die_errno("Cannot come back to cwd");
					*nongit_ok = 1;
					return NULL;
				}
				cwd[offset] = '\0';
				die("Not a git repository (or any parent up to mount parent %s)\n"
500
				"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).", cwd);
501 502
			}
		}
503 504
		if (chdir("..")) {
			cwd[offset] = '\0';
505
			die_errno("Cannot change to '%s/..'", cwd);
506
		}
507
	}
508
}
509

510 511 512 513 514 515 516 517 518 519
const char *setup_git_directory_gently(int *nongit_ok)
{
	const char *prefix;

	prefix = setup_git_directory_gently_1(nongit_ok);
	if (startup_info)
		startup_info->have_repository = !nongit_ok || !*nongit_ok;
	return prefix;
}

520 521
int git_config_perm(const char *var, const char *value)
{
522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545
	int i;
	char *endptr;

	if (value == NULL)
		return PERM_GROUP;

	if (!strcmp(value, "umask"))
		return PERM_UMASK;
	if (!strcmp(value, "group"))
		return PERM_GROUP;
	if (!strcmp(value, "all") ||
	    !strcmp(value, "world") ||
	    !strcmp(value, "everybody"))
		return PERM_EVERYBODY;

	/* Parse octal numbers */
	i = strtol(value, &endptr, 8);

	/* If not an octal number, maybe true/false? */
	if (*endptr != 0)
		return git_config_bool(var, value) ? PERM_GROUP : PERM_UMASK;

	/*
	 * Treat values 0, 1 and 2 as compatibility cases, otherwise it is
546
	 * a chmod value to restrict to.
547 548 549 550 551 552 553 554
	 */
	switch (i) {
	case PERM_UMASK:               /* 0 */
		return PERM_UMASK;
	case OLD_PERM_GROUP:           /* 1 */
		return PERM_GROUP;
	case OLD_PERM_EVERYBODY:       /* 2 */
		return PERM_EVERYBODY;
555
	}
556 557 558 559 560 561 562 563 564 565 566 567

	/* A filemode value was given: 0xxx */

	if ((i & 0600) != 0600)
		die("Problem with core.sharedRepository filemode value "
		    "(0%.3o).\nThe owner of files must always have "
		    "read and write permissions.", i);

	/*
	 * Mask filemode value. Others can not get write permission.
	 * x flags for directories are handled separately.
	 */
568
	return -(i & 0666);
569 570
}

571
int check_repository_format_version(const char *var, const char *value, void *cb)
J
Junio C Hamano 已提交
572
{
573 574
	if (strcmp(var, "core.repositoryformatversion") == 0)
		repository_format_version = git_config_int(var, value);
575
	else if (strcmp(var, "core.sharedrepository") == 0)
576
		shared_repository = git_config_perm(var, value);
577 578 579 580 581
	else if (strcmp(var, "core.bare") == 0) {
		is_bare_repository_cfg = git_config_bool(var, value);
		if (is_bare_repository_cfg == 1)
			inside_work_tree = -1;
	} else if (strcmp(var, "core.worktree") == 0) {
582 583
		if (!value)
			return config_error_nonbool(var);
584
		free(git_work_tree_cfg);
585 586 587
		git_work_tree_cfg = xstrdup(value);
		inside_work_tree = -1;
	}
588
	return 0;
J
Junio C Hamano 已提交
589 590 591 592
}

int check_repository_format(void)
{
593
	return check_repository_format_gently(NULL);
J
Junio C Hamano 已提交
594 595
}

C
Clemens Buchacher 已提交
596 597 598 599 600 601
/*
 * Returns the "prefix", a path to the current working directory
 * relative to the work tree root, or NULL, if the current working
 * directory is not a strict subdirectory of the work tree root. The
 * prefix always ends with a '/' character.
 */
602 603
const char *setup_git_directory(void)
{
604
	const char *retval = setup_git_directory_gently(NULL);
605 606 607 608 609 610

	/* If the work tree is not the default one, recompute prefix */
	if (inside_work_tree < 0) {
		static char buffer[PATH_MAX + 1];
		char *rel;
		if (retval && chdir(retval))
611
			die_errno ("Could not jump back into original cwd");
612
		rel = get_relative_cwd(buffer, PATH_MAX, get_git_work_tree());
613
		if (rel && *rel && chdir(get_git_work_tree()))
614
			die_errno ("Could not jump to working directory");
615 616 617
		return rel && *rel ? strcat(rel, "/") : NULL;
	}

618 619
	return retval;
}