aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--builtin/clone.c2
-rw-r--r--builtin/gc.c2
-rw-r--r--builtin/index-pack.c21
-rw-r--r--builtin/receive-pack.c2
-rw-r--r--builtin/repack.c2
-rw-r--r--builtin/upload-archive.c2
-rw-r--r--builtin/upload-pack.c2
-rw-r--r--chdir-notify.c18
-rw-r--r--chdir-notify.h2
-rw-r--r--fsck.c6
-rw-r--r--fsck.h7
-rw-r--r--http-backend.c1
-rw-r--r--http-push.c5
-rw-r--r--midx-write.c2
-rw-r--r--odb.c98
-rw-r--r--odb.h37
-rw-r--r--oidset.c16
-rw-r--r--oidset.h9
-rw-r--r--packfile.c15
-rw-r--r--packfile.h1
-rw-r--r--path.c100
-rw-r--r--path.h15
-rw-r--r--refs.c2
-rw-r--r--repository.c27
-rw-r--r--repository.h10
-rw-r--r--run-command.c2
-rw-r--r--scalar.c2
-rw-r--r--setup.c214
-rw-r--r--setup.h39
-rw-r--r--t/helper/test-repository.c16
-rwxr-xr-xt/t5302-pack-index.sh16
31 files changed, 414 insertions, 279 deletions
diff --git a/builtin/clone.c b/builtin/clone.c
index c990f398ef..b19b302b06 100644
--- a/builtin/clone.c
+++ b/builtin/clone.c
@@ -1617,7 +1617,7 @@ int cmd_clone(int argc,
transport_disconnect(transport);
if (option_dissociate) {
- close_object_store(the_repository->objects);
+ odb_close(the_repository->objects);
dissociate_from_references();
}
diff --git a/builtin/gc.c b/builtin/gc.c
index e9a76243aa..92c6e7b954 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -1063,7 +1063,7 @@ int cmd_gc(int argc,
report_garbage = report_pack_garbage;
odb_reprepare(the_repository->objects);
if (pack_garbage.nr > 0) {
- close_object_store(the_repository->objects);
+ odb_close(the_repository->objects);
clean_pack_garbage();
}
diff --git a/builtin/index-pack.c b/builtin/index-pack.c
index 2b78ba7fe4..699fe678cd 100644
--- a/builtin/index-pack.c
+++ b/builtin/index-pack.c
@@ -1640,7 +1640,7 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
rename_tmp_packfile(&final_index_name, curr_index_name, &index_name,
hash, "idx", 1);
- if (do_fsck_object)
+ if (do_fsck_object && startup_info->have_repository)
packfile_store_load_pack(the_repository->objects->packfiles,
final_index_name, 0);
@@ -2110,8 +2110,23 @@ int cmd_index_pack(int argc,
else
close(input_fd);
- if (do_fsck_object && fsck_finish(&fsck_options))
- die(_("fsck error in pack objects"));
+ if (do_fsck_object) {
+ /*
+ * We cannot perform queued consistency checks when running
+ * outside of a repository because those require us to read
+ * from the object database, which is uninitialized.
+ *
+ * TODO: we may eventually set up an in-memory object database,
+ * which would allow us to perform these queued checks.
+ */
+ if (!startup_info->have_repository &&
+ fsck_has_queued_checks(&fsck_options))
+ die(_("cannot perform queued object checks outside "
+ "of a repository"));
+
+ if (fsck_finish(&fsck_options))
+ die(_("fsck error in pack objects"));
+ }
free(opts.anomaly);
free(objects);
diff --git a/builtin/receive-pack.c b/builtin/receive-pack.c
index 28a6404219..9c49174616 100644
--- a/builtin/receive-pack.c
+++ b/builtin/receive-pack.c
@@ -34,7 +34,6 @@
#include "object-file.h"
#include "object-name.h"
#include "odb.h"
-#include "path.h"
#include "protocol.h"
#include "commit-reach.h"
#include "server-info.h"
@@ -42,6 +41,7 @@
#include "trace2.h"
#include "worktree.h"
#include "shallow.h"
+#include "setup.h"
#include "parse-options.h"
static const char * const receive_pack_usage[] = {
diff --git a/builtin/repack.c b/builtin/repack.c
index cfdb4c0920..d9012141f6 100644
--- a/builtin/repack.c
+++ b/builtin/repack.c
@@ -488,7 +488,7 @@ int cmd_repack(int argc,
string_list_sort(&names);
- close_object_store(repo->objects);
+ odb_close(repo->objects);
/*
* Ok we have prepared all new packfiles.
diff --git a/builtin/upload-archive.c b/builtin/upload-archive.c
index 97d7c9522f..25312bb2a5 100644
--- a/builtin/upload-archive.c
+++ b/builtin/upload-archive.c
@@ -4,8 +4,8 @@
#define USE_THE_REPOSITORY_VARIABLE
#include "builtin.h"
#include "archive.h"
-#include "path.h"
#include "pkt-line.h"
+#include "setup.h"
#include "sideband.h"
#include "run-command.h"
#include "strvec.h"
diff --git a/builtin/upload-pack.c b/builtin/upload-pack.c
index c2bbc035ab..30498fafea 100644
--- a/builtin/upload-pack.c
+++ b/builtin/upload-pack.c
@@ -5,11 +5,11 @@
#include "gettext.h"
#include "pkt-line.h"
#include "parse-options.h"
-#include "path.h"
#include "protocol.h"
#include "replace-object.h"
#include "upload-pack.h"
#include "serve.h"
+#include "setup.h"
#include "commit.h"
#include "environment.h"
diff --git a/chdir-notify.c b/chdir-notify.c
index 0d7bc04607..f8bfe3cbef 100644
--- a/chdir-notify.c
+++ b/chdir-notify.c
@@ -25,6 +25,24 @@ void chdir_notify_register(const char *name,
list_add_tail(&e->list, &chdir_notify_entries);
}
+void chdir_notify_unregister(const char *name, chdir_notify_callback cb,
+ void *data)
+{
+ struct list_head *pos, *p;
+
+ list_for_each_safe(pos, p, &chdir_notify_entries) {
+ struct chdir_notify_entry *e =
+ list_entry(pos, struct chdir_notify_entry, list);
+
+ if (e->cb != cb || e->data != data || !e->name != !name ||
+ (e->name && strcmp(e->name, name)))
+ continue;
+
+ list_del(pos);
+ free(e);
+ }
+}
+
static void reparent_cb(const char *name,
const char *old_cwd,
const char *new_cwd,
diff --git a/chdir-notify.h b/chdir-notify.h
index 366e4c1ee9..81eb69d846 100644
--- a/chdir-notify.h
+++ b/chdir-notify.h
@@ -41,6 +41,8 @@ typedef void (*chdir_notify_callback)(const char *name,
const char *new_cwd,
void *data);
void chdir_notify_register(const char *name, chdir_notify_callback cb, void *data);
+void chdir_notify_unregister(const char *name, chdir_notify_callback cb,
+ void *data);
void chdir_notify_reparent(const char *name, char **path);
/*
diff --git a/fsck.c b/fsck.c
index 620861a4f1..138fffded9 100644
--- a/fsck.c
+++ b/fsck.c
@@ -1379,6 +1379,12 @@ int fsck_finish(struct fsck_options *options)
return ret;
}
+bool fsck_has_queued_checks(struct fsck_options *options)
+{
+ return !oidset_equal(&options->gitmodules_found, &options->gitmodules_done) ||
+ !oidset_equal(&options->gitattributes_found, &options->gitattributes_done);
+}
+
void fsck_options_clear(struct fsck_options *options)
{
free(options->msg_type);
diff --git a/fsck.h b/fsck.h
index cb6ef32f4f..336917c045 100644
--- a/fsck.h
+++ b/fsck.h
@@ -249,6 +249,13 @@ int fsck_tag_standalone(const struct object_id *oid, const char *buffer,
int fsck_finish(struct fsck_options *options);
/*
+ * Check whether there are any checks that have been queued up and that still
+ * need to be run. Returns `false` iff `fsck_finish()` wouldn't perform any
+ * actions, `true` otherwise.
+ */
+bool fsck_has_queued_checks(struct fsck_options *options);
+
+/*
* Clear the fsck_options struct, freeing any allocated memory.
*/
void fsck_options_clear(struct fsck_options *options);
diff --git a/http-backend.c b/http-backend.c
index 273ed7266f..24f0dc119a 100644
--- a/http-backend.c
+++ b/http-backend.c
@@ -16,6 +16,7 @@
#include "run-command.h"
#include "string-list.h"
#include "url.h"
+#include "setup.h"
#include "strvec.h"
#include "packfile.h"
#include "odb.h"
diff --git a/http-push.c b/http-push.c
index d86ce77119..60a9b75620 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1725,6 +1725,7 @@ int cmd_main(int argc, const char **argv)
int i;
int new_refs;
struct ref *ref, *local_refs = NULL;
+ const char *gitdir;
CALLOC_ARRAY(repo, 1);
@@ -1787,7 +1788,7 @@ int cmd_main(int argc, const char **argv)
if (delete_branch && rs.nr != 1)
die("You must specify only one branch name when deleting a remote branch");
- setup_git_directory();
+ gitdir = setup_git_directory();
memset(remote_dir_exists, -1, 256);
@@ -1941,7 +1942,7 @@ int cmd_main(int argc, const char **argv)
if (!push_all && !is_null_oid(&ref->old_oid))
strvec_pushf(&commit_argv, "^%s",
oid_to_hex(&ref->old_oid));
- repo_init_revisions(the_repository, &revs, setup_git_directory());
+ repo_init_revisions(the_repository, &revs, gitdir);
setup_revisions_from_strvec(&commit_argv, &revs, NULL);
revs.edge_hint = 0; /* just in case */
diff --git a/midx-write.c b/midx-write.c
index 23e61cb000..e3e9be6d03 100644
--- a/midx-write.c
+++ b/midx-write.c
@@ -1458,7 +1458,7 @@ static int write_midx_internal(struct odb_source *source,
}
if (ctx.m || ctx.base_midx)
- close_object_store(ctx.repo->objects);
+ odb_close(ctx.repo->objects);
if (commit_lock_file(&lk) < 0)
die_errno(_("could not write multi-pack-index"));
diff --git a/odb.c b/odb.c
index 3ec21ef24e..dc8f292f3d 100644
--- a/odb.c
+++ b/odb.c
@@ -1,5 +1,6 @@
#include "git-compat-util.h"
#include "abspath.h"
+#include "chdir-notify.h"
#include "commit-graph.h"
#include "config.h"
#include "dir.h"
@@ -9,6 +10,7 @@
#include "khash.h"
#include "lockfile.h"
#include "loose.h"
+#include "midx.h"
#include "object-file-convert.h"
#include "object-file.h"
#include "odb.h"
@@ -22,6 +24,7 @@
#include "strbuf.h"
#include "strvec.h"
#include "submodule.h"
+#include "tmp-objdir.h"
#include "trace2.h"
#include "write-or-die.h"
@@ -141,9 +144,9 @@ static void read_info_alternates(struct object_database *odb,
const char *relative_base,
int depth);
-struct odb_source *odb_source_new(struct object_database *odb,
- const char *path,
- bool local)
+static struct odb_source *odb_source_new(struct object_database *odb,
+ const char *path,
+ bool local)
{
struct odb_source *source;
@@ -359,7 +362,7 @@ struct odb_source *odb_set_temporary_primary_source(struct object_database *odb,
* Disable ref updates while a temporary odb is active, since
* the objects in the database may roll back.
*/
- source->disable_ref_updates = 1;
+ odb->repo->disable_ref_updates = true;
source->will_destroy = will_destroy;
source->next = odb->sources;
odb->sources = source;
@@ -386,6 +389,7 @@ void odb_restore_primary_source(struct object_database *odb,
if (cur_source->next != restore_source)
BUG("we expect the old primary object store to be the first alternate");
+ odb->repo->disable_ref_updates = false;
odb->sources = restore_source;
odb_source_free(cur_source);
}
@@ -1032,18 +1036,79 @@ int odb_write_object_stream(struct object_database *odb,
return odb_source_loose_write_stream(odb->sources, stream, len, oid);
}
-struct object_database *odb_new(struct repository *repo)
+static void odb_update_commondir(const char *name UNUSED,
+ const char *old_cwd,
+ const char *new_cwd,
+ void *cb_data)
+{
+ struct object_database *odb = cb_data;
+ struct tmp_objdir *tmp_objdir;
+ struct odb_source *source;
+
+ tmp_objdir = tmp_objdir_unapply_primary_odb();
+
+ /*
+ * In theory, we only have to do this for the primary object source, as
+ * alternates' paths are always resolved to an absolute path.
+ */
+ for (source = odb->sources; source; source = source->next) {
+ char *path;
+
+ if (is_absolute_path(source->path))
+ continue;
+
+ path = reparent_relative_path(old_cwd, new_cwd,
+ source->path);
+
+ free(source->path);
+ source->path = path;
+ }
+
+ if (tmp_objdir)
+ tmp_objdir_reapply_primary_odb(tmp_objdir, old_cwd, new_cwd);
+}
+
+struct object_database *odb_new(struct repository *repo,
+ const char *primary_source,
+ const char *secondary_sources)
{
struct object_database *o = xmalloc(sizeof(*o));
+ char *to_free = NULL;
memset(o, 0, sizeof(*o));
o->repo = repo;
o->packfiles = packfile_store_new(o);
pthread_mutex_init(&o->replace_mutex, NULL);
string_list_init_dup(&o->submodule_source_paths);
+
+ if (!primary_source)
+ primary_source = to_free = xstrfmt("%s/objects", repo->commondir);
+ o->sources = odb_source_new(o, primary_source, true);
+ o->sources_tail = &o->sources->next;
+ o->alternate_db = xstrdup_or_null(secondary_sources);
+
+ free(to_free);
+
+ chdir_notify_register(NULL, odb_update_commondir, o);
+
return o;
}
+void odb_close(struct object_database *o)
+{
+ struct odb_source *source;
+
+ packfile_store_close(o->packfiles);
+
+ for (source = o->sources; source; source = source->next) {
+ if (source->midx)
+ close_midx(source->midx);
+ source->midx = NULL;
+ }
+
+ close_commit_graph(o);
+}
+
static void odb_free_sources(struct object_database *o)
{
while (o->sources) {
@@ -1057,30 +1122,29 @@ static void odb_free_sources(struct object_database *o)
o->source_by_path = NULL;
}
-void odb_clear(struct object_database *o)
+void odb_free(struct object_database *o)
{
- FREE_AND_NULL(o->alternate_db);
+ if (!o)
+ return;
+
+ free(o->alternate_db);
oidmap_clear(&o->replace_map, 1);
pthread_mutex_destroy(&o->replace_mutex);
- free_commit_graph(o->commit_graph);
- o->commit_graph = NULL;
- o->commit_graph_attempted = 0;
-
odb_free_sources(o);
- o->sources_tail = NULL;
- o->loaded_alternates = 0;
for (size_t i = 0; i < o->cached_object_nr; i++)
free((char *) o->cached_objects[i].value.buf);
- FREE_AND_NULL(o->cached_objects);
+ free(o->cached_objects);
- close_object_store(o);
+ odb_close(o);
packfile_store_free(o->packfiles);
- o->packfiles = NULL;
-
string_list_clear(&o->submodule_source_paths, 0);
+
+ chdir_notify_unregister(NULL, odb_update_commondir, o);
+
+ free(o);
}
void odb_reprepare(struct object_database *o)
diff --git a/odb.h b/odb.h
index 9bb28008b1..014cd9585a 100644
--- a/odb.h
+++ b/odb.h
@@ -67,13 +67,6 @@ struct odb_source {
bool local;
/*
- * This is a temporary object store created by the tmp_objdir
- * facility. Disable ref updates since the objects in the store
- * might be discarded on rollback.
- */
- int disable_ref_updates;
-
- /*
* This object store is ephemeral, so there is no need to fsync.
*/
int will_destroy;
@@ -85,10 +78,6 @@ struct odb_source {
char *path;
};
-struct odb_source *odb_source_new(struct object_database *odb,
- const char *path,
- bool local);
-
struct packed_git;
struct packfile_store;
struct cached_object_entry;
@@ -166,8 +155,30 @@ struct object_database {
struct string_list submodule_source_paths;
};
-struct object_database *odb_new(struct repository *repo);
-void odb_clear(struct object_database *o);
+/*
+ * Create a new object database for the given repository.
+ *
+ * If the primary source parameter is set it will override the usual primary
+ * object directory derived from the repository's common directory. The
+ * alternate sources are expected to be a PATH_SEP-separated list of secondary
+ * sources. Note that these alternate sources will be added in addition to, not
+ * instead of, the alternates identified by the primary source.
+ *
+ * Returns the newly created object database.
+ */
+struct object_database *odb_new(struct repository *repo,
+ const char *primary_source,
+ const char *alternate_sources);
+
+/* Free the object database and release all resources. */
+void odb_free(struct object_database *o);
+
+/*
+ * Close the object database and all of its sources so that any held resources
+ * will be released. The database can still be used after closing it, in which
+ * case these resources may be reallocated.
+ */
+void odb_close(struct object_database *o);
/*
* Clear caches, reload alternates and then reload object sources so that new
diff --git a/oidset.c b/oidset.c
index 8d36aef8dc..c8ff0b385c 100644
--- a/oidset.c
+++ b/oidset.c
@@ -16,6 +16,22 @@ int oidset_contains(const struct oidset *set, const struct object_id *oid)
return pos != kh_end(&set->set);
}
+bool oidset_equal(const struct oidset *a, const struct oidset *b)
+{
+ struct oidset_iter iter;
+ struct object_id *a_oid;
+
+ if (oidset_size(a) != oidset_size(b))
+ return false;
+
+ oidset_iter_init(a, &iter);
+ while ((a_oid = oidset_iter_next(&iter)))
+ if (!oidset_contains(b, a_oid))
+ return false;
+
+ return true;
+}
+
int oidset_insert(struct oidset *set, const struct object_id *oid)
{
int added;
diff --git a/oidset.h b/oidset.h
index 0106b6f278..e0f1a6ff4f 100644
--- a/oidset.h
+++ b/oidset.h
@@ -39,6 +39,11 @@ void oidset_init(struct oidset *set, size_t initial_size);
int oidset_contains(const struct oidset *set, const struct object_id *oid);
/**
+ * Returns true iff `a` and `b` contain the exact same OIDs.
+ */
+bool oidset_equal(const struct oidset *a, const struct oidset *b);
+
+/**
* Insert the oid into the set; a copy is made, so "oid" does not need
* to persist after this function is called.
*
@@ -94,11 +99,11 @@ void oidset_parse_file_carefully(struct oidset *set, const char *path,
oidset_parse_tweak_fn fn, void *cbdata);
struct oidset_iter {
- kh_oid_set_t *set;
+ const kh_oid_set_t *set;
khiter_t iter;
};
-static inline void oidset_iter_init(struct oidset *set,
+static inline void oidset_iter_init(const struct oidset *set,
struct oidset_iter *iter)
{
iter->set = &set->set;
diff --git a/packfile.c b/packfile.c
index 9cc11b6dc5..3d8b994a61 100644
--- a/packfile.c
+++ b/packfile.c
@@ -443,21 +443,6 @@ void close_pack(struct packed_git *p)
oidset_clear(&p->bad_objects);
}
-void close_object_store(struct object_database *o)
-{
- struct odb_source *source;
-
- packfile_store_close(o->packfiles);
-
- for (source = o->sources; source; source = source->next) {
- if (source->midx)
- close_midx(source->midx);
- source->midx = NULL;
- }
-
- close_commit_graph(o);
-}
-
void unlink_pack_path(const char *pack_name, int force_delete)
{
static const char *exts[] = {".idx", ".pack", ".rev", ".keep", ".bitmap", ".promisor", ".mtimes"};
diff --git a/packfile.h b/packfile.h
index 0e178fb279..20e1cf17b2 100644
--- a/packfile.h
+++ b/packfile.h
@@ -299,7 +299,6 @@ struct object_database;
unsigned char *use_pack(struct packed_git *, struct pack_window **, off_t, unsigned long *);
void close_pack_windows(struct packed_git *);
void close_pack(struct packed_git *);
-void close_object_store(struct object_database *o);
void unuse_pack(struct pack_window **);
void clear_delta_base_cache(void);
struct packed_git *add_packed_git(struct repository *r, const char *path,
diff --git a/path.c b/path.c
index 7f56eaf993..d726537622 100644
--- a/path.c
+++ b/path.c
@@ -738,106 +738,6 @@ return_null:
return NULL;
}
-/*
- * First, one directory to try is determined by the following algorithm.
- *
- * (0) If "strict" is given, the path is used as given and no DWIM is
- * done. Otherwise:
- * (1) "~/path" to mean path under the running user's home directory;
- * (2) "~user/path" to mean path under named user's home directory;
- * (3) "relative/path" to mean cwd relative directory; or
- * (4) "/absolute/path" to mean absolute directory.
- *
- * Unless "strict" is given, we check "%s/.git", "%s", "%s.git/.git", "%s.git"
- * in this order. We select the first one that is a valid git repository, and
- * chdir() to it. If none match, or we fail to chdir, we return NULL.
- *
- * If all goes well, we return the directory we used to chdir() (but
- * before ~user is expanded), avoiding getcwd() resolving symbolic
- * links. User relative paths are also returned as they are given,
- * except DWIM suffixing.
- */
-const char *enter_repo(const char *path, unsigned flags)
-{
- static struct strbuf validated_path = STRBUF_INIT;
- static struct strbuf used_path = STRBUF_INIT;
-
- if (!path)
- return NULL;
-
- if (!(flags & ENTER_REPO_STRICT)) {
- static const char *suffix[] = {
- "/.git", "", ".git/.git", ".git", NULL,
- };
- const char *gitfile;
- int len = strlen(path);
- int i;
- while ((1 < len) && (path[len-1] == '/'))
- len--;
-
- /*
- * We can handle arbitrary-sized buffers, but this remains as a
- * sanity check on untrusted input.
- */
- if (PATH_MAX <= len)
- return NULL;
-
- strbuf_reset(&used_path);
- strbuf_reset(&validated_path);
- strbuf_add(&used_path, path, len);
- strbuf_add(&validated_path, path, len);
-
- if (used_path.buf[0] == '~') {
- char *newpath = interpolate_path(used_path.buf, 0);
- if (!newpath)
- return NULL;
- strbuf_attach(&used_path, newpath, strlen(newpath),
- strlen(newpath));
- }
- for (i = 0; suffix[i]; i++) {
- struct stat st;
- size_t baselen = used_path.len;
- strbuf_addstr(&used_path, suffix[i]);
- if (!stat(used_path.buf, &st) &&
- (S_ISREG(st.st_mode) ||
- (S_ISDIR(st.st_mode) && is_git_directory(used_path.buf)))) {
- strbuf_addstr(&validated_path, suffix[i]);
- break;
- }
- strbuf_setlen(&used_path, baselen);
- }
- if (!suffix[i])
- return NULL;
- gitfile = read_gitfile(used_path.buf);
- if (!(flags & ENTER_REPO_ANY_OWNER_OK))
- die_upon_dubious_ownership(gitfile, NULL, used_path.buf);
- if (gitfile) {
- strbuf_reset(&used_path);
- strbuf_addstr(&used_path, gitfile);
- }
- if (chdir(used_path.buf))
- return NULL;
- path = validated_path.buf;
- }
- else {
- const char *gitfile = read_gitfile(path);
- if (!(flags & ENTER_REPO_ANY_OWNER_OK))
- die_upon_dubious_ownership(gitfile, NULL, path);
- if (gitfile)
- path = gitfile;
- if (chdir(path))
- return NULL;
- }
-
- if (is_git_directory(".")) {
- set_git_dir(".", 0);
- check_repository_format(NULL);
- return path;
- }
-
- return NULL;
-}
-
int calc_shared_perm(struct repository *repo,
int mode)
{
diff --git a/path.h b/path.h
index e67348f253..0ec95a0b07 100644
--- a/path.h
+++ b/path.h
@@ -146,21 +146,6 @@ int adjust_shared_perm(struct repository *repo, const char *path);
char *interpolate_path(const char *path, int real_home);
-/* The bits are as follows:
- *
- * - ENTER_REPO_STRICT: callers that require exact paths (as opposed
- * to allowing known suffixes like ".git", ".git/.git" to be
- * omitted) can set this bit.
- *
- * - ENTER_REPO_ANY_OWNER_OK: callers that are willing to run without
- * ownership check can set this bit.
- */
-enum {
- ENTER_REPO_STRICT = (1<<0),
- ENTER_REPO_ANY_OWNER_OK = (1<<1),
-};
-
-const char *enter_repo(const char *path, unsigned flags);
const char *remove_leading_path(const char *in, const char *prefix);
const char *relative_path(const char *in, const char *prefix, struct strbuf *sb);
int normalize_path_copy_len(char *dst, const char *src, int *prefix_len);
diff --git a/refs.c b/refs.c
index 5583f6e09d..046b695bb2 100644
--- a/refs.c
+++ b/refs.c
@@ -2508,7 +2508,7 @@ int ref_transaction_prepare(struct ref_transaction *transaction,
break;
}
- if (refs->repo->objects->sources->disable_ref_updates) {
+ if (refs->repo->disable_ref_updates) {
strbuf_addstr(err,
_("ref updates forbidden inside quarantine environment"));
return -1;
diff --git a/repository.c b/repository.c
index 6aaa7ba008..863f24411b 100644
--- a/repository.c
+++ b/repository.c
@@ -52,7 +52,6 @@ static void set_default_hash_algo(struct repository *repo)
void initialize_repository(struct repository *repo)
{
- repo->objects = odb_new(repo);
repo->remote_state = remote_state_new();
repo->parsed_objects = parsed_object_pool_new(repo);
ALLOC_ARRAY(repo->index, 1);
@@ -160,29 +159,19 @@ void repo_set_gitdir(struct repository *repo,
* until after xstrdup(root). Then we can free it.
*/
char *old_gitdir = repo->gitdir;
- char *objects_path = NULL;
repo->gitdir = xstrdup(gitfile ? gitfile : root);
free(old_gitdir);
repo_set_commondir(repo, o->commondir);
- expand_base_dir(&objects_path, o->object_dir,
- repo->commondir, "objects");
-
- if (!repo->objects->sources) {
- repo->objects->sources = odb_source_new(repo->objects,
- objects_path, true);
- repo->objects->sources_tail = &repo->objects->sources->next;
- free(objects_path);
- } else {
- free(repo->objects->sources->path);
- repo->objects->sources->path = objects_path;
- }
- repo->objects->sources->disable_ref_updates = o->disable_ref_updates;
+ if (!repo->objects)
+ repo->objects = odb_new(repo, o->object_dir, o->alternate_db);
+ else if (!o->skip_initializing_odb)
+ BUG("cannot reinitialize an already-initialized object directory");
+
+ repo->disable_ref_updates = o->disable_ref_updates;
- free(repo->objects->alternate_db);
- repo->objects->alternate_db = xstrdup_or_null(o->alternate_db);
expand_base_dir(&repo->graft_file, o->graft_file,
repo->commondir, "info/grafts");
expand_base_dir(&repo->index_file, o->index_file,
@@ -382,8 +371,8 @@ void repo_clear(struct repository *repo)
FREE_AND_NULL(repo->worktree);
FREE_AND_NULL(repo->submodule_prefix);
- odb_clear(repo->objects);
- FREE_AND_NULL(repo->objects);
+ odb_free(repo->objects);
+ repo->objects = NULL;
parsed_object_pool_clear(repo->parsed_objects);
FREE_AND_NULL(repo->parsed_objects);
diff --git a/repository.h b/repository.h
index 5808a5d610..6063c4b846 100644
--- a/repository.h
+++ b/repository.h
@@ -72,6 +72,13 @@ struct repository {
struct ref_store *refs_private;
/*
+ * Disable ref updates. This is especially used in contexts where
+ * transactions may still be rolled back so that we don't start to
+ * reference objects that may vanish.
+ */
+ bool disable_ref_updates;
+
+ /*
* A strmap of ref_stores, stored by submodule name, accessible via
* `repo_get_submodule_ref_store()`.
*/
@@ -187,7 +194,8 @@ struct set_gitdir_args {
const char *graft_file;
const char *index_file;
const char *alternate_db;
- int disable_ref_updates;
+ bool disable_ref_updates;
+ bool skip_initializing_odb;
};
void repo_set_gitdir(struct repository *repo, const char *root,
diff --git a/run-command.c b/run-command.c
index ed9575bd6a..e3e02475cc 100644
--- a/run-command.c
+++ b/run-command.c
@@ -743,7 +743,7 @@ fail_pipe:
fflush(NULL);
if (cmd->close_object_store)
- close_object_store(the_repository->objects);
+ odb_close(the_repository->objects);
#ifndef GIT_WINDOWS_NATIVE
{
diff --git a/scalar.c b/scalar.c
index f754311627..2aeb191cc8 100644
--- a/scalar.c
+++ b/scalar.c
@@ -931,7 +931,7 @@ static int cmd_delete(int argc, const char **argv)
if (dir_inside_of(cwd, enlistment.buf) >= 0)
res = error(_("refusing to delete current working directory"));
else {
- close_object_store(the_repository->objects);
+ odb_close(the_repository->objects);
res = delete_enlistment(&enlistment);
}
strbuf_release(&enlistment);
diff --git a/setup.c b/setup.c
index cf47441b7b..3a6a048620 100644
--- a/setup.c
+++ b/setup.c
@@ -22,7 +22,6 @@
#include "chdir-notify.h"
#include "path.h"
#include "quote.h"
-#include "tmp-objdir.h"
#include "trace.h"
#include "trace2.h"
#include "worktree.h"
@@ -1002,6 +1001,83 @@ cleanup_return:
return error_code ? NULL : path;
}
+static void setup_git_env_internal(const char *git_dir,
+ bool skip_initializing_odb)
+{
+ char *git_replace_ref_base;
+ const char *shallow_file;
+ const char *replace_ref_base;
+ struct set_gitdir_args args = { NULL };
+ struct strvec to_free = STRVEC_INIT;
+
+ args.commondir = getenv_safe(&to_free, GIT_COMMON_DIR_ENVIRONMENT);
+ args.object_dir = getenv_safe(&to_free, DB_ENVIRONMENT);
+ args.graft_file = getenv_safe(&to_free, GRAFT_ENVIRONMENT);
+ args.index_file = getenv_safe(&to_free, INDEX_ENVIRONMENT);
+ args.alternate_db = getenv_safe(&to_free, ALTERNATE_DB_ENVIRONMENT);
+ if (getenv(GIT_QUARANTINE_ENVIRONMENT))
+ args.disable_ref_updates = true;
+ args.skip_initializing_odb = skip_initializing_odb;
+
+ repo_set_gitdir(the_repository, git_dir, &args);
+ strvec_clear(&to_free);
+
+ if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
+ disable_replace_refs();
+ replace_ref_base = getenv(GIT_REPLACE_REF_BASE_ENVIRONMENT);
+ git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base
+ : "refs/replace/");
+ update_ref_namespace(NAMESPACE_REPLACE, git_replace_ref_base);
+
+ shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
+ if (shallow_file)
+ set_alternate_shallow_file(the_repository, shallow_file, 0);
+
+ if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0))
+ fetch_if_missing = 0;
+}
+
+void setup_git_env(const char *git_dir)
+{
+ setup_git_env_internal(git_dir, false);
+}
+
+static void set_git_dir_1(const char *path, bool skip_initializing_odb)
+{
+ xsetenv(GIT_DIR_ENVIRONMENT, path, 1);
+ setup_git_env_internal(path, skip_initializing_odb);
+}
+
+static void update_relative_gitdir(const char *name UNUSED,
+ const char *old_cwd,
+ const char *new_cwd,
+ void *data UNUSED)
+{
+ char *path = reparent_relative_path(old_cwd, new_cwd,
+ repo_get_git_dir(the_repository));
+ trace_printf_key(&trace_setup_key,
+ "setup: move $GIT_DIR to '%s'",
+ path);
+ set_git_dir_1(path, true);
+ free(path);
+}
+
+static void set_git_dir(const char *path, int make_realpath)
+{
+ struct strbuf realpath = STRBUF_INIT;
+
+ if (make_realpath) {
+ strbuf_realpath(&realpath, path, 1);
+ path = realpath.buf;
+ }
+
+ set_git_dir_1(path, false);
+ if (!is_absolute_path(path))
+ chdir_notify_register(NULL, update_relative_gitdir, NULL);
+
+ strbuf_release(&realpath);
+}
+
static const char *setup_explicit_git_dir(const char *gitdirenv,
struct strbuf *cwd,
struct repository_format *repo_fmt,
@@ -1628,79 +1704,85 @@ enum discovery_result discover_git_directory_reason(struct strbuf *commondir,
return result;
}
-void setup_git_env(const char *git_dir)
-{
- char *git_replace_ref_base;
- const char *shallow_file;
- const char *replace_ref_base;
- struct set_gitdir_args args = { NULL };
- struct strvec to_free = STRVEC_INIT;
-
- args.commondir = getenv_safe(&to_free, GIT_COMMON_DIR_ENVIRONMENT);
- args.object_dir = getenv_safe(&to_free, DB_ENVIRONMENT);
- args.graft_file = getenv_safe(&to_free, GRAFT_ENVIRONMENT);
- args.index_file = getenv_safe(&to_free, INDEX_ENVIRONMENT);
- args.alternate_db = getenv_safe(&to_free, ALTERNATE_DB_ENVIRONMENT);
- if (getenv(GIT_QUARANTINE_ENVIRONMENT)) {
- args.disable_ref_updates = 1;
- }
-
- repo_set_gitdir(the_repository, git_dir, &args);
- strvec_clear(&to_free);
-
- if (getenv(NO_REPLACE_OBJECTS_ENVIRONMENT))
- disable_replace_refs();
- replace_ref_base = getenv(GIT_REPLACE_REF_BASE_ENVIRONMENT);
- git_replace_ref_base = xstrdup(replace_ref_base ? replace_ref_base
- : "refs/replace/");
- update_ref_namespace(NAMESPACE_REPLACE, git_replace_ref_base);
-
- shallow_file = getenv(GIT_SHALLOW_FILE_ENVIRONMENT);
- if (shallow_file)
- set_alternate_shallow_file(the_repository, shallow_file, 0);
-
- if (git_env_bool(NO_LAZY_FETCH_ENVIRONMENT, 0))
- fetch_if_missing = 0;
-}
-
-static void set_git_dir_1(const char *path)
+const char *enter_repo(const char *path, unsigned flags)
{
- xsetenv(GIT_DIR_ENVIRONMENT, path, 1);
- setup_git_env(path);
-}
+ static struct strbuf validated_path = STRBUF_INIT;
+ static struct strbuf used_path = STRBUF_INIT;
-static void update_relative_gitdir(const char *name UNUSED,
- const char *old_cwd,
- const char *new_cwd,
- void *data UNUSED)
-{
- char *path = reparent_relative_path(old_cwd, new_cwd,
- repo_get_git_dir(the_repository));
- struct tmp_objdir *tmp_objdir = tmp_objdir_unapply_primary_odb();
+ if (!path)
+ return NULL;
- trace_printf_key(&trace_setup_key,
- "setup: move $GIT_DIR to '%s'",
- path);
- set_git_dir_1(path);
- if (tmp_objdir)
- tmp_objdir_reapply_primary_odb(tmp_objdir, old_cwd, new_cwd);
- free(path);
-}
+ if (!(flags & ENTER_REPO_STRICT)) {
+ static const char *suffix[] = {
+ "/.git", "", ".git/.git", ".git", NULL,
+ };
+ const char *gitfile;
+ int len = strlen(path);
+ int i;
+ while ((1 < len) && (path[len-1] == '/'))
+ len--;
-void set_git_dir(const char *path, int make_realpath)
-{
- struct strbuf realpath = STRBUF_INIT;
+ /*
+ * We can handle arbitrary-sized buffers, but this remains as a
+ * sanity check on untrusted input.
+ */
+ if (PATH_MAX <= len)
+ return NULL;
- if (make_realpath) {
- strbuf_realpath(&realpath, path, 1);
- path = realpath.buf;
+ strbuf_reset(&used_path);
+ strbuf_reset(&validated_path);
+ strbuf_add(&used_path, path, len);
+ strbuf_add(&validated_path, path, len);
+
+ if (used_path.buf[0] == '~') {
+ char *newpath = interpolate_path(used_path.buf, 0);
+ if (!newpath)
+ return NULL;
+ strbuf_attach(&used_path, newpath, strlen(newpath),
+ strlen(newpath));
+ }
+ for (i = 0; suffix[i]; i++) {
+ struct stat st;
+ size_t baselen = used_path.len;
+ strbuf_addstr(&used_path, suffix[i]);
+ if (!stat(used_path.buf, &st) &&
+ (S_ISREG(st.st_mode) ||
+ (S_ISDIR(st.st_mode) && is_git_directory(used_path.buf)))) {
+ strbuf_addstr(&validated_path, suffix[i]);
+ break;
+ }
+ strbuf_setlen(&used_path, baselen);
+ }
+ if (!suffix[i])
+ return NULL;
+ gitfile = read_gitfile(used_path.buf);
+ if (!(flags & ENTER_REPO_ANY_OWNER_OK))
+ die_upon_dubious_ownership(gitfile, NULL, used_path.buf);
+ if (gitfile) {
+ strbuf_reset(&used_path);
+ strbuf_addstr(&used_path, gitfile);
+ }
+ if (chdir(used_path.buf))
+ return NULL;
+ path = validated_path.buf;
+ }
+ else {
+ const char *gitfile = read_gitfile(path);
+ if (!(flags & ENTER_REPO_ANY_OWNER_OK))
+ die_upon_dubious_ownership(gitfile, NULL, path);
+ if (gitfile)
+ path = gitfile;
+ if (chdir(path))
+ return NULL;
}
- set_git_dir_1(path);
- if (!is_absolute_path(path))
- chdir_notify_register(NULL, update_relative_gitdir, NULL);
+ if (is_git_directory(".")) {
+ set_git_dir(".", 0);
+ check_repository_format(NULL);
+ return path;
+ }
- strbuf_release(&realpath);
+ return NULL;
}
static int git_work_tree_initialized;
diff --git a/setup.h b/setup.h
index 8522fa8575..d55dcc6608 100644
--- a/setup.h
+++ b/setup.h
@@ -94,9 +94,46 @@ static inline int discover_git_directory(struct strbuf *commondir,
return 0;
}
-void set_git_dir(const char *path, int make_realpath);
void set_git_work_tree(const char *tree);
+/* Flags that can be passed to `enter_repo()`. */
+enum {
+ /*
+ * Callers that require exact paths (as opposed to allowing known
+ * suffixes like ".git", ".git/.git" to be omitted) can set this bit.
+ */
+ ENTER_REPO_STRICT = (1<<0),
+
+ /*
+ * Callers that are willing to run without ownership check can set this
+ * bit.
+ */
+ ENTER_REPO_ANY_OWNER_OK = (1<<1),
+};
+
+/*
+ * Discover and enter a repository.
+ *
+ * First, one directory to try is determined by the following algorithm.
+ *
+ * (0) If "strict" is given, the path is used as given and no DWIM is
+ * done. Otherwise:
+ * (1) "~/path" to mean path under the running user's home directory;
+ * (2) "~user/path" to mean path under named user's home directory;
+ * (3) "relative/path" to mean cwd relative directory; or
+ * (4) "/absolute/path" to mean absolute directory.
+ *
+ * Unless "strict" is given, we check "%s/.git", "%s", "%s.git/.git", "%s.git"
+ * in this order. We select the first one that is a valid git repository, and
+ * chdir() to it. If none match, or we fail to chdir, we return NULL.
+ *
+ * If all goes well, we return the directory we used to chdir() (but
+ * before ~user is expanded), avoiding getcwd() resolving symbolic
+ * links. User relative paths are also returned as they are given,
+ * except DWIM suffixing.
+ */
+const char *enter_repo(const char *path, unsigned flags);
+
const char *setup_git_directory_gently(int *);
const char *setup_git_directory(void);
char *prefix_path(const char *prefix, int len, const char *path);
diff --git a/t/helper/test-repository.c b/t/helper/test-repository.c
index 63c37de33d..9ba94cdffa 100644
--- a/t/helper/test-repository.c
+++ b/t/helper/test-repository.c
@@ -17,10 +17,6 @@ static void test_parse_commit_in_graph(const char *gitdir, const char *worktree,
struct commit *c;
struct commit_list *parent;
- setup_git_env(gitdir);
-
- repo_clear(the_repository);
-
if (repo_init(&r, gitdir, worktree))
die("Couldn't init repo");
@@ -47,10 +43,6 @@ static void test_get_commit_tree_in_graph(const char *gitdir,
struct commit *c;
struct tree *tree;
- setup_git_env(gitdir);
-
- repo_clear(the_repository);
-
if (repo_init(&r, gitdir, worktree))
die("Couldn't init repo");
@@ -75,24 +67,20 @@ static void test_get_commit_tree_in_graph(const char *gitdir,
int cmd__repository(int argc, const char **argv)
{
- int nongit_ok = 0;
-
- setup_git_directory_gently(&nongit_ok);
-
if (argc < 2)
die("must have at least 2 arguments");
if (!strcmp(argv[1], "parse_commit_in_graph")) {
struct object_id oid;
if (argc < 5)
die("not enough arguments");
- if (parse_oid_hex(argv[4], &oid, &argv[4]))
+ if (parse_oid_hex_any(argv[4], &oid, &argv[4]) == GIT_HASH_UNKNOWN)
die("cannot parse oid '%s'", argv[4]);
test_parse_commit_in_graph(argv[2], argv[3], &oid);
} else if (!strcmp(argv[1], "get_commit_tree_in_graph")) {
struct object_id oid;
if (argc < 5)
die("not enough arguments");
- if (parse_oid_hex(argv[4], &oid, &argv[4]))
+ if (parse_oid_hex_any(argv[4], &oid, &argv[4]) == GIT_HASH_UNKNOWN)
die("cannot parse oid '%s'", argv[4]);
test_get_commit_tree_in_graph(argv[2], argv[3], &oid);
} else {
diff --git a/t/t5302-pack-index.sh b/t/t5302-pack-index.sh
index 413c99274c..9697448cb2 100755
--- a/t/t5302-pack-index.sh
+++ b/t/t5302-pack-index.sh
@@ -293,4 +293,20 @@ test_expect_success 'too-large packs report the breach' '
grep "maximum allowed size (20 bytes)" err
'
+# git-index-pack(1) uses the default hash algorithm outside of the repository,
+# and it has no way to tell it otherwise. So we can only run this test with the
+# default hash algorithm, as it would otherwise fail to parse the tree.
+test_expect_success DEFAULT_HASH_ALGORITHM 'index-pack --fsck-objects outside of a repo' '
+ test_when_finished "rm -rf repo" &&
+ git init repo &&
+ (
+ cd repo &&
+ printf "100644 blob $(test_oid 001)\t.gitattributes\n" >tree &&
+ git mktree --missing <tree >tree-oid &&
+ git pack-objects <tree-oid pack &&
+ test_must_fail nongit git index-pack --fsck-objects "$(pwd)"/pack-*.pack 2>err &&
+ test_grep "cannot perform queued object checks outside of a repository" err
+ )
+'
+
test_done