Skip to content

Commit 2816b74

Browse files
pks-tgitster
authored andcommitted
odb: handle changing a repository's commondir
The function `repo_set_gitdir()` is called in two situations: - To initialize the repository with its discovered location. As part of this we also set up the new object database. - To update the repository's discovered location in case the process changes its working directory so that we update relative paths. This means we also have to update any relative paths that are potentially used in the object database. In the context of the object database we ideally wouldn't ever have to worry about the second case: if all paths used by our object database sources were absolute, then we wouldn't have to update them. But unfortunately, the paths aren't only used to locate files owned by the given source, but we also use them for reporting purposes. One such example is `repo_get_object_directory()`, where we cannot just change semantics to always return absolute paths, as that is likely to break tooling out there. One solution to this would be to have both a "display path" and an "internal path". This would allow us to use internal paths for all internal matters, but continue to use the potentially-relative display paths so that we don't break compatibility. But converting the codebase to honor this split is quite a messy endeavour, and it wouldn't even help us with the goal to get rid of the need to update the display path on chdir(3p). Another solution would be to rework "setup.c" so that we never have to update paths in the first place. In that case, we'd only initialize the repository once we have figured out final locations for all directories. This would be a significant simplification of that subsystem indeed, but the current logic is so messy that it would take significant investments to get there. Meanwhile though, while object sources may still use relative paths, the best thing we can do is to handle the reparenting of the object source paths in the object database itself. This can be done by registering one callback for each object database so that we get notified whenever the current working directory changes, and we then perform the reparenting ourselves. Ideally, this wouldn't even happen on the object database level, but instead handled by each object database source. But we don't yet have proper pluggable object database sources, so this will need to be handled at a later point in time. The logic itself is rather simple: - We register the callback when creating the object database. - We unregister the callback when releasing it again. - We split up `set_git_dir_1()` so that it becomes possible to skip recreating the object database. This is required because the function is called both when the current working directory changes, but also when we set up the repository. Calling this function without skipping creation of the ODB will result in a bug in case it's already created. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
1 parent 2574c61 commit 2816b74

5 files changed

Lines changed: 83 additions & 56 deletions

File tree

odb.c

Lines changed: 34 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "git-compat-util.h"
22
#include "abspath.h"
3+
#include "chdir-notify.h"
34
#include "commit-graph.h"
45
#include "config.h"
56
#include "dir.h"
@@ -142,9 +143,9 @@ static void read_info_alternates(struct object_database *odb,
142143
const char *relative_base,
143144
int depth);
144145

145-
struct odb_source *odb_source_new(struct object_database *odb,
146-
const char *path,
147-
bool local)
146+
static struct odb_source *odb_source_new(struct object_database *odb,
147+
const char *path,
148+
bool local)
148149
{
149150
struct odb_source *source;
150151

@@ -1034,6 +1035,32 @@ int odb_write_object_stream(struct object_database *odb,
10341035
return odb_source_loose_write_stream(odb->sources, stream, len, oid);
10351036
}
10361037

1038+
static void odb_update_commondir(const char *name UNUSED,
1039+
const char *old_cwd,
1040+
const char *new_cwd,
1041+
void *cb_data)
1042+
{
1043+
struct object_database *odb = cb_data;
1044+
struct odb_source *source;
1045+
1046+
/*
1047+
* In theory, we only have to do this for the primary object source, as
1048+
* alternates' paths are always resolved to an absolute path.
1049+
*/
1050+
for (source = odb->sources; source; source = source->next) {
1051+
char *path;
1052+
1053+
if (is_absolute_path(source->path))
1054+
continue;
1055+
1056+
path = reparent_relative_path(old_cwd, new_cwd,
1057+
source->path);
1058+
1059+
free(source->path);
1060+
source->path = path;
1061+
}
1062+
}
1063+
10371064
struct object_database *odb_new(struct repository *repo,
10381065
const char *primary_source,
10391066
const char *secondary_sources)
@@ -1055,6 +1082,8 @@ struct object_database *odb_new(struct repository *repo,
10551082

10561083
free(to_free);
10571084

1085+
chdir_notify_register(NULL, odb_update_commondir, o);
1086+
10581087
return o;
10591088
}
10601089

@@ -1106,6 +1135,8 @@ void odb_free(struct object_database *o)
11061135
packfile_store_free(o->packfiles);
11071136
string_list_clear(&o->submodule_source_paths, 0);
11081137

1138+
chdir_notify_unregister(NULL, odb_update_commondir, o);
1139+
11091140
free(o);
11101141
}
11111142

odb.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,6 @@ struct odb_source {
7878
char *path;
7979
};
8080

81-
struct odb_source *odb_source_new(struct object_database *odb,
82-
const char *path,
83-
bool local);
84-
8581
struct packed_git;
8682
struct packfile_store;
8783
struct cached_object_entry;

repository.c

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -165,17 +165,10 @@ void repo_set_gitdir(struct repository *repo,
165165

166166
repo_set_commondir(repo, o->commondir);
167167

168-
if (!repo->objects) {
168+
if (!repo->objects)
169169
repo->objects = odb_new(repo, o->object_dir, o->alternate_db);
170-
} else {
171-
char *objects_path = NULL;
172-
expand_base_dir(&objects_path, o->object_dir,
173-
repo->commondir, "objects");
174-
free(repo->objects->sources->path);
175-
repo->objects->sources->path = objects_path;
176-
free(repo->objects->alternate_db);
177-
repo->objects->alternate_db = xstrdup_or_null(o->alternate_db);
178-
}
170+
else if (!o->skip_initializing_odb)
171+
BUG("cannot reinitialize an already-initialized object directory");
179172

180173
repo->disable_ref_updates = o->disable_ref_updates;
181174

repository.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ struct set_gitdir_args {
195195
const char *index_file;
196196
const char *alternate_db;
197197
bool disable_ref_updates;
198+
bool skip_initializing_odb;
198199
};
199200

200201
void repo_set_gitdir(struct repository *repo, const char *root,

setup.c

Lines changed: 45 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1002,10 +1002,51 @@ const char *read_gitfile_gently(const char *path, int *return_error_code)
10021002
return error_code ? NULL : path;
10031003
}
10041004

1005-
static void set_git_dir_1(const char *path)
1005+
static void setup_git_env_internal(const char *git_dir,
1006+
bool skip_initializing_odb)
1007+
{
1008+
char *git_replace_ref_base;
1009+
const char *shallow_file;
1010+
const char *replace_ref_base;
1011+
struct set_gitdir_args args = { NULL };
1012+
struct strvec to_free = STRVEC_INIT;
1013+
1014+
args.commondir = getenv_safe(&to_free, GIT_COMMON_DIR_ENVIRONMENT);
1015+
args.object_dir = getenv_safe(&to_free, DB_ENVIRONMENT);
1016+
args.graft_file = getenv_safe(&to_free, GRAFT_ENVIRONMENT);
1017+
args.index_file = getenv_safe(&to_free, INDEX_ENVIRONMENT);
1018+
args.alternate_db = getenv_safe(&to_free, ALTERNATE_DB_ENVIRONMENT);
1019+
if (getenv(GIT_QUARANTINE_ENVIRONMENT))
1020+
args.disable_ref_updates = true;
1021+
args.skip_initializing_odb = skip_initializing_odb;
1022+
1023+
repo_set_gitdir(the_repository, git_dir, &args);
1024+
strvec_clear(&to_free);
1025+
1026+
if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
1027+
disable_replace_refs();
1028+
replace_ref_base = getenv(GIT_REPLACE_REF_BASE_ENVIRONMENT);
1029+
git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base
1030+
: "refs/replace/");
1031+
update_ref_namespace(NAMESPACE_REPLACE, git_replace_ref_base);
1032+
1033+
shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
1034+
if (shallow_file)
1035+
set_alternate_shallow_file(the_repository, shallow_file, 0);
1036+
1037+
if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0))
1038+
fetch_if_missing = 0;
1039+
}
1040+
1041+
void setup_git_env(const char *git_dir)
1042+
{
1043+
setup_git_env_internal(git_dir, false);
1044+
}
1045+
1046+
static void set_git_dir_1(const char *path, bool skip_initializing_odb)
10061047
{
10071048
xsetenv(GIT_DIR_ENVIRONMENT, path, 1);
1008-
setup_git_env(path);
1049+
setup_git_env_internal(path, skip_initializing_odb);
10091050
}
10101051

10111052
static void update_relative_gitdir(const char *name UNUSED,
@@ -1020,7 +1061,7 @@ static void update_relative_gitdir(const char *name UNUSED,
10201061
trace_printf_key(&trace_setup_key,
10211062
"setup: move $GIT_DIR to '%s'",
10221063
path);
1023-
set_git_dir_1(path);
1064+
set_git_dir_1(path, true);
10241065
if (tmp_objdir)
10251066
tmp_objdir_reapply_primary_odb(tmp_objdir, old_cwd, new_cwd);
10261067
free(path);
@@ -1035,7 +1076,7 @@ static void set_git_dir(const char *path, int make_realpath)
10351076
path = realpath.buf;
10361077
}
10371078

1038-
set_git_dir_1(path);
1079+
set_git_dir_1(path, false);
10391080
if (!is_absolute_path(path))
10401081
chdir_notify_register(NULL, update_relative_gitdir, NULL);
10411082

@@ -1668,41 +1709,6 @@ enum discovery_result discover_git_directory_reason(struct strbuf *commondir,
16681709
return result;
16691710
}
16701711

1671-
void setup_git_env(const char *git_dir)
1672-
{
1673-
char *git_replace_ref_base;
1674-
const char *shallow_file;
1675-
const char *replace_ref_base;
1676-
struct set_gitdir_args args = { NULL };
1677-
struct strvec to_free = STRVEC_INIT;
1678-
1679-
args.commondir = getenv_safe(&to_free, GIT_COMMON_DIR_ENVIRONMENT);
1680-
args.object_dir = getenv_safe(&to_free, DB_ENVIRONMENT);
1681-
args.graft_file = getenv_safe(&to_free, GRAFT_ENVIRONMENT);
1682-
args.index_file = getenv_safe(&to_free, INDEX_ENVIRONMENT);
1683-
args.alternate_db = getenv_safe(&to_free, ALTERNATE_DB_ENVIRONMENT);
1684-
if (getenv(GIT_QUARANTINE_ENVIRONMENT)) {
1685-
args.disable_ref_updates = true;
1686-
}
1687-
1688-
repo_set_gitdir(the_repository, git_dir, &args);
1689-
strvec_clear(&to_free);
1690-
1691-
if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
1692-
disable_replace_refs();
1693-
replace_ref_base = getenv(GIT_REPLACE_REF_BASE_ENVIRONMENT);
1694-
git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base
1695-
: "refs/replace/");
1696-
update_ref_namespace(NAMESPACE_REPLACE, git_replace_ref_base);
1697-
1698-
shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
1699-
if (shallow_file)
1700-
set_alternate_shallow_file(the_repository, shallow_file, 0);
1701-
1702-
if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0))
1703-
fetch_if_missing = 0;
1704-
}
1705-
17061712
const char *enter_repo(const char *path, unsigned flags)
17071713
{
17081714
static struct strbuf validated_path = STRBUF_INIT;

0 commit comments

Comments
 (0)