This variable is intended to support multiple working directories attached to a repository. Such a repository may have a main working directory, created by either "git init" or "git clone" and one or more linked working directories. These working directories and the main repository share the same repository directory. In linked working directories, $GIT_COMMON_DIR must be defined to point to the real repository directory and $GIT_DIR points to an unused subdirectory inside $GIT_COMMON_DIR. File locations inside the repository are reorganized from the linked worktree view point: - worktree-specific such as HEAD, logs/HEAD, index, other top-level refs and unrecognized files are from $GIT_DIR. - the rest like objects, refs, info, hooks, packed-refs, shallow... are from $GIT_COMMON_DIR Scripts are supposed to retrieve paths in $GIT_DIR with "git rev-parse --git-path", which will take care of "$GIT_DIR vs $GIT_COMMON_DIR" business. Note that logs/refs/.tmp-renamed-log is used to prepare new reflog entry and it's supposed to be on the same filesystem as the target reflog file. This is not guaranteed true for logs/HEAD when it's mapped to repos/xx/logs/HEAD because the user can put "repos" directory on different filesystem. Don't mess with .git unless you know what you're doing. The redirection is done by git_path(), git_pathdup() and strbuf_git_path(). The selected list of paths goes to $GIT_COMMON_DIR, not the other way around in case a developer adds a new worktree-specific file and it's accidentally promoted to be shared across repositories (this includes unknown files added by third party commands) The list of known files that belong to $GIT_DIR are: ADD_EDIT.patch BISECT_ANCESTORS_OK BISECT_EXPECTED_REV BISECT_LOG BISECT_NAMES CHERRY_PICK_HEAD COMMIT_MSG FETCH_HEAD HEAD MERGE_HEAD MERGE_MODE MERGE_RR NOTES_EDITMSG NOTES_MERGE_WORKTREE ORIG_HEAD REVERT_HEAD SQUASH_MSG TAG_EDITMSG fast_import_crash_* logs/HEAD next-index-* rebase-apply rebase-merge rsync-refs-* sequencer/* shallow_* Path mapping is NOT done for git_path_submodule(). Multi-checkouts are not supported as submodules. Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@xxxxxxxxx> --- Documentation/git.txt | 8 ++++++++ cache.h | 4 +++- environment.c | 19 +++++++++++++++---- path.c | 28 ++++++++++++++++++++++++++++ t/t0060-path-utils.sh | 15 +++++++++++++++ 5 files changed, 69 insertions(+), 5 deletions(-) diff --git a/Documentation/git.txt b/Documentation/git.txt index 02bbc08..2c4a055 100644 --- a/Documentation/git.txt +++ b/Documentation/git.txt @@ -773,6 +773,14 @@ Git so take care if using Cogito etc. an explicit repository directory set via 'GIT_DIR' or on the command line. +'GIT_COMMON_DIR':: + If this variable is set to a path, non-worktree files that are + normally in $GIT_DIR will be taken from this path + instead. Worktree-specific files such as HEAD or index are + taken from $GIT_DIR. This variable has lower precedence than + other path variables such as GIT_INDEX_FILE, + GIT_OBJECT_DIRECTORY... + Git Commits ~~~~~~~~~~~ 'GIT_AUTHOR_NAME':: diff --git a/cache.h b/cache.h index 6c08e4a..51ade32 100644 --- a/cache.h +++ b/cache.h @@ -347,6 +347,7 @@ static inline enum object_type object_type(unsigned int mode) /* Double-check local_repo_env below if you add to this list. */ #define GIT_DIR_ENVIRONMENT "GIT_DIR" +#define GIT_COMMON_DIR_ENVIRONMENT "GIT_COMMON_DIR" #define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE" #define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE" #define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX" @@ -400,6 +401,7 @@ extern int is_inside_git_dir(void); extern char *git_work_tree_cfg; extern int is_inside_work_tree(void); extern const char *get_git_dir(void); +extern const char *get_git_common_dir(void); extern int is_git_directory(const char *path); extern char *get_object_directory(void); extern char *get_index_file(void); @@ -585,7 +587,7 @@ extern int fsync_object_files; extern int core_preload_index; extern int core_apply_sparse_checkout; extern int precomposed_unicode; -extern int git_db_env, git_index_env, git_graft_env; +extern int git_db_env, git_index_env, git_graft_env, git_common_dir_env; /* * The character that begins a commented line in user-editable file diff --git a/environment.c b/environment.c index f513479..c998120 100644 --- a/environment.c +++ b/environment.c @@ -80,9 +80,9 @@ static char *work_tree; static const char *namespace; static size_t namespace_len; -static const char *git_dir; +static const char *git_dir, *git_common_dir; static char *git_object_dir, *git_index_file, *git_graft_file; -int git_db_env, git_index_env, git_graft_env; +int git_db_env, git_index_env, git_graft_env, git_common_dir_env; /* * Repository-local GIT_* environment variables; see cache.h for details. @@ -134,10 +134,16 @@ static void setup_git_env(void) git_dir = DEFAULT_GIT_DIR_ENVIRONMENT; gitfile = read_gitfile(git_dir); git_dir = xstrdup(gitfile ? gitfile : git_dir); + git_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT); + if (git_common_dir) { + git_common_dir_env = 1; + git_common_dir = xstrdup(git_common_dir); + } else + git_common_dir = git_dir; 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 = xmalloc(strlen(git_common_dir) + 9); + sprintf(git_object_dir, "%s/objects", git_common_dir); } else git_db_env = 1; git_index_file = getenv(INDEX_ENVIRONMENT); @@ -173,6 +179,11 @@ const char *get_git_dir(void) return git_dir; } +const char *get_git_common_dir(void) +{ + return git_common_dir; +} + const char *get_git_namespace(void) { if (!namespace) diff --git a/path.c b/path.c index 0f8c3dc..2d757dc 100644 --- a/path.c +++ b/path.c @@ -90,6 +90,32 @@ static void replace_dir(struct strbuf *buf, int len, const char *newdir) buf->buf[newlen] = '/'; } +static void update_common_dir(struct strbuf *buf, int git_dir_len) +{ + const char *common_dir_list[] = { + "branches", "hooks", "info", "logs", "lost-found", "modules", + "objects", "refs", "remotes", "rr-cache", "svn", + NULL + }; + const char *common_top_file_list[] = { + "config", "gc.pid", "packed-refs", "shallow", NULL + }; + char *base = buf->buf + git_dir_len; + const char **p; + if (is_dir_file(base, "logs", "HEAD")) + return; /* keep this in $GIT_DIR */ + for (p = common_dir_list; *p; p++) + if (dir_prefix(base, *p)) { + replace_dir(buf, git_dir_len, get_git_common_dir()); + return; + } + for (p = common_top_file_list; *p; p++) + if (!strcmp(base, *p)) { + replace_dir(buf, git_dir_len, get_git_common_dir()); + return; + } +} + static void adjust_git_path(struct strbuf *buf, int git_dir_len) { const char *base = buf->buf + git_dir_len; @@ -101,6 +127,8 @@ static void adjust_git_path(struct strbuf *buf, int git_dir_len) get_index_file(), strlen(get_index_file())); else if (git_db_env && dir_prefix(base, "objects")) replace_dir(buf, git_dir_len + 7, get_object_directory()); + else if (git_common_dir_env) + update_common_dir(buf, git_dir_len); } static void do_git_path(struct strbuf *buf, const char *fmt, va_list args) diff --git a/t/t0060-path-utils.sh b/t/t0060-path-utils.sh index 1d29901..f9a77e4 100755 --- a/t/t0060-path-utils.sh +++ b/t/t0060-path-utils.sh @@ -241,5 +241,20 @@ test_expect_success 'setup fake objects directory foo' 'mkdir foo' test_git_path GIT_OBJECT_DIRECTORY=foo objects foo test_git_path GIT_OBJECT_DIRECTORY=foo objects/foo foo/foo test_git_path GIT_OBJECT_DIRECTORY=foo objects2 .git/objects2 +test_expect_success 'setup common repository' 'git --git-dir=bar init' +test_git_path GIT_COMMON_DIR=bar index .git/index +test_git_path GIT_COMMON_DIR=bar HEAD .git/HEAD +test_git_path GIT_COMMON_DIR=bar logs/HEAD .git/logs/HEAD +test_git_path GIT_COMMON_DIR=bar objects bar/objects +test_git_path GIT_COMMON_DIR=bar objects/bar bar/objects/bar +test_git_path GIT_COMMON_DIR=bar info/exclude bar/info/exclude +test_git_path GIT_COMMON_DIR=bar remotes/bar bar/remotes/bar +test_git_path GIT_COMMON_DIR=bar branches/bar bar/branches/bar +test_git_path GIT_COMMON_DIR=bar logs/refs/heads/master bar/logs/refs/heads/master +test_git_path GIT_COMMON_DIR=bar refs/heads/master bar/refs/heads/master +test_git_path GIT_COMMON_DIR=bar hooks/me bar/hooks/me +test_git_path GIT_COMMON_DIR=bar config bar/config +test_git_path GIT_COMMON_DIR=bar packed-refs bar/packed-refs +test_git_path GIT_COMMON_DIR=bar shallow bar/shallow test_done -- 1.8.5.2.240.g8478abd -- 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