From a38bf926edcba780445ae2a67eb8fe33b266a94f Mon Sep 17 00:00:00 2001 From: Marius Storm-Olsen Date: Thu, 28 Feb 2008 13:22:42 +0100 Subject: [PATCH] Add cross-platform workdir support --- builtin-init-db.c | 53 ++++++++++++++++++++++++++++++++++++++----- builtin-rev-parse.c | 19 +++++++++++++++ cache.h | 2 + environment.c | 63 +++++++++++++++++++++++++++++++++++++++++++++----- path.c | 37 +++++++++++++++++++++++++++-- setup.c | 2 +- 6 files changed, 159 insertions(+), 17 deletions(-) diff --git a/builtin-init-db.c b/builtin-init-db.c index 79eaf8d..94622f9 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -310,7 +310,7 @@ static void guess_repository_type(const char *git_dir) } static const char init_db_usage[] = -"git-init [-q | --quiet] [--template=] [--shared]"; +"git-init [-q | --quiet] [--template=] [--shared] [--workdir-for=]"; /* * If you want to, you can share the DB area with any number of branches. @@ -323,8 +323,9 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) const char *git_dir; const char *sha1_dir; const char *template_dir = NULL; + char *repo_dir = NULL; char *path; - int len, i, reinit; + int len, i, reinit, workdir_fd; int quiet = 0; for (i = 1; i < argc; i++, argv++) { @@ -337,7 +338,10 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) shared_repository = git_config_perm("arg", arg+9); else if (!strcmp(arg, "-q") || !strcmp(arg, "--quiet")) quiet = 1; - else + else if (!prefixcmp(arg, "--workdir-for=")) { + repo_dir = xmalloc(PATH_MAX); + strcpy(repo_dir, arg+14); + } else usage(init_db_usage); } @@ -406,11 +410,48 @@ int cmd_init_db(int argc, const char **argv, const char *prefix) git_config_set("receive.denyNonFastforwards", "true"); } - if (!quiet) - printf("%s%s Git repository in %s/\n", + if (repo_dir) { + len = strlen(git_dir); + memcpy(path, git_dir, len); + strcpy(path+len, "/workdir_for"); + len = strlen(repo_dir); +#ifdef __MINGW32__ + for(i = 0; i < len; ++i) { + if (repo_dir[i] == '\\') + repo_dir[i] = '/'; + } +#endif + if (!is_git_directory(repo_dir)) { + strcpy(repo_dir+len, "/.git"); + if (!is_git_directory(repo_dir)) + die("specified --workdir-for path is not a git repository (%s)", repo_dir); + } + workdir_fd = open(path, O_CREAT | O_EXCL | O_WRONLY, 0666); + if (!workdir_fd) + die("couldn't create workdir redirection file: %s", strerror(errno)); + write_or_die(workdir_fd, repo_dir, strlen(repo_dir)); + if (close(workdir_fd) < 0) + die("closing file %s: %s", path, strerror(errno)); + + /* Copy current HEAD */ + strcpy(repo_dir+len, "/HEAD"); + len = strlen(git_dir); + strcpy(path+len, "/HEAD"); + copy_file(path, repo_dir, 0); + + /* Should force a checkout of the current HEAD. Oh well.. */ + } + + if (!quiet) { + printf("%s%s %s repository in %s/\n", reinit ? "Reinitialized existing" : "Initialized empty", shared_repository ? " shared" : "", + repo_dir ? "workdir" : "Git", git_dir); - + if (repo_dir) + printf("You now either need to do a 'git reset --hard', or force a switch to another branch.\n"); + } + if (repo_dir) + free(repo_dir); return 0; } diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c index 90dbb9d..d83ba59 100644 --- a/builtin-rev-parse.c +++ b/builtin-rev-parse.c @@ -511,6 +511,25 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) printf("%s/.git\n", cwd); continue; } + if (!strcmp(arg, "--git-redirection-dir")) { + const char *redir = get_git_redirection_dir(); + printf("%s\n", redir ? redir + : ""); + continue; + } + if (!strcmp(arg, "--git-object-dir")) { + printf("%s\n", get_object_directory()); + continue; + } + if (!strcmp(arg, "--git-refs-dir")) { + printf("%s\n", get_refs_directory()); + continue; + } + if (!strcmp(arg, "--uses-git-redirection")) { + printf("%s\n", get_git_redirection_dir() ? "true" + : "false"); + continue; + } if (!strcmp(arg, "--is-inside-git-dir")) { printf("%s\n", is_inside_git_dir() ? "true" : "false"); diff --git a/cache.h b/cache.h index 660ea04..e8b6879 100644 --- a/cache.h +++ b/cache.h @@ -301,6 +301,7 @@ static inline enum object_type object_type(unsigned int mode) extern int is_bare_repository_cfg; extern int is_bare_repository(void); extern int is_inside_git_dir(void); +extern int is_git_directory(const char *suspect); extern char *git_work_tree_cfg; extern int is_inside_work_tree(void); extern const char *get_git_dir(void); @@ -310,6 +311,7 @@ extern char *get_index_file(void); extern char *get_graft_file(void); extern int set_git_dir(const char *path); extern const char *get_git_work_tree(void); +extern const char *get_git_redirection_dir(void); #define ALTERNATE_DB_ENVIRONMENT "GIT_ALTERNATE_OBJECT_DIRECTORIES" diff --git a/environment.c b/environment.c index 6739a3f..74391de 100644 --- a/environment.c +++ b/environment.c @@ -44,7 +44,7 @@ char *git_work_tree_cfg; static const char *work_tree; static const char *git_dir; -static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file; +static char *git_object_dir, *git_index_file, *git_refs_dir, *git_graft_file, *git_workdir_redirection; static void setup_git_env(void) { @@ -53,15 +53,12 @@ static void setup_git_env(void) git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; git_object_dir = getenv(DB_ENVIRONMENT); if (!git_object_dir) { - git_object_dir = xmalloc(strlen(git_dir) + 9); - sprintf(git_object_dir, "%s/objects", git_dir); + git_object_dir = xstrdup(git_path("objects")); } - git_refs_dir = xmalloc(strlen(git_dir) + 6); - sprintf(git_refs_dir, "%s/refs", git_dir); + git_refs_dir = xstrdup(git_path("refs")); git_index_file = getenv(INDEX_ENVIRONMENT); if (!git_index_file) { - git_index_file = xmalloc(strlen(git_dir) + 7); - sprintf(git_index_file, "%s/index", git_dir); + git_index_file = xstrdup(git_path("index")); } git_graft_file = getenv(GRAFT_ENVIRONMENT); if (!git_graft_file) @@ -101,6 +98,58 @@ const char *get_git_work_tree(void) return work_tree; } +static char *workdir_for = "/workdir_for"; +const char *get_git_redirection_dir(void) +{ + static int checked_for_redirection = 0; + if (!checked_for_redirection && git_dir) { + char *buf; + struct stat st; + int fd; + size_t len; + int gitdirlen = strlen(git_dir); + int workdirlen = strlen(workdir_for); + + checked_for_redirection = 1; + + /* Check if we have a .git/workdir_for redirection file */ + buf = xmalloc(gitdirlen + workdirlen + 1); + memcpy(buf, git_dir, gitdirlen); + memcpy(buf + gitdirlen, workdir_for, strlen(workdir_for)); + buf[gitdirlen + workdirlen] = '\0'; + + if (stat(buf, &st) || !S_ISREG(st.st_mode)) { + free(buf); + return 0; + } + + /* Read the 1st line of the file */ + fd = open(buf, O_RDONLY); + if (fd < 0) { + fprintf(stderr, "Error opening %s: %s", buf, strerror(errno)); + free(buf); + return 0; + } + buf = xrealloc(buf, st.st_size + 1); + len = read_in_full(fd, buf, st.st_size); + close(fd); + buf[len] = '\0'; + + if (len != st.st_size) + fprintf(stderr, "Error reading .git%s", workdir_for); + + if (len < 4 || !is_git_directory(buf)) { + fprintf(stderr, ".git%s pointing to a non-proper git repo path '%s'\n", workdir_for, buf); + free(buf); + return 0; + } + + git_workdir_redirection = buf; + /* buf not freed, since redirected_gitpath now points to it */ + } + return git_workdir_redirection; +} + char *get_object_directory(void) { if (!git_object_dir) diff --git a/path.c b/path.c index 4260952..f6332d6 100644 --- a/path.c +++ b/path.c @@ -46,12 +46,28 @@ char *mkpath(const char *fmt, ...) return cleanup_path(pathname); } +static char *redirection_paths[] = { "config", "refs" ,"logs/refs", "objects", "info", "hooks", "packed-refs", "remotes", "rr-cache", 0 }; +static const char *get_redirected_dir(const char *req_path) +{ + int i = 0; + const char *redir = get_git_redirection_dir(); + if (redir) { + do { + if (!memcmp(req_path, redirection_paths[i], strlen(redirection_paths[i]))) { + /* fprintf(stderr, "Looking for '%s', returning in '%s' instead of workdir/.git '%s'\n", req_path, redir, get_git_dir()); */ + return redir; + } + } while(redirection_paths[++i]); + } + return 0; +} + char *git_path(const char *fmt, ...) { const char *git_dir = get_git_dir(); char *pathname = get_pathname(); va_list args; - unsigned len; + unsigned len, newlen, rdlen, minlen; len = strlen(git_dir); if (len > PATH_MAX-100) @@ -60,10 +76,25 @@ char *git_path(const char *fmt, ...) if (len && git_dir[len-1] != '/') pathname[len++] = '/'; va_start(args, fmt); - len += vsnprintf(pathname + len, PATH_MAX - len, fmt, args); + newlen = len + vsnprintf(pathname + len, PATH_MAX - len, fmt, args); va_end(args); - if (len >= PATH_MAX) + if (newlen >= PATH_MAX) return bad_path; + + { /* Redirect certain files when in a workdir */ + const char *redir = get_redirected_dir(pathname + len); + if (redir) { + char *pathname_redir = get_pathname(); + rdlen = strlen(redir); + memcpy(pathname_redir, redir, rdlen); + if (rdlen && redir[rdlen-1] != '/') + pathname_redir[rdlen++] = '/'; + minlen = MIN(PATH_MAX-rdlen, strlen(pathname+len)); + memcpy(pathname_redir+rdlen, pathname+len, minlen); + pathname_redir[rdlen + minlen] = '\0'; + pathname = pathname_redir; + } + } return cleanup_path(pathname); } diff --git a/setup.c b/setup.c index 89c81e5..2c507d5 100644 --- a/setup.c +++ b/setup.c @@ -221,7 +221,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); -- 1.5.4.3.394.ga38b