Re: [PATCH] git-init: set core.workdir when GIT_WORK_DIR is specified

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Matthias Lederhofer <matled@xxxxxxx> writes:

> git init will die with an error message before doing anything if the
> value of GIT_WORK_DIR is no valid directory.  GIT_WORK_DIR is also
> expanded to an absolute path for the config file and is shown to the
> user (core.workdir = <path>).

No other configuration variable that is automatically set gives
such an unwarranted noise to the standard output.

Applied with printf() removed.  Will cook further in 'pu', as I
am seeing some questionable things that are queued in there
already.

diff --git a/cache.h b/cache.h
index a4762ed..3bacc46 100644
--- a/cache.h
+++ b/cache.h
@@ -144,6 +144,7 @@ enum object_type {
 };
 
 #define GIT_DIR_ENVIRONMENT "GIT_DIR"
+#define GIT_WORKING_DIR_ENVIRONMENT "GIT_WORK_DIR"
 #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
 #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
 #define INDEX_ENVIRONMENT "GIT_INDEX_FILE"
@@ -164,6 +165,7 @@ extern char *get_graft_file(void);
 
 #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES"
 
+extern int has_working_directory;
 extern const char **get_pathspec(const char *prefix, const char **pathspec);
 extern const char *setup_git_directory_gently(int *);
 extern const char *setup_git_directory(void);

I think "has_working_directory" and "GIT_WORK_DIR" are a bit
confusing, as everybody knows what a working directory is, and
it is returned by `pwd`.  But that is obviously not what you
mean by these words.

We seem to use the word "working tree" in our documentation to
differenciate the concept of "directory that corresponds to the
toplevel of the tree structure represented by the index and a
tree object" from the UNIXy concept of the "current working
directory".

No matter what that concept will end up being called, has_XXX
does not sound right either.  Everybody has the working tree;
the new mechanism is about having the working tree in unusual
location, different from the conventional "attached directly
above the repository" layout.  Perhaps "separate_working_tree"?


diff --git a/environment.c b/environment.c
index 0151ad0..7bf6a87 100644
--- a/environment.c
+++ b/environment.c
@@ -59,8 +59,15 @@ static void setup_git_env(void)
 int is_bare_repository(void)
 {
 	const char *dir, *s;
-	if (0 <= is_bare_repository_cfg)
-		return is_bare_repository_cfg;
+	/* definitely bare */
+	if (is_bare_repository_cfg == 1)
+		return 1;
+	/* GIT_WORK_DIR is set, bare if cwd is outside */
+	if (has_working_directory >= 0)
+		return !has_working_directory;
+	/* configuration says it is not bare */
+	if (is_bare_repository_cfg == 0)
+		return 0;

This feels convoluted.  Both of these variables stay -1
(uninitialized) when unknown, set to 0 if definite negative and
set to 1 if definite positive, so it is unclear why you are
checking only positive is_bare first, then the new setting and
then negative is_bare later.  Wouldn't it be easier to
understand if it were written like this?

	/* if configuration says so, then we obey. */
	if (0 <= is_bare_repository_cfg)
		return is_bare_repository_cfg;

	/*
         * if separate working-tree is specified, then we _do_
         * have the working tree and the repository is not bare.
         */
	if (0 <= separate_working_tree_cfg)
        	return !separate_working_tree_cfg;
 
... especially since you updated git-init so that you can
instruct it not to set core.bare by having working tree location
explicitly specified with an environment variable.

diff --git a/setup.c b/setup.c
index a45ea83..208124f 100644
--- a/setup.c
+++ b/setup.c
@@ -192,28 +192,143 @@ int is_inside_git_dir(void)
 	return inside_git_dir;
 }
 
+static int stat_git_work_dir(struct stat *st)
+{
+	char workdir[PATH_MAX], cwd[PATH_MAX];
+	const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
+	const char *gitwd = getenv(GIT_WORKING_DIR_ENVIRONMENT);
+
+	if (gitwd) {
+		if (!stat(gitwd, st))
+			return 1;
+		die("Unable to stat git working directory '%s'", gitwd);
+	}
+
+	/* get workdir from config */
+	workdir[0] = '\0';
+	git_work_dir = workdir;
+	git_config(git_workdir_config);
+	git_work_dir = NULL;
+	if (!workdir[0])
+		return 0;
+
+	/* relative path: change to gitdir for stat */
+	if (workdir[0] != '/') {
+		if (!getcwd(cwd, sizeof(cwd)) || cwd[0] != '/')
+			die("Unable to read current working directory");
+		if (chdir(gitdir))
+			die("Cannot change directory to '%s'", gitdir);
+	}
+
+	if (stat(workdir, st))
+		die("Unable to stat git working directory '%s'", workdir);
+
+	if (workdir[0] != '/' && chdir(cwd))
+		die("Cannot come back to cwd");
+
+	return 1;
+}

Blech.  stat_* that returns 1 for success, 0 for wtf (perhaps
meaning "n/a")?

I think naming this function "stat" is exposing too much
implementation detail.  Can the name describe _why_ the caller
may want to call this function, iow, to achieve what goal?  For
example, "check_separate_working_tree()" that returns false when
separate-working-tree feature is not used is easier to
understand, I would say.  And as a side effect of checking, you
would get stat info back that you can later use to dig into the
working tree, but that is an implementation detail.

+int has_working_directory = -1;
+
 const char *setup_git_directory_gently(int *nongit_ok)
 {
 	static char cwd[PATH_MAX+1];
 	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.
-	 */

Isn't this comment still half-valid?  We never do "discovery";
we however now allow the working tree to be specified explicitly
as an option.

 	gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
 	if (gitdirenv) {
+		struct stat st, st_work, st_git;
+		char *prefix;
+		char c;
+		int len;
+
 		if (PATH_MAX - 40 < strlen(gitdirenv))
 			die("'$%s' too big", GIT_DIR_ENVIRONMENT);
-		if (is_git_directory(gitdirenv))
-			return NULL;
-		if (nongit_ok) {
-			*nongit_ok = 1;
+		if (!is_git_directory(gitdirenv)) {
+			if (nongit_ok) {
+				*nongit_ok = 1;
+				return NULL;
+			}
+			die("Not a git repository: '%s'", gitdirenv);
+		}

... and this part is the "validation" part.

+
+		/* check for working directory */
+		if (!stat_git_work_dir(&st_work))
 			return NULL;

... and this return is "is the separate, explicit working tree
feature is in use?".  And the rest is the new codepath that can
be triggered with the optional stuff.

+		if (inside_git_dir == -1 && stat(gitdirenv, &st_git))
+			die("Unable to stat git directory");
+		if (!getcwd(cwd, sizeof(cwd)-1) || cwd[0] != '/')
+			die("Unable to read current working directory");
+		len = strlen(cwd);
+
+		prefix = cwd+len;
+		for (;;) {
+			c = *prefix;
+			*prefix = '\0';
+			if (stat(cwd, &st))
+				die("Unable to stat '%s'", cwd);
+			if (st_work.st_dev == st.st_dev &&
+			    st_work.st_ino == st.st_ino)
+				break;
+			if (inside_git_dir == -1 &&
+			    st_git.st_dev == st.st_dev &&
+			    st_git.st_ino == st.st_ino)
+				inside_git_dir = 1;
+			*prefix = c;
+
+			if (prefix == cwd+1) {
+				has_working_directory = 0;
+				return NULL;
+			}
+			while (*(--prefix) != '/')
+				; /* do nothing */
+			if (prefix == cwd)
+				prefix++;
 		}

Is it safe to assume that we can rely on st_dev/st_ino
comparison?

Do we really need to do this?  Unless symlinks and automounts
are involved, shouldn't git_work_dir be a substring of cwd and
in that case can't you simply find the prefix with prefixcmp()?
I know doing that would not let you test the "is this inside
git-dir" condition when GIT_DIR is relative path, but I suspect
the user deserves it if he is dissociating the working tree from
the usual repository location with the new mechanism, specify
GIT_DIR with relative path _and_ chdir around into GIT_DIR.  I
know you meant well, but the above feels rather scary code for
dubious benefit.

-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]