setup.c 15.2 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
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) {
		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;
}

381 382 383 384 385 386 387 388 389 390
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;
}

391 392 393 394
/*
 * 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.
 */
395
const char *setup_git_directory_gently(int *nongit_ok)
396
{
397
	const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
398
	const char *env_ceiling_dirs = getenv(CEILING_DIRECTORIES_ENVIRONMENT);
399
	static char cwd[PATH_MAX+1];
400
	const char *gitdirenv;
401
	const char *gitfile_dir;
402
	int len, offset, ceil_offset, root_len;
403 404
	dev_t current_device = 0;
	int one_filesystem = 1;
405
	struct stat buf;
406

407 408 409 410 411 412 413 414
	/*
	 * 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;

415 416 417 418 419
	/*
	 * If GIT_DIR is set explicitly, we're not going
	 * to do any discovery, but we still do repository
	 * validation.
	 */
420
	gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
421 422
	if (gitdirenv)
		return setup_explicit_git_dir(gitdirenv, work_tree_env, nongit_ok);
423

424
	if (!getcwd(cwd, sizeof(cwd)-1))
425
		die_errno("Unable to read current working directory");
426

427
	ceil_offset = longest_ancestor_length(cwd, env_ceiling_dirs);
J
Junio C Hamano 已提交
428 429
	if (ceil_offset < 0 && has_dos_drive_prefix(cwd))
		ceil_offset = 1;
430

431
	/*
432
	 * Test in the following order (relative to the cwd):
433
	 * - .git (file containing "gitdir: <path>")
434 435
	 * - .git/
	 * - ./ (bare)
436
	 * - ../.git
437 438 439 440
	 * - ../.git/
	 * - ../ (bare)
	 * - ../../.git/
	 *   etc.
441
	 */
442
	offset = len = strlen(cwd);
443
	one_filesystem = !git_env_bool("GIT_DISCOVERY_ACROSS_FILESYSTEM", 0);
444 445 446 447 448
	if (one_filesystem) {
		if (stat(".", &buf))
			die_errno("failed to stat '.'");
		current_device = buf.st_dev;
	}
449
	for (;;) {
450
		if (cwd_contains_git_dir(&gitfile_dir))
451
			break;
452 453 454
		if (is_git_directory("."))
			return setup_bare_git_dir(work_tree_env, offset,
							len, cwd, nongit_ok);
455
		while (--offset > ceil_offset && cwd[offset] != '/');
456 457
		if (offset <= ceil_offset)
			return setup_nongit(cwd, nongit_ok);
458 459 460 461 462 463 464 465 466 467 468 469 470 471
		if (one_filesystem) {
			if (stat("..", &buf)) {
				cwd[offset] = '\0';
				die_errno("failed to stat '%s/..'", cwd);
			}
			if (buf.st_dev != current_device) {
				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"
472
				"Stopping at filesystem boundary (GIT_DISCOVERY_ACROSS_FILESYSTEM not set).", cwd);
473 474
			}
		}
475 476
		if (chdir("..")) {
			cwd[offset] = '\0';
477
			die_errno("Cannot change to '%s/..'", cwd);
478
		}
479 480
	}

481 482 483
	inside_git_dir = 0;
	if (!work_tree_env)
		inside_work_tree = 1;
484 485
	root_len = offset_1st_component(cwd);
	git_work_tree_cfg = xstrndup(cwd, offset > root_len ? offset : root_len);
486 487
	if (check_repository_format_gently(nongit_ok))
		return NULL;
488
	if (offset == len)
489 490
		return NULL;

491 492 493 494 495
	/* Make "offset" point to past the '/', and add a '/' at the end */
	offset++;
	cwd[len++] = '/';
	cwd[len] = 0;
	return cwd + offset;
496
}
497

498 499
int git_config_perm(const char *var, const char *value)
{
500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523
	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
524
	 * a chmod value to restrict to.
525 526 527 528 529 530 531 532
	 */
	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;
533
	}
534 535 536 537 538 539 540 541 542 543 544 545

	/* 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.
	 */
546
	return -(i & 0666);
547 548
}

549
int check_repository_format_version(const char *var, const char *value, void *cb)
J
Junio C Hamano 已提交
550
{
551 552
	if (strcmp(var, "core.repositoryformatversion") == 0)
		repository_format_version = git_config_int(var, value);
553
	else if (strcmp(var, "core.sharedrepository") == 0)
554
		shared_repository = git_config_perm(var, value);
555 556 557 558 559
	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) {
560 561
		if (!value)
			return config_error_nonbool(var);
562
		free(git_work_tree_cfg);
563 564 565
		git_work_tree_cfg = xstrdup(value);
		inside_work_tree = -1;
	}
566
	return 0;
J
Junio C Hamano 已提交
567 568 569 570
}

int check_repository_format(void)
{
571
	return check_repository_format_gently(NULL);
J
Junio C Hamano 已提交
572 573
}

C
Clemens Buchacher 已提交
574 575 576 577 578 579
/*
 * 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.
 */
580 581
const char *setup_git_directory(void)
{
582
	const char *retval = setup_git_directory_gently(NULL);
583 584 585 586 587 588

	/* 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))
589
			die_errno ("Could not jump back into original cwd");
590
		rel = get_relative_cwd(buffer, PATH_MAX, get_git_work_tree());
591
		if (rel && *rel && chdir(get_git_work_tree()))
592
			die_errno ("Could not jump to working directory");
593 594 595
		return rel && *rel ? strcat(rel, "/") : NULL;
	}

596 597
	return retval;
}