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);