@ -1,4 +1,8 @@
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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;
}