Re: Global .git directory

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

 



On Tue, 27 Apr 2010, Thomas Rast wrote:

Gerhard Wiesinger wrote:
Hello,

I'm new to git and as far as I saw that feature is missing: Placing .git
directory/repository on a central place for many git repositories. This
feature ensures that data directories are kept clean from metadata of git.
[...]
I think this should be easy to implement on a centralized point.

What problem are you trying to solve, what solution are you
attempting, and why does the .git get in the way of your proposed
solution?

I don't like metadata of the repository in MY file structure (e.g. .git in top directory, .svn in every directory in subversion, .hg in top directory) for several reasons: 1.) Searching might arise false results, longer output and takes longer (e.g. grep -ir string .) 2.) Making tarballs, diffs, etc.: One has to make ugly --exclude or some other workarounds when directories are traversed. 3.) From an archtectural point of view: It is not technically necessary to mix up user data with repository data.

(In other words, please convince us that this is actually worth
having.)

Ok, rather than convincing I've written a (fast) patch which works for me :-)

Any comments? Any (further) changes needed? Will you include it in main git?

Thnx.

Ciao,
Gerhard

--
http://www.wiesinger.com/
diff --git a/builtin/init-db.c b/builtin/init-db.c
index edc40ff..a91e762 100644
--- a/builtin/init-db.c
+++ b/builtin/init-db.c
@@ -7,6 +7,7 @@
 #include "builtin.h"
 #include "exec_cmd.h"
 #include "parse-options.h"
+#include "environment.h"
 
 #ifndef DEFAULT_GIT_TEMPLATE_DIR
 #define DEFAULT_GIT_TEMPLATE_DIR "/usr/share/git-core/templates"
@@ -34,6 +35,27 @@ static void safe_create_dir(const char *dir, int share)
 		die("Could not make %s writable by group", dir);
 }
 
+static void mkdir_recusive(char *directory)
+{
+	char tmp[PATH_MAX+1];
+	char *p = NULL;
+	size_t len;
+
+	snprintf(tmp, sizeof(tmp), "%s", directory);
+	len = strlen(tmp);
+	if(is_dir_sep(tmp[len - 1]))
+		tmp[len - 1] = 0;
+
+	for(p = tmp + 1; *p; p++)
+		if(is_dir_sep(*p)) {
+			char c = *p;
+			*p = 0;
+			safe_create_dir(tmp, 1);
+			*p = c;
+		}
+	safe_create_dir(tmp,1);
+}
+
 static void copy_templates_1(char *path, int baselen,
 			     char *template, int template_baselen,
 			     DIR *dir)
@@ -473,7 +495,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
 	 * GIT_WORK_TREE makes sense only in conjunction with GIT_DIR
 	 * without --bare.  Catch the error early.
 	 */
-	git_dir = getenv(GIT_DIR_ENVIRONMENT);
+	git_dir = get_git_dir_from_environment();
 	if ((!git_dir || is_bare_repository_cfg == 1)
 	    && getenv(GIT_WORK_TREE_ENVIRONMENT))
 		die("%s (or --work-tree=<directory>) not allowed without "
@@ -490,7 +512,19 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)
 	if (is_bare_repository_cfg < 0)
 		is_bare_repository_cfg = guess_repository_type(git_dir);
 
-	if (!is_bare_repository_cfg) {
+	if (is_git_dir_global_environment()) {
+		char *global_base_dir = get_git_dir_global_base_dir();
+		mkdir_recusive(global_base_dir);
+		if (!git_work_tree_cfg) {
+			git_work_tree_cfg = xcalloc(PATH_MAX, 1);
+			strncpy(git_work_tree_cfg, global_base_dir, PATH_MAX);
+		}
+		if (access(get_git_work_tree(), X_OK))
+			die_errno ("Cannot access work tree '%s'",
+				   get_git_work_tree());
+	}
+		
+	if (!is_bare_repository_cfg && !is_git_dir_global_environment()) {
 		if (git_dir) {
 			const char *git_dir_parent = strrchr(git_dir, '/');
 			if (git_dir_parent) {
diff --git a/builtin/rev-parse.c b/builtin/rev-parse.c
index 8fbf9d0..b64260f 100644
--- a/builtin/rev-parse.c
+++ b/builtin/rev-parse.c
@@ -9,6 +9,7 @@
 #include "quote.h"
 #include "builtin.h"
 #include "parse-options.h"
+#include "environment.h"
 
 #define DO_REVS		1
 #define DO_NOREV	2
@@ -642,7 +643,7 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 				continue;
 			}
 			if (!strcmp(arg, "--git-dir")) {
-				const char *gitdir = getenv(GIT_DIR_ENVIRONMENT);
+				const char *gitdir = get_git_dir_from_environment();
 				static char cwd[PATH_MAX];
 				int len;
 				if (gitdir) {
diff --git a/cache.h b/cache.h
index 5eb0573..57a647d 100644
--- a/cache.h
+++ b/cache.h
@@ -372,6 +372,8 @@ static inline enum object_type object_type(unsigned int mode)
 }
 
 #define GIT_DIR_ENVIRONMENT "GIT_DIR"
+#define GIT_DIR_GLOBAL_ENVIRONMENT "GIT_DIR_GLOBAL"
+#define GIT_DIR_GLOBAL_SPECIAL_ROOT_DIRECTORY "/SPECIAL_ROOT_DIRECTORY"
 #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
 #define DEFAULT_GIT_DIR_ENVIRONMENT ".git"
 #define DB_ENVIRONMENT "GIT_OBJECT_DIRECTORY"
diff --git a/environment.c b/environment.c
index 876c5e5..d9768a9 100644
--- a/environment.c
+++ b/environment.c
@@ -8,6 +8,9 @@
  * are.
  */
 #include "cache.h"
+#include "setup.h"
+
+#define DIR_SEPARATOR "/"
 
 char git_default_email[MAX_GITNAME];
 char git_default_name[MAX_GITNAME];
@@ -80,9 +83,129 @@ const char * const local_repo_env[LOCAL_REPO_ENV_SIZE + 1] = {
 	NULL
 };
 
+char* path_shorten(char *path)
+{
+	int index = strlen(path);
+
+	while (index > 0) {
+		index--;
+		if (is_dir_sep(path[index])) {
+			path[index] = 0;
+			return path;
+		}
+	}
+
+	// should never get here
+	return "";
+}
+
+static char *get_dir_global_environment(void)
+{
+	return getenv(GIT_DIR_GLOBAL_ENVIRONMENT);
+}
+
+char *get_git_dir_global_base_dir(void)
+{
+	static char cwd_buf[PATH_MAX+1];
+	static char dir_global_buf[PATH_MAX+1];
+	static char git_dir_buf[PATH_MAX+1];
+	int global_environment_len = 0;
+	char *git_dir_global_environment = get_dir_global_environment();
+
+	if (!getcwd(cwd_buf, sizeof(cwd_buf)-1))
+		die_errno("Unable to read current working directory");
+
+	if (!is_absolute_path(git_dir_global_environment))
+		die_errno("Global environment variable" GIT_DIR_GLOBAL_ENVIRONMENT " needs absolute path!");
+
+	strncpy(dir_global_buf, git_dir_global_environment, sizeof(dir_global_buf));
+	global_environment_len = strlen(dir_global_buf);
+	if (is_dir_sep(dir_global_buf[global_environment_len]))
+		dir_global_buf[global_environment_len] = 0;
+
+	return mksnpath(git_dir_buf, sizeof(git_dir_buf), "%s%s", dir_global_buf, cwd_buf);
+}
+
+char *get_git_dir_from_global_environment(char *git_dir_global_environment)
+{
+	static char cwd_buf[PATH_MAX+1];
+	static char cwd_original_buf[PATH_MAX+1];
+	static char dir_global_buf[PATH_MAX+1];
+	static char git_dir_buf[PATH_MAX+1];
+	char *cwd = cwd_buf;
+	char *git_dir = git_dir_buf;
+	int found = 0;
+	int global_environment_len = 0;
+
+	if (!getcwd(cwd_buf, sizeof(cwd_buf)-1))
+		die_errno("Unable to read current working directory");
+
+	if (!is_absolute_path(git_dir_global_environment))
+		die_errno("Global environment variable" GIT_DIR_GLOBAL_ENVIRONMENT " needs absolute path!");
+
+	strncpy(dir_global_buf, git_dir_global_environment, sizeof(dir_global_buf));
+	global_environment_len = strlen(dir_global_buf);
+	if (is_dir_sep(dir_global_buf[global_environment_len]))
+		dir_global_buf[global_environment_len] = 0;
+
+	if (strlen(cwd) == 1 && is_dir_sep(cwd[0]))
+	{
+		// special case for / git repository
+		git_dir = mksnpath(git_dir_buf, sizeof(git_dir_buf), "%s%s" DIR_SEPARATOR "%s", dir_global_buf, GIT_DIR_GLOBAL_SPECIAL_ROOT_DIRECTORY, DEFAULT_GIT_DIR_ENVIRONMENT);
+		return git_dir;
+	}
+
+	strncpy(cwd_original_buf, cwd, sizeof(cwd_original_buf));
+
+	/*
+	 * Test in the following order (relative to the cwd):
+	 * - .git (file containing "gitdir: <path>")
+	 * - .git/
+	 * - ./ (bare)
+	 * - ../.git
+	 * - ../.git/
+	 * - ../ (bare)
+	 * - ../../.git/
+	 *   etc.
+	 */
+	for (;;) {
+		if (*cwd == '\0') {
+			break;
+		}
+
+		git_dir = mksnpath(git_dir_buf, sizeof(git_dir_buf), "%s%s" DIR_SEPARATOR "%s", dir_global_buf, cwd, DEFAULT_GIT_DIR_ENVIRONMENT);
+		if (is_git_directory(git_dir)) {
+			found = 1;
+			break;
+		}
+
+		cwd = path_shorten(cwd);
+	}
+
+	if (!found) {
+		git_dir = mksnpath(git_dir_buf, sizeof(git_dir_buf), "%s%s/%s", dir_global_buf, cwd_original_buf, DEFAULT_GIT_DIR_ENVIRONMENT);
+		return git_dir;
+	}
+
+	return git_dir;
+}
+
+int is_git_dir_global_environment(void)
+{
+	return (get_dir_global_environment() != NULL);
+}
+
+char *get_git_dir_from_environment(void)
+{
+	char *git_dir_global_environment = get_dir_global_environment();
+	if (git_dir_global_environment) return get_git_dir_from_global_environment(git_dir_global_environment);
+
+	return getenv(GIT_DIR_ENVIRONMENT);
+}
+
 static void setup_git_env(void)
 {
-	git_dir = getenv(GIT_DIR_ENVIRONMENT);
+	git_dir = get_git_dir_from_environment();
 	if (!git_dir)
 		git_dir = read_gitfile_gently(DEFAULT_GIT_DIR_ENVIRONMENT);
 	if (!git_dir)
diff --git a/setup.c b/setup.c
index 5716d90..6ee3a59 100644
--- a/setup.c
+++ b/setup.c
@@ -1,5 +1,6 @@
 #include "cache.h"
 #include "dir.h"
+#include "environment.h"
 
 static int inside_git_dir = -1;
 static int inside_work_tree = -1;
@@ -165,7 +166,7 @@ const char **get_pathspec(const char *prefix, const char **pathspec)
  *    a proper "ref:", or a regular file HEAD that has a properly
  *    formatted sha1 object name.
  */
-static int is_git_directory(const char *suspect)
+int is_git_directory(const char *suspect)
 {
 	char path[PATH_MAX];
 	size_t len = strlen(suspect);
@@ -337,7 +338,7 @@ const char *setup_git_directory_gently(int *nongit_ok)
 	 * to do any discovery, but we still do repository
 	 * validation.
 	 */
-	gitdirenv = getenv(GIT_DIR_ENVIRONMENT);
+	gitdirenv = get_git_dir_from_environment();
 	if (gitdirenv) {
 		if (PATH_MAX - 40 < strlen(gitdirenv))
 			die("'$%s' too big", GIT_DIR_ENVIRONMENT);

[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]