setup.c 9.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

J
Junio C Hamano 已提交
7
const char *prefix_path(const char *prefix, int len, const char *path)
8
{
J
Junio C Hamano 已提交
9
	const char *orig = path;
10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
	for (;;) {
		char c;
		if (*path != '.')
			break;
		c = path[1];
		/* "." */
		if (!c) {
			path++;
			break;
		}
		/* "./" */
		if (c == '/') {
			path += 2;
			continue;
		}
		if (c != '.')
			break;
		c = path[2];
		if (!c)
			path += 2;
		else if (c == '/')
			path += 3;
		else
			break;
		/* ".." and "../" */
		/* Remove last component of the prefix */
		do {
			if (!len)
				die("'%s' is outside repository", orig);
			len--;
		} while (len && prefix[len-1] != '/');
		continue;
	}
	if (len) {
		int speclen = strlen(path);
		char *n = xmalloc(speclen + len + 1);
J
Junio C Hamano 已提交
46

47 48 49 50 51 52 53
		memcpy(n, prefix, len);
		memcpy(n + len, path, speclen+1);
		path = n;
	}
	return path;
}

J
Junio C Hamano 已提交
54
/*
55 56 57 58 59 60 61 62 63 64 65 66 67 68
 * 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];
	if (!pfx || !*pfx || arg[0] == '/')
		return arg;
	memcpy(path, pfx, pfx_len);
	strcpy(path + pfx_len, arg);
	return path;
}

69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86
/*
 * 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)
{
	const char *name;
	struct stat st;

	if (*arg == '-')
		die("bad flag '%s' used after filename", arg);
	name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
	if (!lstat(name, &st))
		return;
	if (errno == ENOENT)
87 88
		die("ambiguous argument '%s': unknown revision or path not in the working tree.\n"
		    "Use '--' to separate paths from revisions", arg);
89 90 91
	die("'%s': %s", arg, strerror(errno));
}

92 93 94 95 96 97 98 99 100 101
/*
 * 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)
{
	const char *name;
	struct stat st;

102
	if (!is_inside_work_tree() || is_inside_git_dir())
103
		return;
104 105 106 107 108 109
	if (*arg == '-')
		return; /* flag */
	name = prefix ? prefix_filename(prefix, strlen(prefix), arg) : arg;
	if (!lstat(name, &st))
		die("ambiguous argument '%s': both revision and filename\n"
		    "Use '--' to separate filenames from revisions", arg);
110
	if (errno != ENOENT && errno != ENOTDIR)
111 112 113
		die("'%s': %s", arg, strerror(errno));
}

J
Junio C Hamano 已提交
114
const char **get_pathspec(const char *prefix, const char **pathspec)
115
{
J
Junio C Hamano 已提交
116 117
	const char *entry = *pathspec;
	const char **p;
118 119
	int prefixlen;

120 121
	if (!prefix && !entry)
		return NULL;
122 123 124 125 126 127 128 129 130 131

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

	/* Otherwise we have to re-write the entries.. */
	p = pathspec;
132
	prefixlen = prefix ? strlen(prefix) : 0;
133
	do {
134
		*p = prefix_path(prefix, prefixlen, entry);
135 136 137 138
	} while ((entry = *++p) != NULL);
	return (const char **) pathspec;
}

139
/*
140
 * Test if it looks like we're at a git directory.
141
 * We want to see:
142
 *
143
 *  - either a objects/ directory _or_ the proper
144
 *    GIT_OBJECT_DIRECTORY environment variable
145
 *  - a refs/ directory
J
Junio C Hamano 已提交
146
 *  - either a HEAD symlink or a HEAD file that is formatted as
J
Junio C Hamano 已提交
147 148
 *    a proper "ref:", or a regular file HEAD that has a properly
 *    formatted sha1 object name.
149
 */
150
static int is_git_directory(const char *suspect)
151
{
152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
	char path[PATH_MAX];
	size_t len = strlen(suspect);

	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 已提交
168
		return 0;
169 170

	strcpy(path + len, "/HEAD");
J
Junio C Hamano 已提交
171
	if (validate_headref(path))
172 173
		return 0;

J
Junio C Hamano 已提交
174
	return 1;
175 176
}

177 178
int is_inside_git_dir(void)
{
179 180 181
	if (inside_git_dir < 0)
		inside_git_dir = is_inside_dir(get_git_dir());
	return inside_git_dir;
182 183 184 185
}

int is_inside_work_tree(void)
{
186 187 188
	if (inside_work_tree < 0)
		inside_work_tree = is_inside_dir(get_git_work_tree());
	return inside_work_tree;
189 190
}

191 192 193 194 195 196 197 198 199 200 201 202
/*
 * If no worktree was given, and we are outside of a default work tree,
 * now is the time to set it.
 *
 * In other words, if the user calls git with something like
 *
 *	git --git-dir=/some/where/else/.git bla
 *
 * default to /some/where/else as working directory; if the specified
 * git-dir does not end in "/.git", the cwd is used as working directory.
 */
const char *set_work_tree(const char *dir)
203
{
J
Junio C Hamano 已提交
204 205 206
	char dir_buffer[PATH_MAX], *rel = NULL;
	static char buffer[PATH_MAX + 1];
	int len, suffix_len = strlen(DEFAULT_GIT_DIR_ENVIRONMENT) + 1;
207 208 209

	/* strip the variable 'dir' of the postfix "/.git" if it has it */
	len = strlen(dir);
J
Junio C Hamano 已提交
210 211 212 213 214 215
	if (len > suffix_len &&
	    !strcmp(dir + len - suffix_len, "/" DEFAULT_GIT_DIR_ENVIRONMENT)) {
		if ((len - suffix_len) >= sizeof(dir_buffer))
			die("directory name too long");
		memcpy(dir_buffer, dir, len - suffix_len);
		dir_buffer[len - suffix_len] = '\0';
216 217 218 219

		/* are we inside the default work tree? */
		rel = get_relative_cwd(buffer, sizeof(buffer), dir_buffer);
	}
J
Junio C Hamano 已提交
220

221 222 223 224 225
	/* if rel is set, the cwd is _not_ the current working tree */
	if (rel && *rel) {
		if (!is_absolute_path(dir))
			set_git_dir(make_absolute_path(dir));
		dir = dir_buffer;
J
Junio C Hamano 已提交
226 227 228 229
		if (chdir(dir))
			die("cannot chdir to %s: %s", dir, strerror(errno));
		else
			strcat(rel, "/");
230 231 232 233
		inside_git_dir = 0;
	} else {
		rel = NULL;
		dir = getcwd(buffer, sizeof(buffer));
234
	}
235 236 237 238
	git_work_tree_cfg = xstrdup(dir);
	inside_work_tree = 1;

	return rel;
239 240
}

241 242 243 244
/*
 * 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.
 */
245
const char *setup_git_directory_gently(int *nongit_ok)
246
{
247
	const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);
248
	static char cwd[PATH_MAX+1];
249 250
	const char *gitdirenv;
	int len, offset;
251

252 253 254 255 256
	/*
	 * If GIT_DIR is set explicitly, we're not going
	 * to do any discovery, but we still do repository
	 * validation.
	 */
257
	gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
258 259 260 261 262 263
	if (gitdirenv) {
		if (PATH_MAX - 40 < strlen(gitdirenv))
			die("'$%s' too big", GIT_DIR_ENVIRONMENT);
		if (is_git_directory(gitdirenv)) {
			if (!work_tree_env)
				return set_work_tree(gitdirenv);
264
			return NULL;
265
		}
266
		if (nongit_ok) {
267 268 269
			*nongit_ok = 1;
			return NULL;
		}
270
		die("Not a git repository: '%s'", gitdirenv);
271
	}
272

273
	if (!getcwd(cwd, sizeof(cwd)-1))
274 275
		die("Unable to read current working directory");

276
	/*
277 278 279 280 281 282 283
	 * Test in the following order (relative to the cwd):
	 * - .git/
	 * - ./ (bare)
	 * - ../.git/
	 * - ../ (bare)
	 * - ../../.git/
	 *   etc.
284
	 */
285 286 287 288 289 290 291 292 293
	offset = len = strlen(cwd);
	for (;;) {
		if (is_git_directory(DEFAULT_GIT_DIR_ENVIRONMENT))
			break;
		if (is_git_directory(".")) {
			inside_git_dir = 1;
			if (!work_tree_env)
				inside_work_tree = 0;
			setenv(GIT_DIR_ENVIRONMENT, ".", 1);
294 295
			return NULL;
		}
296 297 298 299 300 301 302 303 304 305 306 307
		chdir("..");
		do {
			if (!offset) {
				if (nongit_ok) {
					if (chdir(cwd))
						die("Cannot come back to cwd");
					*nongit_ok = 1;
					return NULL;
				}
				die("Not a git repository");
			}
		} while (cwd[--offset] != '/');
308 309
	}

310 311 312 313 314
	inside_git_dir = 0;
	if (!work_tree_env)
		inside_work_tree = 1;
	git_work_tree_cfg = xstrndup(cwd, offset);
	if (offset == len)
315 316
		return NULL;

317 318 319 320 321
	/* Make "offset" point to past the '/', and add a '/' at the end */
	offset++;
	cwd[len++] = '/';
	cwd[len] = 0;
	return cwd + offset;
322
}
323

324 325 326
int git_config_perm(const char *var, const char *value)
{
	if (value) {
327
		int i;
328 329 330 331 332 333 334 335
		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;
336 337 338
		i = atoi(value);
		if (i > 1)
			return i;
339 340 341 342
	}
	return git_config_bool(var, value);
}

J
Junio C Hamano 已提交
343 344
int check_repository_format_version(const char *var, const char *value)
{
345 346
	if (strcmp(var, "core.repositoryformatversion") == 0)
		repository_format_version = git_config_int(var, value);
347
	else if (strcmp(var, "core.sharedrepository") == 0)
348
		shared_repository = git_config_perm(var, value);
349 350 351 352 353 354 355 356 357 358
	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) {
		if (git_work_tree_cfg)
			free(git_work_tree_cfg);
		git_work_tree_cfg = xstrdup(value);
		inside_work_tree = -1;
	}
359
	return 0;
J
Junio C Hamano 已提交
360 361 362 363 364 365 366 367 368 369 370
}

int check_repository_format(void)
{
	git_config(check_repository_format_version);
	if (GIT_REPO_VERSION < repository_format_version)
		die ("Expected git repo version <= %d, found %d",
		     GIT_REPO_VERSION, repository_format_version);
	return 0;
}

371 372
const char *setup_git_directory(void)
{
373
	const char *retval = setup_git_directory_gently(NULL);
374
	check_repository_format();
375 376 377 378 379 380 381 382 383 384 385

	/* 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))
			die ("Could not jump back into original cwd");
		rel = get_relative_cwd(buffer, PATH_MAX, get_git_work_tree());
		return rel && *rel ? strcat(rel, "/") : NULL;
	}

386 387
	return retval;
}