diff --git a/Documentation/RelNotes/2.54.0.adoc b/Documentation/RelNotes/2.54.0.adoc index f37c0be602db33..fa6e42f3bb100a 100644 --- a/Documentation/RelNotes/2.54.0.adoc +++ b/Documentation/RelNotes/2.54.0.adoc @@ -136,9 +136,13 @@ Performance, Internal Implementation, Development Support etc. * Clean-up the code around "git repo info" command. - * Mark the marge-ort codebase to prevent more uses of the_repository + * Mark the merge-ort codebase to prevent more uses of the_repository from getting added. + * The core.attributesfile is intended to be set per repository, but + were kept track of by a single global variable in-core, which has + been corrected by moving it to per-repository data structure. + Fixes since v2.53 ----------------- @@ -225,6 +229,11 @@ Fixes since v2.53 corrected. (merge 3ef68ff40e sp/shallow-deepen-relative-fix later to maint). + * "fsck" iterates over packfiles and its access to pack data caused + the list to be permuted, which caused it to loop forever; the code + to access pack data by "fsck" has been updated to avoid this. + (merge 13eb65d366 ps/fsck-stream-from-the-right-object-instance later to maint). + * Other code cleanup, docfix, build fix, etc. (merge d79fff4a11 jk/remote-tracking-ref-leakfix later to maint). (merge 7a747f972d dd/t5403-modernise later to maint). diff --git a/attr.c b/attr.c index 4999b7e09da930..75369547b306d6 100644 --- a/attr.c +++ b/attr.c @@ -881,10 +881,11 @@ const char *git_attr_system_file(void) const char *git_attr_global_file(void) { - if (!git_attributes_file) - git_attributes_file = xdg_config_home("attributes"); + struct repo_config_values *cfg = repo_config_values(the_repository); + if (!cfg->attributes_file) + cfg->attributes_file = xdg_config_home("attributes"); - return git_attributes_file; + return cfg->attributes_file; } int git_attr_system_is_enabled(void) diff --git a/branch.h b/branch.h index ec2f35fda449aa..3dc6e2a0ffe635 100644 --- a/branch.h +++ b/branch.h @@ -15,8 +15,6 @@ enum branch_track { BRANCH_TRACK_SIMPLE, }; -extern enum branch_track git_branch_track; - /* Functions for acting on the information about branches. */ /** diff --git a/builtin/backfill.c b/builtin/backfill.c index d8cb3b0eba799d..e9a33e81be4cdb 100644 --- a/builtin/backfill.c +++ b/builtin/backfill.c @@ -128,6 +128,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit N_("Restrict the missing objects to the current sparse-checkout")), OPT_END(), }; + struct repo_config_values *cfg = repo_config_values(the_repository); show_usage_with_options_if_asked(argc, argv, builtin_backfill_usage, options); @@ -138,7 +139,7 @@ int cmd_backfill(int argc, const char **argv, const char *prefix, struct reposit repo_config(repo, git_default_config, NULL); if (ctx.sparse < 0) - ctx.sparse = core_apply_sparse_checkout; + ctx.sparse = cfg->apply_sparse_checkout; result = do_backfill(&ctx); backfill_context_clear(&ctx); diff --git a/builtin/branch.c b/builtin/branch.c index c577b5d20f2969..a1a43380d0640b 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -724,6 +724,7 @@ int cmd_branch(int argc, static struct ref_sorting *sorting; struct string_list sorting_options = STRING_LIST_INIT_DUP; struct ref_format format = REF_FORMAT_INIT; + struct repo_config_values *cfg = repo_config_values(the_repository); int ret; struct option options[] = { @@ -795,7 +796,7 @@ int cmd_branch(int argc, if (!sorting_options.nr) string_list_append(&sorting_options, "refname"); - track = git_branch_track; + track = cfg->branch_track; head = refs_resolve_refdup(get_main_ref_store(the_repository), "HEAD", 0, &head_oid, NULL); diff --git a/builtin/checkout.c b/builtin/checkout.c index eefefd5d0fe6d5..164a56480a6b81 100644 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@ -1603,6 +1603,7 @@ static void die_if_switching_to_a_branch_in_use(struct checkout_opts *opts, static int checkout_branch(struct checkout_opts *opts, struct branch_info *new_branch_info) { + struct repo_config_values *cfg = repo_config_values(the_repository); int noop_switch = (!new_branch_info->name && !opts->new_branch && !opts->force_detach); @@ -1646,7 +1647,7 @@ static int checkout_branch(struct checkout_opts *opts, if (opts->track != BRANCH_TRACK_UNSPECIFIED) die(_("'%s' cannot be used with '%s'"), "--detach", "-t"); } else if (opts->track == BRANCH_TRACK_UNSPECIFIED) - opts->track = git_branch_track; + opts->track = cfg->branch_track; if (new_branch_info->name && !new_branch_info->commit) die(_("Cannot switch branch to a non-commit '%s'"), diff --git a/builtin/clone.c b/builtin/clone.c index 70c8a68e6f30df..fba3c9c508bc06 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -616,13 +616,15 @@ static int git_sparse_checkout_init(const char *repo) { struct child_process cmd = CHILD_PROCESS_INIT; int result = 0; + struct repo_config_values *cfg = repo_config_values(the_repository); + strvec_pushl(&cmd.args, "-C", repo, "sparse-checkout", "set", NULL); /* * We must apply the setting in the current process * for the later checkout to use the sparse-checkout file. */ - core_apply_sparse_checkout = 1; + cfg->apply_sparse_checkout = 1; cmd.git_cmd = 1; if (run_command(&cmd)) { diff --git a/builtin/grep.c b/builtin/grep.c index 5b8b87b1ac4d7a..b6cecfd9b60f05 100644 --- a/builtin/grep.c +++ b/builtin/grep.c @@ -482,7 +482,7 @@ static int grep_submodule(struct grep_opt *opt, * "forget" the sparse-index feature switch. As a result, the index * of these submodules are expanded unexpectedly. * - * 2. "core_apply_sparse_checkout" + * 2. "config_values_private_.apply_sparse_checkout" * When running `grep` in the superproject, this setting is * populated using the superproject's configs. However, once * initialized, this config is globally accessible and is read by diff --git a/builtin/mv.c b/builtin/mv.c index d43925097b420f..2215d34e31f29a 100644 --- a/builtin/mv.c +++ b/builtin/mv.c @@ -238,6 +238,7 @@ int cmd_mv(int argc, struct hashmap moved_dirs = HASHMAP_INIT(pathmap_cmp, NULL); struct strbuf pathbuf = STRBUF_INIT; int ret; + struct repo_config_values *cfg = repo_config_values(the_repository); repo_config(the_repository, git_default_config, NULL); @@ -572,7 +573,7 @@ int cmd_mv(int argc, rename_index_entry_at(the_repository->index, pos, dst); if (ignore_sparse && - core_apply_sparse_checkout && + cfg->apply_sparse_checkout && core_sparse_checkout_cone) { /* * NEEDSWORK: we are *not* paying attention to diff --git a/builtin/push.c b/builtin/push.c index 5b6cebbb856cfc..7100ffba5da17e 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -151,6 +151,7 @@ static NORETURN void die_push_simple(struct branch *branch, const char *advice_pushdefault_maybe = ""; const char *advice_automergesimple_maybe = ""; const char *short_upstream = branch->merge[0]->src; + struct repo_config_values *cfg = repo_config_values(the_repository); skip_prefix(short_upstream, "refs/heads/", &short_upstream); @@ -162,7 +163,7 @@ static NORETURN void die_push_simple(struct branch *branch, advice_pushdefault_maybe = _("\n" "To choose either option permanently, " "see push.default in 'git help config'.\n"); - if (git_branch_track != BRANCH_TRACK_SIMPLE) + if (cfg->branch_track != BRANCH_TRACK_SIMPLE) advice_automergesimple_maybe = _("\n" "To avoid automatically configuring " "an upstream branch when its name\n" diff --git a/builtin/sparse-checkout.c b/builtin/sparse-checkout.c index 34e965bfa6f6c7..f4aa405da93760 100644 --- a/builtin/sparse-checkout.c +++ b/builtin/sparse-checkout.c @@ -61,9 +61,10 @@ static int sparse_checkout_list(int argc, const char **argv, const char *prefix, struct pattern_list pl; char *sparse_filename; int res; + struct repo_config_values *cfg = repo_config_values(the_repository); setup_work_tree(); - if (!core_apply_sparse_checkout) + if (!cfg->apply_sparse_checkout) die(_("this worktree is not sparse")); argc = parse_options(argc, argv, prefix, @@ -397,12 +398,14 @@ static int set_config(struct repository *repo, } static enum sparse_checkout_mode update_cone_mode(int *cone_mode) { + struct repo_config_values *cfg = repo_config_values(the_repository); + /* If not specified, use previous definition of cone mode */ - if (*cone_mode == -1 && core_apply_sparse_checkout) + if (*cone_mode == -1 && cfg->apply_sparse_checkout) *cone_mode = core_sparse_checkout_cone; /* Set cone/non-cone mode appropriately */ - core_apply_sparse_checkout = 1; + cfg->apply_sparse_checkout = 1; if (*cone_mode == 1 || *cone_mode == -1) { core_sparse_checkout_cone = 1; return MODE_CONE_PATTERNS; @@ -414,9 +417,10 @@ static enum sparse_checkout_mode update_cone_mode(int *cone_mode) { static int update_modes(struct repository *repo, int *cone_mode, int *sparse_index) { int mode, record_mode; + struct repo_config_values *cfg = repo_config_values(the_repository); /* Determine if we need to record the mode; ensure sparse checkout on */ - record_mode = (*cone_mode != -1) || !core_apply_sparse_checkout; + record_mode = (*cone_mode != -1) || !cfg->apply_sparse_checkout; mode = update_cone_mode(cone_mode); if (record_mode && set_config(repo, mode)) @@ -682,6 +686,7 @@ static int modify_pattern_list(struct repository *repo, int result; int changed_config = 0; struct pattern_list *pl = xcalloc(1, sizeof(*pl)); + struct repo_config_values *cfg = repo_config_values(the_repository); switch (m) { case ADD: @@ -697,9 +702,9 @@ static int modify_pattern_list(struct repository *repo, break; } - if (!core_apply_sparse_checkout) { + if (!cfg->apply_sparse_checkout) { set_config(repo, MODE_ALL_PATTERNS); - core_apply_sparse_checkout = 1; + cfg->apply_sparse_checkout = 1; changed_config = 1; } @@ -794,9 +799,10 @@ static int sparse_checkout_add(int argc, const char **argv, const char *prefix, }; struct strvec patterns = STRVEC_INIT; int ret; + struct repo_config_values *cfg = repo_config_values(the_repository); setup_work_tree(); - if (!core_apply_sparse_checkout) + if (!cfg->apply_sparse_checkout) die(_("no sparse-checkout to add to")); repo_read_index(repo); @@ -903,9 +909,10 @@ static int sparse_checkout_reapply(int argc, const char **argv, N_("toggle the use of a sparse index")), OPT_END(), }; + struct repo_config_values *cfg = repo_config_values(the_repository); setup_work_tree(); - if (!core_apply_sparse_checkout) + if (!cfg->apply_sparse_checkout) die(_("must be in a sparse-checkout to reapply sparsity patterns")); reapply_opts.cone_mode = -1; @@ -958,6 +965,7 @@ static int sparse_checkout_clean(int argc, const char **argv, size_t worktree_len; int force = 0, dry_run = 0, verbose = 0; int require_force = 1; + struct repo_config_values *cfg = repo_config_values(the_repository); struct option builtin_sparse_checkout_clean_options[] = { OPT__DRY_RUN(&dry_run, N_("dry run")), @@ -967,7 +975,7 @@ static int sparse_checkout_clean(int argc, const char **argv, }; setup_work_tree(); - if (!core_apply_sparse_checkout) + if (!cfg->apply_sparse_checkout) die(_("must be in a sparse-checkout to clean directories")); if (!core_sparse_checkout_cone) die(_("must be in a cone-mode sparse-checkout to clean directories")); @@ -1031,9 +1039,10 @@ static int sparse_checkout_disable(int argc, const char **argv, OPT_END(), }; struct pattern_list pl; + struct repo_config_values *cfg = repo_config_values(the_repository); /* - * We do not exit early if !core_apply_sparse_checkout; due to the + * We do not exit early if !repo->config_values.apply_sparse_checkout; due to the * ability for users to manually muck things up between * direct editing of .git/info/sparse-checkout * running read-tree -m u HEAD or update-index --skip-worktree @@ -1059,7 +1068,7 @@ static int sparse_checkout_disable(int argc, const char **argv, hashmap_init(&pl.recursive_hashmap, pl_hashmap_cmp, NULL, 0); hashmap_init(&pl.parent_hashmap, pl_hashmap_cmp, NULL, 0); pl.use_cone_patterns = 0; - core_apply_sparse_checkout = 1; + cfg->apply_sparse_checkout = 1; add_pattern("/*", empty_base, 0, &pl, 0); diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index b621d14275c34d..143f7cb3cca7d0 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -3300,9 +3300,10 @@ static int module_create_branch(int argc, const char **argv, const char *prefix, N_("git submodule--helper create-branch [-f|--force] [--create-reflog] [-q|--quiet] [-t|--track] [-n|--dry-run] "), NULL }; + struct repo_config_values *cfg = repo_config_values(the_repository); repo_config(the_repository, git_default_config, NULL); - track = git_branch_track; + track = cfg->branch_track; argc = parse_options(argc, argv, prefix, options, usage, 0); if (argc != 3) diff --git a/builtin/worktree.c b/builtin/worktree.c index 892800f70915f1..bc2d0d645ba945 100644 --- a/builtin/worktree.c +++ b/builtin/worktree.c @@ -473,6 +473,7 @@ static int add_worktree(const char *path, const char *refname, struct strbuf sb_name = STRBUF_INIT; struct worktree **worktrees, *wt = NULL; struct ref_store *wt_refs; + struct repo_config_values *cfg = repo_config_values(the_repository); worktrees = get_worktrees(); check_candidate_path(path, opts->force, worktrees, "add"); @@ -570,7 +571,7 @@ static int add_worktree(const char *path, const char *refname, * If the current worktree has sparse-checkout enabled, then copy * the sparse-checkout patterns from the current worktree. */ - if (core_apply_sparse_checkout) + if (cfg->apply_sparse_checkout) copy_sparse_checkout(sb_repo.buf); /* diff --git a/dir.c b/dir.c index b00821f294fea2..026d8516a912af 100644 --- a/dir.c +++ b/dir.c @@ -1551,7 +1551,9 @@ enum pattern_match_result path_matches_pattern_list( int init_sparse_checkout_patterns(struct index_state *istate) { - if (!core_apply_sparse_checkout) + struct repo_config_values *cfg = repo_config_values(the_repository); + + if (!cfg->apply_sparse_checkout) return 1; if (istate->sparse_checkout_patterns) return 0; diff --git a/environment.c b/environment.c index 2764d8f4817c2a..fc3ed8bb1c7a66 100644 --- a/environment.c +++ b/environment.c @@ -54,7 +54,6 @@ char *git_commit_encoding; char *git_log_output_encoding; char *apply_default_whitespace; char *apply_default_ignorewhitespace; -char *git_attributes_file; int zlib_compression_level = Z_BEST_SPEED; int pack_compression_level = Z_DEFAULT_COMPRESSION; int fsync_object_files = -1; @@ -68,7 +67,6 @@ enum auto_crlf auto_crlf = AUTO_CRLF_FALSE; enum eol core_eol = EOL_UNSET; int global_conv_flags_eol = CONV_EOL_RNDTRP_WARN; char *check_roundtrip_encoding; -enum branch_track git_branch_track = BRANCH_TRACK_REMOTE; enum rebase_setup_type autorebase = AUTOREBASE_NEVER; enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED; #ifndef OBJECT_CREATION_MODE @@ -76,7 +74,6 @@ enum push_default_type push_default = PUSH_DEFAULT_UNSPECIFIED; #endif enum object_creation_mode object_creation_mode = OBJECT_CREATION_MODE; int grafts_keep_true_parents; -int core_apply_sparse_checkout; int core_sparse_checkout_cone; int sparse_expect_files_outside_of_patterns; int precomposed_unicode = -1; /* see probe_utf8_pathname_composition() */ @@ -304,6 +301,8 @@ static enum fsync_component parse_fsync_components(const char *var, const char * int git_default_core_config(const char *var, const char *value, const struct config_context *ctx, void *cb) { + struct repo_config_values *cfg = repo_config_values(the_repository); + /* This needs a better name */ if (!strcmp(var, "core.filemode")) { trust_executable_bit = git_config_bool(var, value); @@ -341,8 +340,8 @@ int git_default_core_config(const char *var, const char *value, } if (!strcmp(var, "core.attributesfile")) { - FREE_AND_NULL(git_attributes_file); - return git_config_pathname(&git_attributes_file, var, value); + FREE_AND_NULL(cfg->attributes_file); + return git_config_pathname(&cfg->attributes_file, var, value); } if (!strcmp(var, "core.bare")) { @@ -527,7 +526,7 @@ int git_default_core_config(const char *var, const char *value, } if (!strcmp(var, "core.sparsecheckout")) { - core_apply_sparse_checkout = git_config_bool(var, value); + cfg->apply_sparse_checkout = git_config_bool(var, value); return 0; } @@ -584,18 +583,20 @@ static int git_default_i18n_config(const char *var, const char *value) static int git_default_branch_config(const char *var, const char *value) { + struct repo_config_values *cfg = repo_config_values(the_repository); + if (!strcmp(var, "branch.autosetupmerge")) { if (value && !strcmp(value, "always")) { - git_branch_track = BRANCH_TRACK_ALWAYS; + cfg->branch_track = BRANCH_TRACK_ALWAYS; return 0; } else if (value && !strcmp(value, "inherit")) { - git_branch_track = BRANCH_TRACK_INHERIT; + cfg->branch_track = BRANCH_TRACK_INHERIT; return 0; } else if (value && !strcmp(value, "simple")) { - git_branch_track = BRANCH_TRACK_SIMPLE; + cfg->branch_track = BRANCH_TRACK_SIMPLE; return 0; } - git_branch_track = git_config_bool(var, value); + cfg->branch_track = git_config_bool(var, value); return 0; } if (!strcmp(var, "branch.autosetuprebase")) { @@ -714,3 +715,10 @@ int git_default_config(const char *var, const char *value, /* Add other config variables here and to Documentation/config.adoc. */ return 0; } + +void repo_config_values_init(struct repo_config_values *cfg) +{ + cfg->attributes_file = NULL; + cfg->apply_sparse_checkout = 0; + cfg->branch_track = BRANCH_TRACK_REMOTE; +} diff --git a/environment.h b/environment.h index 540e0a7f6dd51f..123a71cdc8d14e 100644 --- a/environment.h +++ b/environment.h @@ -2,6 +2,7 @@ #define ENVIRONMENT_H #include "repo-settings.h" +#include "branch.h" /* Double-check local_repo_env below if you add to this list. */ #define GIT_DIR_ENVIRONMENT "GIT_DIR" @@ -85,6 +86,18 @@ extern const char * const local_repo_env[]; struct strvec; +struct repository; +struct repo_config_values { + /* section "core" config values */ + char *attributes_file; + int apply_sparse_checkout; + + /* section "branch" config values */ + enum branch_track branch_track; +}; + +struct repo_config_values *repo_config_values(struct repository *repo); + /* * Wrapper of getenv() that returns a strdup value. This value is kept * in argv to be freed later. @@ -110,6 +123,8 @@ int git_default_config(const char *, const char *, int git_default_core_config(const char *var, const char *value, const struct config_context *ctx, void *cb); +void repo_config_values_init(struct repo_config_values *cfg); + /* * TODO: All the below state either explicitly or implicitly relies on * `the_repository`. We should eventually get rid of these and make the @@ -155,7 +170,6 @@ extern int assume_unchanged; extern int warn_on_object_refname_ambiguity; extern char *apply_default_whitespace; extern char *apply_default_ignorewhitespace; -extern char *git_attributes_file; extern int zlib_compression_level; extern int pack_compression_level; extern unsigned long pack_size_limit_cfg; @@ -164,7 +178,6 @@ extern int precomposed_unicode; extern int protect_hfs; extern int protect_ntfs; -extern int core_apply_sparse_checkout; extern int core_sparse_checkout_cone; extern int sparse_expect_files_outside_of_patterns; diff --git a/object-file.c b/object-file.c index b8265021f988eb..3094140055cf42 100644 --- a/object-file.c +++ b/object-file.c @@ -129,18 +129,15 @@ int check_object_signature(struct repository *r, const struct object_id *oid, return !oideq(oid, &real_oid) ? -1 : 0; } -int stream_object_signature(struct repository *r, const struct object_id *oid) +int stream_object_signature(struct repository *r, + struct odb_read_stream *st, + const struct object_id *oid) { struct object_id real_oid; - struct odb_read_stream *st; struct git_hash_ctx c; char hdr[MAX_HEADER_LEN]; int hdrlen; - st = odb_read_stream_open(r->objects, oid, NULL); - if (!st) - return -1; - /* Generate the header */ hdrlen = format_object_header(hdr, sizeof(hdr), st->type, st->size); @@ -160,7 +157,6 @@ int stream_object_signature(struct repository *r, const struct object_id *oid) git_hash_update(&c, buf, readlen); } git_hash_final_oid(&real_oid, &c); - odb_read_stream_close(st); return !oideq(oid, &real_oid) ? -1 : 0; } diff --git a/object-file.h b/object-file.h index 47fad6663ba5fd..ff6da6529640dc 100644 --- a/object-file.h +++ b/object-file.h @@ -166,7 +166,9 @@ int check_object_signature(struct repository *r, const struct object_id *oid, * Try reading the object named with "oid" using * the streaming interface and rehash it to do the same. */ -int stream_object_signature(struct repository *r, const struct object_id *oid); +int stream_object_signature(struct repository *r, + struct odb_read_stream *stream, + const struct object_id *oid); enum finalize_object_file_flags { FOF_SKIP_COLLISION_CHECK = 1, diff --git a/object.c b/object.c index 99b6df3780475e..465902ecc6dbb5 100644 --- a/object.c +++ b/object.c @@ -6,6 +6,7 @@ #include "object.h" #include "replace-object.h" #include "object-file.h" +#include "odb/streaming.h" #include "blob.h" #include "statinfo.h" #include "tree.h" @@ -343,9 +344,21 @@ struct object *parse_object_with_flags(struct repository *r, if ((!obj || obj->type == OBJ_NONE || obj->type == OBJ_BLOB) && odb_read_object_info(r->objects, oid, NULL) == OBJ_BLOB) { - if (!skip_hash && stream_object_signature(r, repl) < 0) { - error(_("hash mismatch %s"), oid_to_hex(oid)); - return NULL; + if (!skip_hash) { + struct odb_read_stream *stream = odb_read_stream_open(r->objects, oid, NULL); + + if (!stream) { + error(_("unable to open object stream for %s"), oid_to_hex(oid)); + return NULL; + } + + if (stream_object_signature(r, stream, repl) < 0) { + error(_("hash mismatch %s"), oid_to_hex(oid)); + odb_read_stream_close(stream); + return NULL; + } + + odb_read_stream_close(stream); } parse_blob_buffer(lookup_blob(r, oid)); return lookup_object(r, oid); diff --git a/oss-fuzz/fuzz-commit-graph.c b/oss-fuzz/fuzz-commit-graph.c index fb8b8787a460f1..59bbb849d1e1c4 100644 --- a/oss-fuzz/fuzz-commit-graph.c +++ b/oss-fuzz/fuzz-commit-graph.c @@ -10,6 +10,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { struct commit_graph *g; + memset(the_repository, 0, sizeof(*the_repository)); initialize_repository(the_repository); /* diff --git a/pack-check.c b/pack-check.c index 67cb2cf72f2b93..7378c80730d537 100644 --- a/pack-check.c +++ b/pack-check.c @@ -9,6 +9,7 @@ #include "packfile.h" #include "object-file.h" #include "odb.h" +#include "odb/streaming.h" struct idx_entry { off_t offset; @@ -104,6 +105,7 @@ static int verify_packfile(struct repository *r, QSORT(entries, nr_objects, compare_entries); for (i = 0; i < nr_objects; i++) { + struct odb_read_stream *stream = NULL; void *data; struct object_id oid; enum object_type type; @@ -152,7 +154,9 @@ static int verify_packfile(struct repository *r, type) < 0) err = error("packed %s from %s is corrupt", oid_to_hex(&oid), p->pack_name); - else if (!data && stream_object_signature(r, &oid) < 0) + else if (!data && + (packfile_read_object_stream(&stream, &oid, p, entries[i].offset) < 0 || + stream_object_signature(r, stream, &oid) < 0)) err = error("packed %s from %s is corrupt", oid_to_hex(&oid), p->pack_name); else if (fn) { @@ -163,12 +167,14 @@ static int verify_packfile(struct repository *r, } if (((base_count + i) & 1023) == 0) display_progress(progress, base_count + i); - free(data); + if (stream) + odb_read_stream_close(stream); + free(data); } + display_progress(progress, base_count + i); free(entries); - return err; } diff --git a/packfile.c b/packfile.c index ce837f852ad1f6..4a6d4a80ea4a99 100644 --- a/packfile.c +++ b/packfile.c @@ -2621,32 +2621,28 @@ static int close_istream_pack_non_delta(struct odb_read_stream *_st) return 0; } -int packfile_store_read_object_stream(struct odb_read_stream **out, - struct packfile_store *store, - const struct object_id *oid) +int packfile_read_object_stream(struct odb_read_stream **out, + const struct object_id *oid, + struct packed_git *pack, + off_t offset) { struct odb_packed_read_stream *stream; struct pack_window *window = NULL; - struct object_info oi = OBJECT_INFO_INIT; enum object_type in_pack_type; unsigned long size; - oi.sizep = &size; + in_pack_type = unpack_object_header(pack, &window, &offset, &size); + unuse_pack(&window); - if (packfile_store_read_object_info(store, oid, &oi, 0) || - oi.u.packed.type == PACKED_OBJECT_TYPE_REF_DELTA || - oi.u.packed.type == PACKED_OBJECT_TYPE_OFS_DELTA || - repo_settings_get_big_file_threshold(store->source->odb->repo) >= size) + if (repo_settings_get_big_file_threshold(pack->repo) >= size) return -1; - in_pack_type = unpack_object_header(oi.u.packed.pack, - &window, - &oi.u.packed.offset, - &size); - unuse_pack(&window); switch (in_pack_type) { default: return -1; /* we do not do deltas for now */ + case OBJ_BAD: + mark_bad_packed_object(pack, oid); + return -1; case OBJ_COMMIT: case OBJ_TREE: case OBJ_BLOB: @@ -2660,10 +2656,22 @@ int packfile_store_read_object_stream(struct odb_read_stream **out, stream->base.type = in_pack_type; stream->base.size = size; stream->z_state = ODB_PACKED_READ_STREAM_UNINITIALIZED; - stream->pack = oi.u.packed.pack; - stream->pos = oi.u.packed.offset; + stream->pack = pack; + stream->pos = offset; *out = &stream->base; return 0; } + +int packfile_store_read_object_stream(struct odb_read_stream **out, + struct packfile_store *store, + const struct object_id *oid) +{ + struct pack_entry e; + + if (!find_pack_entry(store, oid, &e)) + return -1; + + return packfile_read_object_stream(out, oid, e.p, e.offset); +} diff --git a/packfile.h b/packfile.h index 224142fd346715..85ce44f8ee88a7 100644 --- a/packfile.h +++ b/packfile.h @@ -449,6 +449,11 @@ off_t get_delta_base(struct packed_git *p, struct pack_window **w_curs, off_t *curpos, enum object_type type, off_t delta_obj_offset); +int packfile_read_object_stream(struct odb_read_stream **out, + const struct object_id *oid, + struct packed_git *pack, + off_t offset); + void release_pack_memory(size_t); /* global flag to enable extra checks when accessing packed objects */ diff --git a/repository.c b/repository.c index 44e77cd05a3287..e7fa42c14f2254 100644 --- a/repository.c +++ b/repository.c @@ -50,13 +50,27 @@ static void set_default_hash_algo(struct repository *repo) repo_set_hash_algo(repo, algo); } +struct repo_config_values *repo_config_values(struct repository *repo) +{ + if (repo != the_repository) + BUG("trying to read config from wrong repository instance"); + if (!repo->initialized) + BUG("config values from uninitialized repository"); + return &repo->config_values_private_; +} + void initialize_repository(struct repository *repo) { + if (repo->initialized) + BUG("repository initialized already"); + repo->initialized = true; + repo->remote_state = remote_state_new(); repo->parsed_objects = parsed_object_pool_new(repo); ALLOC_ARRAY(repo->index, 1); index_state_init(repo->index, repo); repo->check_deprecated_config = true; + repo_config_values_init(&repo->config_values_private_); /* * When a command runs inside a repository, it learns what diff --git a/repository.h b/repository.h index 72a5e9d410c264..9ad6520c3732fe 100644 --- a/repository.h +++ b/repository.h @@ -3,6 +3,7 @@ #include "strmap.h" #include "repo-settings.h" +#include "environment.h" struct config_set; struct git_hash_algo; @@ -148,6 +149,9 @@ struct repository { /* Repository's compatibility hash algorithm. */ const struct git_hash_algo *compat_hash_algo; + /* Repository's config values parsed by git_default_config() */ + struct repo_config_values config_values_private_; + /* Repository's reference storage format, as serialized on disk. */ enum ref_storage_format ref_storage_format; /* @@ -177,6 +181,9 @@ struct repository { /* Should repo_config() check for deprecated settings */ bool check_deprecated_config; + + /* Has this repository instance been initialized? */ + bool initialized; }; #ifdef USE_THE_REPOSITORY_VARIABLE diff --git a/sparse-index.c b/sparse-index.c index 76f90da5f5f41e..13629c075d06e0 100644 --- a/sparse-index.c +++ b/sparse-index.c @@ -152,7 +152,9 @@ static int index_has_unmerged_entries(struct index_state *istate) int is_sparse_index_allowed(struct index_state *istate, int flags) { - if (!core_apply_sparse_checkout || !core_sparse_checkout_cone) + struct repo_config_values *cfg = repo_config_values(the_repository); + + if (!cfg->apply_sparse_checkout || !core_sparse_checkout_cone) return 0; if (!(flags & SPARSE_INDEX_MEMORY_ONLY)) { @@ -670,7 +672,9 @@ static void clear_skip_worktree_from_present_files_full(struct index_state *ista void clear_skip_worktree_from_present_files(struct index_state *istate) { - if (!core_apply_sparse_checkout || + struct repo_config_values *cfg = repo_config_values(the_repository); + + if (!cfg->apply_sparse_checkout || sparse_expect_files_outside_of_patterns) return; diff --git a/t/helper/test-genrandom.c b/t/helper/test-genrandom.c index 51b67f2f874694..d681961abbbc37 100644 --- a/t/helper/test-genrandom.c +++ b/t/helper/test-genrandom.c @@ -6,6 +6,7 @@ #include "test-tool.h" #include "git-compat-util.h" +#include "parse.h" int cmd__genrandom(int argc, const char **argv) { @@ -22,7 +23,9 @@ int cmd__genrandom(int argc, const char **argv) next = next * 11 + *c; } while (*c++); - count = (argc == 3) ? strtoul(argv[2], NULL, 0) : ULONG_MAX; + count = ULONG_MAX; + if (argc == 3 && !git_parse_ulong(argv[2], &count)) + return error_errno("cannot parse argument '%s'", argv[2]); while (count--) { next = next * 1103515245 + 12345; diff --git a/t/t1006-cat-file.sh b/t/t1006-cat-file.sh index 0eee3bb8781b30..5499be8dc95559 100755 --- a/t/t1006-cat-file.sh +++ b/t/t1006-cat-file.sh @@ -643,7 +643,7 @@ test_expect_success 'object reference via commit text search' ' ' test_expect_success 'setup blobs which are likely to delta' ' - test-tool genrandom foo 10240 >foo && + test-tool genrandom foo 10k >foo && { cat foo && echo plus; } >foo-plus && git add foo foo-plus && git commit -m foo && diff --git a/t/t1050-large.sh b/t/t1050-large.sh index 5be273611ad850..7d40d0852166b5 100755 --- a/t/t1050-large.sh +++ b/t/t1050-large.sh @@ -104,9 +104,9 @@ test_expect_success 'packsize limit' ' # mid1 and mid2 will fit within 256k limit but # appending mid3 will bust the limit and will # result in a separate packfile. - test-tool genrandom "a" $(( 66 * 1024 )) >mid1 && - test-tool genrandom "b" $(( 80 * 1024 )) >mid2 && - test-tool genrandom "c" $(( 128 * 1024 )) >mid3 && + test-tool genrandom "a" 66k >mid1 && + test-tool genrandom "b" 80k >mid2 && + test-tool genrandom "c" 128k >mid3 && git add mid1 mid2 mid3 && count=0 && diff --git a/t/t1450-fsck.sh b/t/t1450-fsck.sh index 3fae05f9d9f805..54e81c263686de 100755 --- a/t/t1450-fsck.sh +++ b/t/t1450-fsck.sh @@ -852,6 +852,44 @@ test_expect_success 'fsck errors in packed objects' ' ! grep corrupt out ' +test_expect_success 'fsck handles multiple packfiles with big blobs' ' + test_when_finished "rm -rf repo" && + git init repo && + ( + cd repo && + + # We construct two packfiles with two objects in common and one + # object not in common. The objects in common can then be + # corrupted in one of the packfiles, respectively. The other + # objects that are unique to the packs are merely used to not + # have both packs contain the same data. + blob_one=$(test-tool genrandom one 200k | git hash-object -t blob -w --stdin) && + blob_two=$(test-tool genrandom two 200k | git hash-object -t blob -w --stdin) && + blob_three=$(test-tool genrandom three 200k | git hash-object -t blob -w --stdin) && + blob_four=$(test-tool genrandom four 200k | git hash-object -t blob -w --stdin) && + pack_one=$(printf "%s\n" "$blob_one" "$blob_two" "$blob_three" | git pack-objects .git/objects/pack/pack) && + pack_two=$(printf "%s\n" "$blob_two" "$blob_three" "$blob_four" | git pack-objects .git/objects/pack/pack) && + chmod a+w .git/objects/pack/pack-*.pack && + + # Corrupt blob two in the first pack. + git verify-pack -v .git/objects/pack/pack-$pack_one >objects && + offset_one=$(sed objects && + offset_two=$(sed err && + test_grep "unknown object type 0 at offset $offset_one in .git/objects/pack/pack-$pack_one.pack" err && + test_grep "unknown object type 0 at offset $offset_two in .git/objects/pack/pack-$pack_two.pack" err + ) +' + test_expect_success 'fsck fails on corrupt packfile' ' hsh=$(git commit-tree -m mycommit HEAD^{tree}) && pack=$(echo $hsh | git pack-objects .git/objects/pack/pack) && @@ -918,7 +956,7 @@ test_expect_success 'fsck detects trailing loose garbage (large blob)' ' test_expect_success 'fsck detects truncated loose object' ' # make it big enough that we know we will truncate in the data # portion, not the header - test-tool genrandom truncate 4096 >file && + test-tool genrandom truncate 4k >file && blob=$(git hash-object -w file) && file=$(sha1_file $blob) && test_when_finished "remove_object $blob" && diff --git a/t/t5301-sliding-window.sh b/t/t5301-sliding-window.sh index ff6b5159a312bb..3c3666b2781946 100755 --- a/t/t5301-sliding-window.sh +++ b/t/t5301-sliding-window.sh @@ -12,7 +12,7 @@ test_expect_success 'setup' ' for i in a b c do echo $i >$i && - test-tool genrandom "$i" 32768 >>$i && + test-tool genrandom "$i" 32k >>$i && git update-index --add $i || return 1 done && echo d >d && cat c >>d && git update-index --add d && diff --git a/t/t5310-pack-bitmaps.sh b/t/t5310-pack-bitmaps.sh index 310b708c5cce92..f693cb56691988 100755 --- a/t/t5310-pack-bitmaps.sh +++ b/t/t5310-pack-bitmaps.sh @@ -242,7 +242,7 @@ test_bitmap_cases () { ' test_expect_success 'splitting packs does not generate bogus bitmaps' ' - test-tool genrandom foo $((1024 * 1024)) >rand && + test-tool genrandom foo 1m >rand && git add rand && git commit -m "commit with big file" && git -c pack.packSizeLimit=500k repack -adb && diff --git a/t/t5710-promisor-remote-capability.sh b/t/t5710-promisor-remote-capability.sh index 532e6f0fea125b..357822c01a7530 100755 --- a/t/t5710-promisor-remote-capability.sh +++ b/t/t5710-promisor-remote-capability.sh @@ -20,7 +20,7 @@ test_expect_success 'setup: create "template" repository' ' test_commit -C template 1 && test_commit -C template 2 && test_commit -C template 3 && - test-tool genrandom foo 10240 >template/foo && + test-tool genrandom foo 10k >template/foo && git -C template add foo && git -C template commit -m foo ' @@ -499,7 +499,7 @@ test_expect_success "clone with promisor.advertise set to 'true' but don't delet test_expect_success "setup for subsequent fetches" ' # Generate new commit with large blob - test-tool genrandom bar 10240 >template/bar && + test-tool genrandom bar 10k >template/bar && git -C template add bar && git -C template commit -m bar && diff --git a/t/t7527-builtin-fsmonitor.sh b/t/t7527-builtin-fsmonitor.sh index 409cd0cd121695..e7b40654694c9e 100755 --- a/t/t7527-builtin-fsmonitor.sh +++ b/t/t7527-builtin-fsmonitor.sh @@ -408,9 +408,8 @@ move_directory() { # ensure we are getting the OS notifications and do not try to confirm what # is reported by `git status`. # -# We run a simple query after modifying the filesystem just to introduce -# a bit of a delay so that the trace logging from the daemon has time to -# get flushed to disk. +# We use retry_grep to handle races between the daemon writing events +# to the trace file and our check. # # We `reset` and `clean` at the bottom of each test (and before stopping the # daemon) because these commands might implicitly restart the daemon. @@ -422,6 +421,24 @@ clean_up_repo_and_stop_daemon () { rm -f .git/trace } +# Retry a grep up to RETRY_TIMEOUT times until it succeeds. +# +RETRY_TIMEOUT=5 + +retry_grep () { + nr_tries_left=$RETRY_TIMEOUT + until grep "$1" "$2" 2>/dev/null + do + if test $nr_tries_left -eq 0 + then + grep "$1" "$2" + return + fi + nr_tries_left=$(($nr_tries_left - 1)) + sleep 1 + done +} + test_expect_success 'edit some files' ' test_when_finished clean_up_repo_and_stop_daemon && @@ -429,12 +446,10 @@ test_expect_success 'edit some files' ' edit_files && - test-tool fsmonitor-client query --token 0 && - - grep "^event: dir1/modified$" .git/trace && - grep "^event: dir2/modified$" .git/trace && - grep "^event: modified$" .git/trace && - grep "^event: dir1/untracked$" .git/trace + retry_grep "^event: dir1/modified$" .git/trace && + retry_grep "^event: dir2/modified$" .git/trace && + retry_grep "^event: modified$" .git/trace && + retry_grep "^event: dir1/untracked$" .git/trace ' test_expect_success 'create some files' ' @@ -444,11 +459,9 @@ test_expect_success 'create some files' ' create_files && - test-tool fsmonitor-client query --token 0 && - - grep "^event: dir1/new$" .git/trace && - grep "^event: dir2/new$" .git/trace && - grep "^event: new$" .git/trace + retry_grep "^event: dir1/new$" .git/trace && + retry_grep "^event: dir2/new$" .git/trace && + retry_grep "^event: new$" .git/trace ' test_expect_success 'delete some files' ' @@ -458,11 +471,9 @@ test_expect_success 'delete some files' ' delete_files && - test-tool fsmonitor-client query --token 0 && - - grep "^event: dir1/delete$" .git/trace && - grep "^event: dir2/delete$" .git/trace && - grep "^event: delete$" .git/trace + retry_grep "^event: dir1/delete$" .git/trace && + retry_grep "^event: dir2/delete$" .git/trace && + retry_grep "^event: delete$" .git/trace ' test_expect_success 'rename some files' ' @@ -472,14 +483,12 @@ test_expect_success 'rename some files' ' rename_files && - test-tool fsmonitor-client query --token 0 && - - grep "^event: dir1/rename$" .git/trace && - grep "^event: dir2/rename$" .git/trace && - grep "^event: rename$" .git/trace && - grep "^event: dir1/renamed$" .git/trace && - grep "^event: dir2/renamed$" .git/trace && - grep "^event: renamed$" .git/trace + retry_grep "^event: dir1/rename$" .git/trace && + retry_grep "^event: dir2/rename$" .git/trace && + retry_grep "^event: rename$" .git/trace && + retry_grep "^event: dir1/renamed$" .git/trace && + retry_grep "^event: dir2/renamed$" .git/trace && + retry_grep "^event: renamed$" .git/trace ' test_expect_success 'rename directory' ' @@ -489,10 +498,8 @@ test_expect_success 'rename directory' ' mv dirtorename dirrenamed && - test-tool fsmonitor-client query --token 0 && - - grep "^event: dirtorename/*$" .git/trace && - grep "^event: dirrenamed/*$" .git/trace + retry_grep "^event: dirtorename/*$" .git/trace && + retry_grep "^event: dirrenamed/*$" .git/trace ' test_expect_success 'file changes to directory' ' @@ -502,10 +509,8 @@ test_expect_success 'file changes to directory' ' file_to_directory && - test-tool fsmonitor-client query --token 0 && - - grep "^event: delete$" .git/trace && - grep "^event: delete/new$" .git/trace + retry_grep "^event: delete$" .git/trace && + retry_grep "^event: delete/new$" .git/trace ' test_expect_success 'directory changes to a file' ' @@ -515,9 +520,7 @@ test_expect_success 'directory changes to a file' ' directory_to_file && - test-tool fsmonitor-client query --token 0 && - - grep "^event: dir1$" .git/trace + retry_grep "^event: dir1$" .git/trace ' # The next few test cases exercise the token-resync code. When filesystem diff --git a/t/t7700-repack.sh b/t/t7700-repack.sh index acc2589f212727..63ef63fc509a1d 100755 --- a/t/t7700-repack.sh +++ b/t/t7700-repack.sh @@ -321,7 +321,7 @@ test_expect_success 'no bitmaps created if .keep files present' ' test_expect_success 'auto-bitmaps do not complain if unavailable' ' test_config -C bare.git pack.packSizeLimit 1M && - blob=$(test-tool genrandom big $((1024*1024)) | + blob=$(test-tool genrandom big 1m | git -C bare.git hash-object -w --stdin) && git -C bare.git update-ref refs/tags/big $blob && @@ -497,9 +497,9 @@ test_expect_success '--filter works with --max-pack-size' ' cd max-pack-size && test_commit base && # two blobs which exceed the maximum pack size - test-tool genrandom foo 1048576 >foo && + test-tool genrandom foo 1m >foo && git hash-object -w foo && - test-tool genrandom bar 1048576 >bar && + test-tool genrandom bar 1m >bar && git hash-object -w bar && git add foo bar && git commit -m "adding foo and bar" diff --git a/unpack-trees.c b/unpack-trees.c index f38c761ab987a6..998a1e6dc70cae 100644 --- a/unpack-trees.c +++ b/unpack-trees.c @@ -1888,6 +1888,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options struct pattern_list pl; int free_pattern_list = 0; struct dir_struct dir = DIR_INIT; + struct repo_config_values *cfg = repo_config_values(the_repository); if (o->reset == UNPACK_RESET_INVALID) BUG("o->reset had a value of 1; should be UNPACK_TREES_*_UNTRACKED"); @@ -1924,7 +1925,7 @@ int unpack_trees(unsigned len, struct tree_desc *t, struct unpack_trees_options if (o->prefix) update_sparsity_for_prefix(o->prefix, o->src_index); - if (!core_apply_sparse_checkout || !o->update) + if (!cfg->apply_sparse_checkout || !o->update) o->skip_sparse_checkout = 1; if (!o->skip_sparse_checkout) { memset(&pl, 0, sizeof(pl)); diff --git a/wt-status.c b/wt-status.c index 68257d6dfd2957..543000311fd615 100644 --- a/wt-status.c +++ b/wt-status.c @@ -1793,8 +1793,10 @@ static void wt_status_check_sparse_checkout(struct repository *r, { int skip_worktree = 0; int i; + struct repo_config_values *cfg = repo_config_values(the_repository); - if (!core_apply_sparse_checkout || r->index->cache_nr == 0) { + if (!cfg->apply_sparse_checkout || + r->index->cache_nr == 0) { /* * Don't compute percentage of checked out files if we * aren't in a sparse checkout or would get division by 0.