diff options
Diffstat (limited to 'setup.c')
| -rw-r--r-- | setup.c | 279 | 
1 files changed, 127 insertions, 152 deletions
| @@ -1,4 +1,8 @@  #include "cache.h" +#include "dir.h" + +static int inside_git_dir = -1; +static int inside_work_tree = -1;  const char *prefix_path(const char *prefix, int len, const char *path)  { @@ -170,100 +174,89 @@ static int is_git_directory(const char *suspect)  	return 1;  } -static int inside_git_dir = -1; -  int is_inside_git_dir(void)  { -	if (inside_git_dir >= 0) -		return inside_git_dir; -	die("BUG: is_inside_git_dir called before setup_git_directory"); +	if (inside_git_dir < 0) +		inside_git_dir = is_inside_dir(get_git_dir()); +	return inside_git_dir;  } -static int inside_work_tree = -1; -  int is_inside_work_tree(void)  { -	if (inside_git_dir >= 0) -		return inside_work_tree; -	die("BUG: is_inside_work_tree called before setup_git_directory"); +	if (inside_work_tree < 0) +		inside_work_tree = is_inside_dir(get_git_work_tree()); +	return inside_work_tree;  } -static char *gitworktree_config; - -static int git_setup_config(const char *var, const char *value) +/* + * 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)  { -	if (!strcmp(var, "core.worktree")) { -		if (gitworktree_config) -			strlcpy(gitworktree_config, value, PATH_MAX); -		return 0; +	char dir_buffer[PATH_MAX]; +	static char buffer[PATH_MAX + 1], *rel = NULL; +	int len, postfix_len = strlen(DEFAULT_GIT_DIR_ENVIRONMENT) + 1; + +	/* strip the variable 'dir' of the postfix "/.git" if it has it */ +	len = strlen(dir); +	if (len > postfix_len && !strcmp(dir + len - postfix_len, +				"/" DEFAULT_GIT_DIR_ENVIRONMENT)) { +			strncpy(dir_buffer, dir, len - postfix_len); + +		/* are we inside the default work tree? */ +		rel = get_relative_cwd(buffer, sizeof(buffer), dir_buffer); +	} +	/* 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; +		chdir(dir); +		strcat(rel, "/"); +		inside_git_dir = 0; +	} else { +		rel = NULL; +		dir = getcwd(buffer, sizeof(buffer));  	} -	return git_default_config(var, value); +	git_work_tree_cfg = xstrdup(dir); +	inside_work_tree = 1; + +	return rel;  } +/* + * 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. + */  const char *setup_git_directory_gently(int *nongit_ok)  { +	const char *work_tree_env = getenv(GIT_WORK_TREE_ENVIRONMENT);  	static char cwd[PATH_MAX+1]; -	char worktree[PATH_MAX+1], gitdir[PATH_MAX+1]; -	const char *gitdirenv, *gitworktree; -	int wt_rel_gitdir = 0; +	const char *gitdirenv; +	int len, offset; +	/* +	 * If GIT_DIR is set explicitly, we're not going +	 * to do any discovery, but we still do repository +	 * validation. +	 */  	gitdirenv = getenv(GIT_DIR_ENVIRONMENT); -	if (!gitdirenv) { -		int len, offset; - -		if (!getcwd(cwd, sizeof(cwd)-1)) -			die("Unable to read current working directory"); - -		offset = len = strlen(cwd); -		for (;;) { -			if (is_git_directory(".git")) -				break; -			if (offset == 0) { -				offset = -1; -				break; -			} -			chdir(".."); -			while (cwd[--offset] != '/') -				; /* do nothing */ -		} - -		if (offset >= 0) { -			inside_work_tree = 1; -			git_config(git_default_config); -			if (offset == len) { -				inside_git_dir = 0; -				return NULL; -			} - -			cwd[len++] = '/'; -			cwd[len] = '\0'; -			inside_git_dir = !prefixcmp(cwd + offset + 1, ".git/"); -			return cwd + offset + 1; -		} - -		if (chdir(cwd)) -			die("Cannot come back to cwd"); -		if (!is_git_directory(".")) { -			if (nongit_ok) { -				*nongit_ok = 1; -				return NULL; -			} -			die("Not a git repository"); -		} -		setenv(GIT_DIR_ENVIRONMENT, cwd, 1); -		gitdirenv = getenv(GIT_DIR_ENVIRONMENT); -		if (!gitdirenv) -			die("getenv after setenv failed"); -	} - -	if (PATH_MAX - 40 < strlen(gitdirenv)) { -		if (nongit_ok) { -			*nongit_ok = 1; +	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);  			return NULL;  		} -		die("$%s too big", GIT_DIR_ENVIRONMENT); -	} -	if (!is_git_directory(gitdirenv)) {  		if (nongit_ok) {  			*nongit_ok = 1;  			return NULL; @@ -273,92 +266,53 @@ const char *setup_git_directory_gently(int *nongit_ok)  	if (!getcwd(cwd, sizeof(cwd)-1))  		die("Unable to read current working directory"); -	if (chdir(gitdirenv)) { -		if (nongit_ok) { -			*nongit_ok = 1; -			return NULL; -		} -		die("Cannot change directory to $%s '%s'", -			GIT_DIR_ENVIRONMENT, gitdirenv); -	} -	if (!getcwd(gitdir, sizeof(gitdir)-1)) -		die("Unable to read current working directory"); -	if (chdir(cwd)) -		die("Cannot come back to cwd");  	/* -	 * In case there is a work tree we may change the directory, -	 * therefore make GIT_DIR an absolute path. +	 * Test in the following order (relative to the cwd): +	 * - .git/ +	 * - ./ (bare) +	 * - ../.git/ +	 * - ../ (bare) +	 * - ../../.git/ +	 *   etc.  	 */ -	if (gitdirenv[0] != '/') { -		setenv(GIT_DIR_ENVIRONMENT, gitdir, 1); -		gitdirenv = getenv(GIT_DIR_ENVIRONMENT); -		if (!gitdirenv) -			die("getenv after setenv failed"); -		if (PATH_MAX - 40 < strlen(gitdirenv)) { -			if (nongit_ok) { -				*nongit_ok = 1; -				return NULL; -			} -			die("$%s too big after expansion to absolute path", -				GIT_DIR_ENVIRONMENT); -		} -	} - -	strcat(cwd, "/"); -	strcat(gitdir, "/"); -	inside_git_dir = !prefixcmp(cwd, gitdir); - -	gitworktree = getenv(GIT_WORK_TREE_ENVIRONMENT); -	if (!gitworktree) { -		gitworktree_config = worktree; -		worktree[0] = '\0'; -	} -	git_config(git_setup_config); -	if (!gitworktree) { -		gitworktree_config = NULL; -		if (worktree[0]) -			gitworktree = worktree; -		if (gitworktree && gitworktree[0] != '/') -			wt_rel_gitdir = 1; -	} - -	if (wt_rel_gitdir && chdir(gitdirenv)) -		die("Cannot change directory to $%s '%s'", -			GIT_DIR_ENVIRONMENT, gitdirenv); -	if (gitworktree && chdir(gitworktree)) { -		if (nongit_ok) { -			if (wt_rel_gitdir && chdir(cwd)) -				die("Cannot come back to cwd"); -			*nongit_ok = 1; +	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);  			return NULL;  		} -		if (wt_rel_gitdir) -			die("Cannot change directory to working tree '%s'" -				" from $%s", gitworktree, GIT_DIR_ENVIRONMENT); -		else -			die("Cannot change directory to working tree '%s'", -				gitworktree); -	} -	if (!getcwd(worktree, sizeof(worktree)-1)) -		die("Unable to read current working directory"); -	strcat(worktree, "/"); -	inside_work_tree = !prefixcmp(cwd, worktree); - -	if (gitworktree && inside_work_tree && !prefixcmp(worktree, gitdir) && -	    strcmp(worktree, gitdir)) { -		inside_git_dir = 0; +		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] != '/');  	} -	if (!inside_work_tree) { -		if (chdir(cwd)) -			die("Cannot come back to cwd"); +	inside_git_dir = 0; +	if (!work_tree_env) +		inside_work_tree = 1; +	git_work_tree_cfg = xstrndup(cwd, offset); +	if (offset == len)  		return NULL; -	} -	if (!strcmp(cwd, worktree)) -		return NULL; -	return cwd+strlen(worktree); +	/* Make "offset" point to past the '/', and add a '/' at the end */ +	offset++; +	cwd[len++] = '/'; +	cwd[len] = 0; +	return cwd + offset;  }  int git_config_perm(const char *var, const char *value) @@ -386,6 +340,16 @@ int check_repository_format_version(const char *var, const char *value)  		repository_format_version = git_config_int(var, value);  	else if (strcmp(var, "core.sharedrepository") == 0)  		shared_repository = git_config_perm(var, value); +	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; +	}  	return 0;  } @@ -402,5 +366,16 @@ const char *setup_git_directory(void)  {  	const char *retval = setup_git_directory_gently(NULL);  	check_repository_format(); + +	/* 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; +	} +  	return retval;  } | 
