Merge branch 'ps/object-source-management'
Code refactoring around object database sources. * ps/object-source-management: odb: handle recreation of quarantine directories odb: handle changing a repository's commondir chdir-notify: add function to unregister listeners odb: handle initialization of sources in `odb_new()` http-push: stop setting up `the_repository` for each reference t/helper: stop setting up `the_repository` repeatedly builtin/index-pack: fix deferred fsck outside repos oidset: introduce `oidset_equal()` odb: move logic to disable ref updates into repo odb: refactor `odb_clear()` to `odb_free()` odb: adopt logic to close object databases setup: convert `set_git_dir()` to have file scope path: move `enter_repo()` into "setup.c"main
commit
9d442ce2e2
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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[] = {
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
/*
|
||||
|
|
|
|||
6
fsck.c
6
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);
|
||||
|
|
|
|||
7
fsck.h
7
fsck.h
|
|
@ -248,6 +248,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.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
|
|
|
|||
98
odb.c
98
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)
|
||||
|
|
|
|||
37
odb.h
37
odb.h
|
|
@ -66,13 +66,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.
|
||||
*/
|
||||
|
|
@ -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
|
||||
|
|
|
|||
16
oidset.c
16
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;
|
||||
|
|
|
|||
9
oidset.h
9
oidset.h
|
|
@ -38,6 +38,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;
|
||||
|
|
|
|||
15
packfile.c
15
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"};
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
100
path.c
100
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)
|
||||
{
|
||||
|
|
|
|||
15
path.h
15
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);
|
||||
|
|
|
|||
2
refs.c
2
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;
|
||||
|
|
|
|||
25
repository.c
25
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;
|
||||
}
|
||||
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->objects->sources->disable_ref_updates = o->disable_ref_updates;
|
||||
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);
|
||||
|
|
|
|||
10
repository.h
10
repository.h
|
|
@ -71,6 +71,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,
|
||||
|
|
|
|||
|
|
@ -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
|
||||
{
|
||||
|
|
|
|||
2
scalar.c
2
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);
|
||||
|
|
|
|||
218
setup.c
218
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)
|
||||
const char *enter_repo(const char *path, unsigned flags)
|
||||
{
|
||||
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;
|
||||
static struct strbuf validated_path = STRBUF_INIT;
|
||||
static struct strbuf used_path = STRBUF_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;
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
xsetenv(GIT_DIR_ENVIRONMENT, path, 1);
|
||||
setup_git_env(path);
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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;
|
||||
if (is_git_directory(".")) {
|
||||
set_git_dir(".", 0);
|
||||
check_repository_format(NULL);
|
||||
return path;
|
||||
}
|
||||
|
||||
set_git_dir_1(path);
|
||||
if (!is_absolute_path(path))
|
||||
chdir_notify_register(NULL, update_relative_gitdir, NULL);
|
||||
|
||||
strbuf_release(&realpath);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int git_work_tree_initialized;
|
||||
|
|
|
|||
39
setup.h
39
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);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue