From: Junio C Hamano Date: Thu, 28 Mar 2024 21:13:50 +0000 (-0700) Subject: Merge branch 'eb/hash-transition' X-Git-Tag: v2.45.0-rc0~75 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=1002f28a527d33893f7dab068dbac7011f84af65;p=thirdparty%2Fgit.git Merge branch 'eb/hash-transition' Work to support a repository that work with both SHA-1 and SHA-256 hash algorithms has started. * eb/hash-transition: (30 commits) t1016-compatObjectFormat: add tests to verify the conversion between objects t1006: test oid compatibility with cat-file t1006: rename sha1 to oid test-lib: compute the compatibility hash so tests may use it builtin/ls-tree: let the oid determine the output algorithm object-file: handle compat objects in check_object_signature tree-walk: init_tree_desc take an oid to get the hash algorithm builtin/cat-file: let the oid determine the output algorithm rev-parse: add an --output-object-format parameter repository: implement extensions.compatObjectFormat object-file: update object_info_extended to reencode objects object-file-convert: convert commits that embed signed tags object-file-convert: convert commit objects when writing object-file-convert: don't leak when converting tag objects object-file-convert: convert tag objects when writing object-file-convert: add a function to convert trees between algorithms object: factor out parse_mode out of fast-import and tree-walk into in object.h cache: add a function to read an OID of a specific algorithm tag: sign both hashes commit: export add_header_signature to support handling signatures on tags ... --- 1002f28a527d33893f7dab068dbac7011f84af65 diff --cc Documentation/config/extensions.txt index 66db0e15da,9f72e6d9f4..38dce3df35 --- a/Documentation/config/extensions.txt +++ b/Documentation/config/extensions.txt @@@ -7,17 -7,18 +7,29 @@@ Note that this setting should only be s linkgit:git-clone[1]. Trying to change it after initialization will not work and will produce hard-to-diagnose issues. + extensions.compatObjectFormat:: + + Specify a compatitbility hash algorithm to use. The acceptable values + are `sha1` and `sha256`. The value specified must be different from the + value of extensions.objectFormat. This allows client level + interoperability between git repositories whose objectFormat matches + this compatObjectFormat. In particular when fully implemented the + pushes and pulls from a repository in whose objectFormat matches + compatObjectFormat. As well as being able to use oids encoded in + compatObjectFormat in addition to oids encoded with objectFormat to + locally specify objects. + +extensions.refStorage:: + Specify the ref storage format to use. The acceptable values are: ++ +include::../ref-storage-format.txt[] ++ +It is an error to specify this key unless `core.repositoryFormatVersion` is 1. ++ +Note that this setting should only be set by linkgit:git-init[1] or +linkgit:git-clone[1]. Trying to change it after initialization will not +work and will produce hard-to-diagnose issues. + extensions.worktreeConfig:: If enabled, then worktrees will load config settings from the `$GIT_DIR/config.worktree` file in addition to the diff --cc Documentation/git-rev-parse.txt index 5d83dd36da,f0f9021f2a..f9d5a35fa0 --- a/Documentation/git-rev-parse.txt +++ b/Documentation/git-rev-parse.txt @@@ -156,9 -156,21 +156,21 @@@ for another option are not refs (i.e. branch or tag names; or more explicitly disambiguating "heads/master" form, when you want to name the "master" branch when there is an - unfortunately named tag "master"), and show them as full + unfortunately named tag "master"), and shows them as full refnames (e.g. "refs/heads/master"). + --output-object-format=(sha1|sha256|storage):: + + Allow oids to be input from any object format that the current + repository supports. + + Specifying "sha1" translates if necessary and returns a sha1 oid. + + Specifying "sha256" translates if necessary and returns a sha256 oid. + + Specifying "storage" translates if necessary and returns an oid in + encoded in the storage hash algorithm. + Options for Objects ~~~~~~~~~~~~~~~~~~~ diff --cc Makefile index 4e255c81f2,3e4444fb9a..e955678908 --- a/Makefile +++ b/Makefile @@@ -796,7 -788,9 +796,8 @@@ TEST_BUILTINS_OBJS += test-chmtime. TEST_BUILTINS_OBJS += test-config.o TEST_BUILTINS_OBJS += test-crontab.o TEST_BUILTINS_OBJS += test-csprng.o -TEST_BUILTINS_OBJS += test-ctype.o TEST_BUILTINS_OBJS += test-date.o + TEST_BUILTINS_OBJS += test-delete-gpgsig.o TEST_BUILTINS_OBJS += test-delta.o TEST_BUILTINS_OBJS += test-dir-iterator.o TEST_BUILTINS_OBJS += test-drop-caches.o diff --cc builtin/cat-file.c index bbf851138e,e615d1f8e0..3f5ce7d34c --- a/builtin/cat-file.c +++ b/builtin/cat-file.c @@@ -221,12 -225,9 +224,13 @@@ static int cat_one_file(int opt, const &type, &size); const char *target; + + if (!buffer) + die(_("unable to read %s"), oid_to_hex(&oid)); + if (!skip_prefix(buffer, "object ", &target) || - get_oid_hex(target, &blob_oid)) + get_oid_hex_algop(target, &blob_oid, + &hash_algos[oid.algo])) die("%s not a valid tag", oid_to_hex(&oid)); free(buffer); } else diff --cc builtin/checkout.c index 902c97ab23,03eff73fd0..2e8b0d18f4 --- a/builtin/checkout.c +++ b/builtin/checkout.c @@@ -704,9 -700,8 +704,9 @@@ static int reset_tree(struct tree *tree init_checkout_metadata(&opts.meta, info->refname, info->commit ? &info->commit->object.oid : null_oid(), NULL); - parse_tree(tree); + if (parse_tree(tree) < 0) + return 128; - init_tree_desc(&tree_desc, tree->buffer, tree->size); + init_tree_desc(&tree_desc, &tree->object.oid, tree->buffer, tree->size); switch (unpack_trees(1, &tree_desc, &opts)) { case -2: *writeout_error = 1; @@@ -826,11 -815,12 +826,13 @@@ static int merge_working_tree(const str die(_("unable to parse commit %s"), oid_to_hex(old_commit_oid)); - init_tree_desc(&trees[0], tree->buffer, tree->size); + init_tree_desc(&trees[0], &tree->object.oid, + tree->buffer, tree->size); - parse_tree(new_tree); + if (parse_tree(new_tree) < 0) + exit(128); tree = new_tree; - init_tree_desc(&trees[1], tree->buffer, tree->size); + init_tree_desc(&trees[1], &tree->object.oid, + tree->buffer, tree->size); ret = unpack_trees(2, trees, &topts); clear_unpack_trees_porcelain(&topts); diff --cc builtin/clone.c index f3bc6edef8,79ceefb939..74ec14542e --- a/builtin/clone.c +++ b/builtin/clone.c @@@ -738,9 -736,8 +738,9 @@@ static int checkout(int submodule_progr tree = parse_tree_indirect(&oid); if (!tree) die(_("unable to parse commit %s"), oid_to_hex(&oid)); - parse_tree(tree); + if (parse_tree(tree) < 0) + exit(128); - init_tree_desc(&t, tree->buffer, tree->size); + init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size); if (unpack_trees(1, &t, &opts) < 0) die(_("unable to checkout working tree")); diff --cc builtin/commit.c index a91197245f,537319932b..f5a5a0ec53 --- a/builtin/commit.c +++ b/builtin/commit.c @@@ -331,9 -339,8 +331,9 @@@ static void create_base_index(const str tree = parse_tree_indirect(¤t_head->object.oid); if (!tree) die(_("failed to unpack HEAD tree object")); - parse_tree(tree); + if (parse_tree(tree) < 0) + exit(128); - init_tree_desc(&t, tree->buffer, tree->size); + init_tree_desc(&t, &tree->object.oid, tree->buffer, tree->size); if (unpack_trees(1, &t, &opts)) exit(128); /* We've already reported the error, finish dying */ } diff --cc builtin/grep.c index 982bcfc4b1,0c2b8a376f..5777ba82a9 --- a/builtin/grep.c +++ b/builtin/grep.c @@@ -571,9 -574,7 +571,9 @@@ static int grep_cache(struct grep_opt * data = repo_read_object_file(the_repository, &ce->oid, &type, &size); + if (!data) + die(_("unable to read tree %s"), oid_to_hex(&ce->oid)); - init_tree_desc(&tree, data, size); + init_tree_desc(&tree, &ce->oid, data, size); hit |= grep_tree(opt, pathspec, &tree, &name, 0, 0); strbuf_setlen(&name, name_base_len); diff --cc builtin/read-tree.c index 1ffd863cff,24d6d156d3..6f89cec0fb --- a/builtin/read-tree.c +++ b/builtin/read-tree.c @@@ -261,9 -263,8 +261,9 @@@ int cmd_read_tree(int argc, const char cache_tree_free(&the_index.cache_tree); for (i = 0; i < nr_trees; i++) { struct tree *tree = trees[i]; - parse_tree(tree); + if (parse_tree(tree) < 0) + return 128; - init_tree_desc(t+i, tree->buffer, tree->size); + init_tree_desc(t+i, &tree->object.oid, tree->buffer, tree->size); } if (unpack_trees(nr_trees, t, &opts)) return 128; diff --cc commit.c index 467be9f7f9,2b61a4d0aa..1da10ec916 --- a/commit.c +++ b/commit.c @@@ -26,7 -28,7 +26,8 @@@ #include "shallow.h" #include "tree.h" #include "hook.h" +#include "parse.h" + #include "object-file-convert.h" static struct commit_extra_header *read_commit_extra_header_lines(const char *buf, size_t len, const char **); diff --cc merge-ort.c index 201f8f7775,3a5729c91e..ac225cc33c --- a/merge-ort.c +++ b/merge-ort.c @@@ -1661,13 -1676,13 +1661,14 @@@ static int collect_merge_info(struct me info.data = opt; info.show_all_errors = 1; - parse_tree(merge_base); - parse_tree(side1); - parse_tree(side2); + if (parse_tree(merge_base) < 0 || + parse_tree(side1) < 0 || + parse_tree(side2) < 0) + return -1; - init_tree_desc(t + 0, merge_base->buffer, merge_base->size); - init_tree_desc(t + 1, side1->buffer, side1->size); - init_tree_desc(t + 2, side2->buffer, side2->size); + init_tree_desc(t + 0, &merge_base->object.oid, + merge_base->buffer, merge_base->size); + init_tree_desc(t + 1, &side1->object.oid, side1->buffer, side1->size); + init_tree_desc(t + 2, &side2->object.oid, side2->buffer, side2->size); trace2_region_enter("merge", "traverse_trees", opt->repo); ret = traverse_trees(NULL, 3, t, &info); @@@ -4444,12 -4400,10 +4445,12 @@@ static int checkout(struct merge_option unpack_opts.verbose_update = (opt->verbosity > 2); unpack_opts.fn = twoway_merge; unpack_opts.preserve_ignored = 0; /* FIXME: !opts->overwrite_ignore */ - parse_tree(prev); + if (parse_tree(prev) < 0) + return -1; - init_tree_desc(&trees[0], prev->buffer, prev->size); + init_tree_desc(&trees[0], &prev->object.oid, prev->buffer, prev->size); - parse_tree(next); + if (parse_tree(next) < 0) + return -1; - init_tree_desc(&trees[1], next->buffer, next->size); + init_tree_desc(&trees[1], &next->object.oid, next->buffer, next->size); ret = unpack_trees(2, trees, &unpack_opts); clear_unpack_trees_porcelain(&unpack_opts); diff --cc merge-recursive.c index 103ee321ae,93df9eecdd..69d67bef5a --- a/merge-recursive.c +++ b/merge-recursive.c @@@ -405,9 -410,8 +405,9 @@@ static inline int merge_detect_rename(s static void init_tree_desc_from_tree(struct tree_desc *desc, struct tree *tree) { - parse_tree(tree); + if (parse_tree(tree) < 0) + exit(128); - init_tree_desc(desc, tree->buffer, tree->size); + init_tree_desc(desc, &tree->object.oid, tree->buffer, tree->size); } static int unpack_trees_start(struct merge_options *opt, diff --cc merge.c index 563281b10f,86179c3410..752a937fa9 --- a/merge.c +++ b/merge.c @@@ -77,11 -80,9 +77,12 @@@ int checkout_fast_forward(struct reposi return -1; } for (i = 0; i < nr_trees; i++) { - parse_tree(trees[i]); + if (parse_tree(trees[i]) < 0) { + rollback_lock_file(&lock_file); + return -1; + } - init_tree_desc(t+i, trees[i]->buffer, trees[i]->size); + init_tree_desc(t+i, &trees[i]->object.oid, + trees[i]->buffer, trees[i]->size); } memset(&opts, 0, sizeof(opts)); diff --cc repository.c index 7aacb51b65,9d91536b61..e15b416944 --- a/repository.c +++ b/repository.c @@@ -104,11 -105,15 +105,20 @@@ void repo_set_hash_algo(struct reposito repo->hash_algo = &hash_algos[hash_algo]; } + void repo_set_compat_hash_algo(struct repository *repo, int algo) + { + if (hash_algo_by_ptr(repo->hash_algo) == algo) + BUG("hash_algo and compat_hash_algo match"); + repo->compat_hash_algo = algo ? &hash_algos[algo] : NULL; + if (repo->compat_hash_algo) + repo_read_loose_object_map(repo); + } + +void repo_set_ref_storage_format(struct repository *repo, unsigned int format) +{ + repo->ref_storage_format = format; +} + /* * Attempt to resolve and set the provided 'gitdir' for repository 'repo'. * Return 0 upon success and a non-zero value upon failure. @@@ -189,7 -194,7 +199,8 @@@ int repo_init(struct repository *repo goto error; repo_set_hash_algo(repo, format.hash_algo); + repo_set_compat_hash_algo(repo, format.compat_hash_algo); + repo_set_ref_storage_format(repo, format.ref_storage_format); repo->repository_format_worktree_config = format.worktree_config; /* take ownership of format.partial_clone */ diff --cc repository.h index 9bf1e33d25,bf3fc601cc..268436779c --- a/repository.h +++ b/repository.h @@@ -163,9 -160,9 +163,12 @@@ struct repository /* Repository's current hash algorithm, as serialized on disk. */ const struct git_hash_algo *hash_algo; + /* Repository's compatibility hash algorithm. */ + const struct git_hash_algo *compat_hash_algo; + + /* Repository's reference storage format, as serialized on disk. */ + unsigned int ref_storage_format; + /* A unique-id for tracing purposes. */ int trace2_repo_id; @@@ -205,7 -202,7 +208,8 @@@ void repo_set_gitdir(struct repository const struct set_gitdir_args *extra_args); void repo_set_worktree(struct repository *repo, const char *path); void repo_set_hash_algo(struct repository *repo, int algo); + void repo_set_compat_hash_algo(struct repository *repo, int compat_algo); +void repo_set_ref_storage_format(struct repository *repo, unsigned int format); void initialize_the_repository(void); RESULT_MUST_BE_USED int repo_init(struct repository *r, const char *gitdir, const char *worktree); diff --cc setup.c index 0b798591c0,85259a259b..f4b32f76e3 --- a/setup.c +++ b/setup.c @@@ -591,17 -590,25 +591,36 @@@ static enum extension_result handle_ext "extensions.objectformat", value); data->hash_algo = format; return EXTENSION_OK; + } else if (!strcmp(ext, "compatobjectformat")) { + struct string_list_item *item; + int format; + + if (!value) + return config_error_nonbool(var); + format = hash_algo_by_name(value); + if (format == GIT_HASH_UNKNOWN) + return error(_("invalid value for '%s': '%s'"), + "extensions.compatobjectformat", value); + /* For now only support compatObjectFormat being specified once. */ + for_each_string_list_item(item, &data->v1_only_extensions) { + if (!strcmp(item->string, "compatobjectformat")) + return error(_("'%s' already specified as '%s'"), + "extensions.compatobjectformat", + hash_algos[data->compat_hash_algo].name); + } + data->compat_hash_algo = format; + return EXTENSION_OK; + } else if (!strcmp(ext, "refstorage")) { + unsigned int format; + + if (!value) + return config_error_nonbool(var); + format = ref_storage_format_by_name(value); + if (format == REF_STORAGE_FORMAT_UNKNOWN) + return error(_("invalid value for '%s': '%s'"), + "extensions.refstorage", value); + data->ref_storage_format = format; + return EXTENSION_OK; } return EXTENSION_UNKNOWN; } @@@ -1603,8 -1583,8 +1622,10 @@@ const char *setup_git_directory_gently( } if (startup_info->have_repository) { repo_set_hash_algo(the_repository, repo_fmt.hash_algo); + repo_set_compat_hash_algo(the_repository, + repo_fmt.compat_hash_algo); + repo_set_ref_storage_format(the_repository, + repo_fmt.ref_storage_format); the_repository->repository_format_worktree_config = repo_fmt.worktree_config; /* take ownership of repo_fmt.partial_clone */ @@@ -1698,8 -1678,7 +1719,9 @@@ void check_repository_format(struct rep check_repository_format_gently(get_git_dir(), fmt, NULL); startup_info->have_repository = 1; repo_set_hash_algo(the_repository, fmt->hash_algo); + repo_set_compat_hash_algo(the_repository, fmt->compat_hash_algo); + repo_set_ref_storage_format(the_repository, + fmt->ref_storage_format); the_repository->repository_format_worktree_config = fmt->worktree_config; the_repository->repository_format_partial_clone = diff --cc setup.h index 3599aec93c,5d678ceb8c..d88bb37aaf --- a/setup.h +++ b/setup.h @@@ -115,7 -86,7 +115,8 @@@ struct repository_format int worktree_config; int is_bare; int hash_algo; + int compat_hash_algo; + unsigned int ref_storage_format; int sparse_index; char *work_tree; struct string_list unknown_extensions; diff --cc t/helper/test-tool.c index 482a1e58a4,8b6c84f202..80a946b847 --- a/t/helper/test-tool.c +++ b/t/helper/test-tool.c @@@ -19,7 -19,9 +19,8 @@@ static struct test_cmd cmds[] = { "config", cmd__config }, { "crontab", cmd__crontab }, { "csprng", cmd__csprng }, - { "ctype", cmd__ctype }, { "date", cmd__date }, + { "delete-gpgsig", cmd__delete_gpgsig }, { "delta", cmd__delta }, { "dir-iterator", cmd__dir_iterator }, { "drop-caches", cmd__drop_caches }, diff --cc t/helper/test-tool.h index b1be7cfcf5,76baaece35..2808b92419 --- a/t/helper/test-tool.h +++ b/t/helper/test-tool.h @@@ -12,8 -12,10 +12,9 @@@ int cmd__chmtime(int argc, const char * int cmd__config(int argc, const char **argv); int cmd__crontab(int argc, const char **argv); int cmd__csprng(int argc, const char **argv); -int cmd__ctype(int argc, const char **argv); int cmd__date(int argc, const char **argv); int cmd__delta(int argc, const char **argv); + int cmd__delete_gpgsig(int argc, const char **argv); int cmd__dir_iterator(int argc, const char **argv); int cmd__drop_caches(int argc, const char **argv); int cmd__dump_cache_tree(int argc, const char **argv); diff --cc t/test-lib-functions.sh index 6eaf116346,92b462e2e7..2eccf100c0 --- a/t/test-lib-functions.sh +++ b/t/test-lib-functions.sh @@@ -1655,14 -1599,18 +1655,23 @@@ test_set_hash () # Detect the hash algorithm in use. test_detect_hash () { - test_hash_algo="${GIT_TEST_DEFAULT_HASH:-sha1}" + case "$GIT_TEST_DEFAULT_HASH" in + "sha256") + test_hash_algo=sha256 + test_compat_hash_algo=sha1 + ;; + *) + test_hash_algo=sha1 + test_compat_hash_algo=sha256 + ;; + esac } +# Detect the hash algorithm in use. +test_detect_ref_format () { + echo "${GIT_TEST_DEFAULT_REF_FORMAT:-files}" +} + # Load common hash metadata and common placeholder object IDs for use with # test_oid. test_oid_init () { diff --cc tree-walk.c index 690fa6569b,0b44ec7c75..6565d9ad99 --- a/tree-walk.c +++ b/tree-walk.c @@@ -9,25 -9,7 +9,8 @@@ #include "tree.h" #include "pathspec.h" #include "json-writer.h" +#include "environment.h" - static const char *get_mode(const char *str, unsigned int *modep) - { - unsigned char c; - unsigned int mode = 0; - - if (*str == ' ') - return NULL; - - while ((c = *str++) != ' ') { - if (c < '0' || c > '7') - return NULL; - mode = (mode << 3) + (c - '0'); - } - *modep = mode; - return str; - } - static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned long size, struct strbuf *err) { const char *path; @@@ -100,9 -89,9 +90,9 @@@ void *fill_tree_descriptor(struct repos if (oid) { buf = read_object_with_reference(r, oid, OBJ_TREE, &size, NULL); if (!buf) - die("unable to read tree %s", oid_to_hex(oid)); + die(_("unable to read tree (%s)"), oid_to_hex(oid)); } - init_tree_desc(desc, buf, size); + init_tree_desc(desc, oid, buf, size); return buf; }