Skip to content

Commit c7b3a3d

Browse files
pcloudsgitster
authored andcommitted
$GIT_COMMON_DIR: a new environment variable
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 (except info/sparse-checkout, but that's a separate patch) 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. 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. Helped-by: Jens Lehmann <Jens.Lehmann@web.de> Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent af07b20 commit c7b3a3d

File tree

6 files changed

+117
-19
lines changed

6 files changed

+117
-19
lines changed

Documentation/git.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -809,6 +809,14 @@ Git so take care if using Cogito etc.
809809
an explicit repository directory set via 'GIT_DIR' or on the
810810
command line.
811811

812+
'GIT_COMMON_DIR'::
813+
If this variable is set to a path, non-worktree files that are
814+
normally in $GIT_DIR will be taken from this path
815+
instead. Worktree-specific files such as HEAD or index are
816+
taken from $GIT_DIR. See linkgit:gitrepository-layout[5] for
817+
details. This variable has lower precedence than other path
818+
variables such as GIT_INDEX_FILE, GIT_OBJECT_DIRECTORY...
819+
812820
Git Commits
813821
~~~~~~~~~~~
814822
'GIT_AUTHOR_NAME'::

Documentation/gitrepository-layout.txt

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@ of incomplete object store is not suitable to be published for
4646
use with dumb transports but otherwise is OK as long as
4747
`objects/info/alternates` points at the object stores it
4848
borrows from.
49+
+
50+
This directory is ignored if $GIT_COMMON_DIR is set and
51+
"$GIT_COMMON_DIR/objects" will be used instead.
4952

5053
objects/[0-9a-f][0-9a-f]::
5154
A newly created object is stored in its own file.
@@ -92,7 +95,8 @@ refs::
9295
References are stored in subdirectories of this
9396
directory. The 'git prune' command knows to preserve
9497
objects reachable from refs found in this directory and
95-
its subdirectories.
98+
its subdirectories. This directory is ignored if $GIT_COMMON_DIR
99+
is set and "$GIT_COMMON_DIR/refs" will be used instead.
96100

97101
refs/heads/`name`::
98102
records tip-of-the-tree commit objects of branch `name`
@@ -114,7 +118,8 @@ refs/replace/`<obj-sha1>`::
114118
packed-refs::
115119
records the same information as refs/heads/, refs/tags/,
116120
and friends record in a more efficient way. See
117-
linkgit:git-pack-refs[1].
121+
linkgit:git-pack-refs[1]. This file is ignored if $GIT_COMMON_DIR
122+
is set and "$GIT_COMMON_DIR/packed-refs" will be used instead.
118123

119124
HEAD::
120125
A symref (see glossary) to the `refs/heads/` namespace
@@ -133,14 +138,22 @@ being a symref to point at the current branch. Such a state
133138
is often called 'detached HEAD.' See linkgit:git-checkout[1]
134139
for details.
135140

141+
config::
142+
Repository specific configuration file. This file is ignored
143+
if $GIT_COMMON_DIR is set and "$GIT_COMMON_DIR/config" will be
144+
used instead.
145+
136146
branches::
137147
A slightly deprecated way to store shorthands to be used
138148
to specify a URL to 'git fetch', 'git pull' and 'git push'.
139149
A file can be stored as `branches/<name>` and then
140150
'name' can be given to these commands in place of
141151
'repository' argument. See the REMOTES section in
142152
linkgit:git-fetch[1] for details. This mechanism is legacy
143-
and not likely to be found in modern repositories.
153+
and not likely to be found in modern repositories. This
154+
directory is ignored if $GIT_COMMON_DIR is set and
155+
"$GIT_COMMON_DIR/branches" will be used instead.
156+
144157

145158
hooks::
146159
Hooks are customization scripts used by various Git
@@ -149,7 +162,9 @@ hooks::
149162
default. To enable, the `.sample` suffix has to be
150163
removed from the filename by renaming.
151164
Read linkgit:githooks[5] for more details about
152-
each hook.
165+
each hook. This directory is ignored if $GIT_COMMON_DIR is set
166+
and "$GIT_COMMON_DIR/hooks" will be used instead.
167+
153168

154169
index::
155170
The current index file for the repository. It is
@@ -161,7 +176,8 @@ sharedindex.<SHA-1>::
161176

162177
info::
163178
Additional information about the repository is recorded
164-
in this directory.
179+
in this directory. This directory is ignored if $GIT_COMMON_DIR
180+
is set and "$GIT_COMMON_DIR/index" will be used instead.
165181

166182
info/refs::
167183
This file helps dumb transports discover what refs are
@@ -201,12 +217,15 @@ remotes::
201217
when interacting with remote repositories via 'git fetch',
202218
'git pull' and 'git push' commands. See the REMOTES section
203219
in linkgit:git-fetch[1] for details. This mechanism is legacy
204-
and not likely to be found in modern repositories.
220+
and not likely to be found in modern repositories. This
221+
directory is ignored if $GIT_COMMON_DIR is set and
222+
"$GIT_COMMON_DIR/remotes" will be used instead.
205223

206224
logs::
207-
Records of changes made to refs are stored in this
208-
directory. See linkgit:git-update-ref[1]
209-
for more information.
225+
Records of changes made to refs are stored in this directory.
226+
See linkgit:git-update-ref[1] for more information. This
227+
directory is ignored if $GIT_COMMON_DIR is set and
228+
"$GIT_COMMON_DIR/logs" will be used instead.
210229

211230
logs/refs/heads/`name`::
212231
Records all changes made to the branch tip named `name`.
@@ -217,10 +236,14 @@ logs/refs/tags/`name`::
217236
shallow::
218237
This is similar to `info/grafts` but is internally used
219238
and maintained by shallow clone mechanism. See `--depth`
220-
option to linkgit:git-clone[1] and linkgit:git-fetch[1].
239+
option to linkgit:git-clone[1] and linkgit:git-fetch[1]. This
240+
file is ignored if $GIT_COMMON_DIR is set and
241+
"$GIT_COMMON_DIR/shallow" will be used instead.
221242

222243
modules::
223-
Contains the git-repositories of the submodules.
244+
Contains the git-repositories of the submodules. This
245+
directory is ignored if $GIT_COMMON_DIR is set and
246+
"$GIT_COMMON_DIR/modules" will be used instead.
224247

225248
SEE ALSO
226249
--------

cache.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,7 @@ static inline enum object_type object_type(unsigned int mode)
377377

378378
/* Double-check local_repo_env below if you add to this list. */
379379
#define GIT_DIR_ENVIRONMENT "GIT_DIR"
380+
#define GIT_COMMON_DIR_ENVIRONMENT "GIT_COMMON_DIR"
380381
#define GIT_NAMESPACE_ENVIRONMENT "GIT_NAMESPACE"
381382
#define GIT_WORK_TREE_ENVIRONMENT "GIT_WORK_TREE"
382383
#define GIT_PREFIX_ENVIRONMENT "GIT_PREFIX"
@@ -430,6 +431,7 @@ extern int is_inside_git_dir(void);
430431
extern char *git_work_tree_cfg;
431432
extern int is_inside_work_tree(void);
432433
extern const char *get_git_dir(void);
434+
extern const char *get_git_common_dir(void);
433435
extern int is_git_directory(const char *path);
434436
extern char *get_object_directory(void);
435437
extern char *get_index_file(void);
@@ -617,7 +619,7 @@ extern int fsync_object_files;
617619
extern int core_preload_index;
618620
extern int core_apply_sparse_checkout;
619621
extern int precomposed_unicode;
620-
extern int git_db_env, git_index_env, git_graft_env;
622+
extern int git_db_env, git_index_env, git_graft_env, git_common_dir_env;
621623

622624
/*
623625
* The character that begins a commented line in user-editable file

environment.c

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -81,9 +81,9 @@ static char *work_tree;
8181
static const char *namespace;
8282
static size_t namespace_len;
8383

84-
static const char *git_dir;
84+
static const char *git_dir, *git_common_dir;
8585
static char *git_object_dir, *git_index_file, *git_graft_file;
86-
int git_db_env, git_index_env, git_graft_env;
86+
int git_db_env, git_index_env, git_graft_env, git_common_dir_env;
8787

8888
/*
8989
* Repository-local GIT_* environment variables; see cache.h for details.
@@ -101,6 +101,7 @@ const char * const local_repo_env[] = {
101101
NO_REPLACE_OBJECTS_ENVIRONMENT,
102102
GIT_PREFIX_ENVIRONMENT,
103103
GIT_SHALLOW_FILE_ENVIRONMENT,
104+
GIT_COMMON_DIR_ENVIRONMENT,
104105
NULL
105106
};
106107

@@ -125,8 +126,8 @@ static char *expand_namespace(const char *raw_namespace)
125126
return strbuf_detach(&buf, NULL);
126127
}
127128

128-
static char *git_path_from_env(const char *envvar, const char *path,
129-
int *fromenv)
129+
static char *git_path_from_env(const char *envvar, const char *git_dir,
130+
const char *path, int *fromenv)
130131
{
131132
const char *value = getenv(envvar);
132133
if (!value) {
@@ -149,9 +150,18 @@ static void setup_git_env(void)
149150
git_dir = DEFAULT_GIT_DIR_ENVIRONMENT;
150151
gitfile = read_gitfile(git_dir);
151152
git_dir = xstrdup(gitfile ? gitfile : git_dir);
152-
git_object_dir = git_path_from_env(DB_ENVIRONMENT, "objects", &git_db_env);
153-
git_index_file = git_path_from_env(INDEX_ENVIRONMENT, "index", &git_index_env);
154-
git_graft_file = git_path_from_env(GRAFT_ENVIRONMENT, "info/grafts", &git_graft_env);
153+
git_common_dir = getenv(GIT_COMMON_DIR_ENVIRONMENT);
154+
if (git_common_dir) {
155+
git_common_dir_env = 1;
156+
git_common_dir = xstrdup(git_common_dir);
157+
} else
158+
git_common_dir = git_dir;
159+
git_object_dir = git_path_from_env(DB_ENVIRONMENT, git_common_dir,
160+
"objects", &git_db_env);
161+
git_index_file = git_path_from_env(INDEX_ENVIRONMENT, git_dir,
162+
"index", &git_index_env);
163+
git_graft_file = git_path_from_env(GRAFT_ENVIRONMENT, git_common_dir,
164+
"info/grafts", &git_graft_env);
155165
if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
156166
check_replace_refs = 0;
157167
namespace = expand_namespace(getenv(GIT_NAMESPACE_ENVIRONMENT));
@@ -174,6 +184,11 @@ const char *get_git_dir(void)
174184
return git_dir;
175185
}
176186

187+
const char *get_git_common_dir(void)
188+
{
189+
return git_common_dir;
190+
}
191+
177192
const char *get_git_namespace(void)
178193
{
179194
if (!namespace)

path.c

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,38 @@ static void replace_dir(struct strbuf *buf, int len, const char *newdir)
9090
buf->buf[newlen] = '/';
9191
}
9292

93+
static const char *common_list[] = {
94+
"/branches", "/hooks", "/info", "/logs", "/lost-found", "/modules",
95+
"/objects", "/refs", "/remotes", "/rr-cache", "/svn",
96+
"config", "gc.pid", "packed-refs", "shallow",
97+
NULL
98+
};
99+
100+
static void update_common_dir(struct strbuf *buf, int git_dir_len)
101+
{
102+
char *base = buf->buf + git_dir_len;
103+
const char **p;
104+
105+
if (is_dir_file(base, "logs", "HEAD"))
106+
return; /* keep this in $GIT_DIR */
107+
for (p = common_list; *p; p++) {
108+
const char *path = *p;
109+
int is_dir = 0;
110+
if (*path == '/') {
111+
path++;
112+
is_dir = 1;
113+
}
114+
if (is_dir && dir_prefix(base, path)) {
115+
replace_dir(buf, git_dir_len, get_git_common_dir());
116+
return;
117+
}
118+
if (!is_dir && !strcmp(base, path)) {
119+
replace_dir(buf, git_dir_len, get_git_common_dir());
120+
return;
121+
}
122+
}
123+
}
124+
93125
static void adjust_git_path(struct strbuf *buf, int git_dir_len)
94126
{
95127
const char *base = buf->buf + git_dir_len;
@@ -101,6 +133,8 @@ static void adjust_git_path(struct strbuf *buf, int git_dir_len)
101133
get_index_file(), strlen(get_index_file()));
102134
else if (git_db_env && dir_prefix(base, "objects"))
103135
replace_dir(buf, git_dir_len + 7, get_object_directory());
136+
else if (git_common_dir_env)
137+
update_common_dir(buf, git_dir_len);
104138
}
105139

106140
static void do_git_path(struct strbuf *buf, const char *fmt, va_list args)

t/t0060-path-utils.sh

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,5 +262,21 @@ test_expect_success 'setup fake objects directory foo' 'mkdir foo'
262262
test_git_path GIT_OBJECT_DIRECTORY=foo objects foo
263263
test_git_path GIT_OBJECT_DIRECTORY=foo objects/foo foo/foo
264264
test_git_path GIT_OBJECT_DIRECTORY=foo objects2 .git/objects2
265+
test_expect_success 'setup common repository' 'git --git-dir=bar init'
266+
test_git_path GIT_COMMON_DIR=bar index .git/index
267+
test_git_path GIT_COMMON_DIR=bar HEAD .git/HEAD
268+
test_git_path GIT_COMMON_DIR=bar logs/HEAD .git/logs/HEAD
269+
test_git_path GIT_COMMON_DIR=bar objects bar/objects
270+
test_git_path GIT_COMMON_DIR=bar objects/bar bar/objects/bar
271+
test_git_path GIT_COMMON_DIR=bar info/exclude bar/info/exclude
272+
test_git_path GIT_COMMON_DIR=bar info/grafts bar/info/grafts
273+
test_git_path GIT_COMMON_DIR=bar remotes/bar bar/remotes/bar
274+
test_git_path GIT_COMMON_DIR=bar branches/bar bar/branches/bar
275+
test_git_path GIT_COMMON_DIR=bar logs/refs/heads/master bar/logs/refs/heads/master
276+
test_git_path GIT_COMMON_DIR=bar refs/heads/master bar/refs/heads/master
277+
test_git_path GIT_COMMON_DIR=bar hooks/me bar/hooks/me
278+
test_git_path GIT_COMMON_DIR=bar config bar/config
279+
test_git_path GIT_COMMON_DIR=bar packed-refs bar/packed-refs
280+
test_git_path GIT_COMMON_DIR=bar shallow bar/shallow
265281

266282
test_done

0 commit comments

Comments
 (0)