git/list[1] front-page[2] threads[3] people[4] search[5] about
 

Re: Global .git directory

From
GWGerhard Wiesinger <lists@wiesinger.com>
Date
Apr 27, 2010, 20:06 UTC
Message-ID
<alpine.LFD.2.00.1004272144470.11216@bbs.intern>
In-Reply-To
<201004271159.34071.trast@student.ethz.ch>
On Tue, 27 Apr 2010, Thomas Rast wrote:
Show 12 quoted lines
> 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);
Previous: Thomas RastNext: Erik Faye-Lund
Message 3 of 17 in “Global .git directory”
  1. Gerhard WiesingerApr 27, 2010
  2. Thomas RastApr 27, 2010
  3. Gerhard WiesingerApr 27, 2010
  4. Erik Faye-LundApr 27, 2010
  5. Gerhard WiesingerApr 28, 2010
  6. Tomas CarneckyApr 28, 2010
  7. Gerhard WiesingerApr 28, 2010
  8. Alex RiesenApr 28, 2010
  9. Gerhard WiesingerApr 28, 2010
  10. Erik Faye-LundApr 28, 2010
  11. Gerhard WiesingerApr 28, 2010
  12. Gerhard WiesingerMay 4, 2010
  13. Andrew RuderMay 4, 2010
  14. Andreas EricssonMay 4, 2010
  15. Gerhard WiesingerMay 4, 2010
  16. Junio C HamanoMay 4, 2010
  17. Jacob HelwigApr 27, 2010

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.