{"thread":{"id":"63675","subject":"[PATCH 01/10] hash: add a constant for the default hash algorithm","startedAt":"2025-06-20T01:19:55Z","lastAt":"2025-07-02T15:08:24Z","messageCount":47,"participants":["brian m. carlson","Junio C Hamano","Patrick Steinhardt","Kristoffer Haugsbakk"],"isPatch":true,"patchVersion":1,"patchTotal":10},"messages":[{"id":"520449","messageId":"20250620011943.586596-2-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250620011943.586596-1-sandals@crustytoothpaste.net","subject":"[PATCH 01/10] hash: add a constant for the default hash algorithm","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-06-20T01:19:33Z","receivedAt":"2025-06-20T01:19:55Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Right now, SHA-1 is the default hash algorithm in Git.  However, this\nmay change in the future.\n\nWe have many places in our code that use the SHA-1 constant to indicate\nthe default hash if none is specified, but it will end up being more\npractical to specify this explicitly and clearly using a constant for\nwhatever the default hash algorithm is.  Then, if we decide to change it\nin the future, we can simply replace the constant representing the\ndefault with a new value.\n\nFor these reasons, introduce GIT_HASH_DEFAULT to represent the default\nhash algorithm.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n hash.h | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/hash.h b/hash.h\nindex d6422ddf45..0d3d85e04c 100644\n--- a/hash.h\n+++ b/hash.h\n@@ -174,6 +174,8 @@ static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *s\n #define GIT_HASH_SHA256 2\n /* Number of algorithms supported (including unknown). */\n #define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)\n+/* Default hash algorithm if unspecified. */\n+#define GIT_HASH_DEFAULT GIT_HASH_SHA1\n \n /* \"sha1\", big-endian */\n #define GIT_SHA1_FORMAT_ID 0x73686131\n"},{"id":"520450","messageId":"20250620011943.586596-4-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250620011943.586596-1-sandals@crustytoothpaste.net","subject":"[PATCH 03/10] builtin: use default hash when outside a repository","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-06-20T01:19:35Z","receivedAt":"2025-06-20T01:19:55Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"We have some commands that can operate inside or outside a repository.\nIf we're operating outside a repository, we clearly cannot use the\nrepository's hash algorithm as a default since it doesn't exist, so\ninstead, let's pick the default instead of specifically SHA-1.  Right\nnow this results in no functional change since the default is SHA-1, but\nthat may change in the future.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n builtin/apply.c       | 2 +-\n builtin/diff.c        | 2 +-\n builtin/hash-object.c | 2 +-\n builtin/index-pack.c  | 2 +-\n builtin/ls-remote.c   | 2 +-\n builtin/patch-id.c    | 2 +-\n builtin/shortlog.c    | 2 +-\n builtin/show-index.c  | 2 +-\n 8 files changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex a1e20c593d..d642a40251 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -29,7 +29,7 @@ int cmd_apply(int argc,\n \t * cf. https://lore.kernel.org/git/xmqqcypfcmn4.fsf@gitster.g/\n \t */\n \tif (!the_hash_algo)\n-\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n+\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n \n \targc = apply_parse_options(argc, argv,\n \t\t\t\t   &state, &force_apply, &options,\ndiff --git a/builtin/diff.c b/builtin/diff.c\nindex c6231edce4..eebffe36cc 100644\n--- a/builtin/diff.c\n+++ b/builtin/diff.c\n@@ -483,7 +483,7 @@ int cmd_diff(int argc,\n \t * configurable via a command line option.\n \t */\n \tif (nongit)\n-\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n+\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n \n \tinit_diff_ui_defaults();\n \tgit_config(git_diff_ui_config, NULL);\ndiff --git a/builtin/hash-object.c b/builtin/hash-object.c\nindex 6a99ec250d..213a302e05 100644\n--- a/builtin/hash-object.c\n+++ b/builtin/hash-object.c\n@@ -104,7 +104,7 @@ int cmd_hash_object(int argc,\n \t\tprefix = setup_git_directory_gently(&nongit);\n \n \tif (nongit && !the_hash_algo)\n-\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n+\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n \n \tif (vpath && prefix) {\n \t\tvpath_free = prefix_filename(prefix, vpath);\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex bb7925bd29..352ce7f88a 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -2034,7 +2034,7 @@ int cmd_index_pack(int argc,\n \t * choice but to guess the object hash.\n \t */\n \tif (!the_repository->hash_algo)\n-\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n+\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n \n \topts.flags &= ~(WRITE_REV | WRITE_REV_VERIFY);\n \tif (rev_index) {\ndiff --git a/builtin/ls-remote.c b/builtin/ls-remote.c\nindex 01a4d4daa1..df09000b30 100644\n--- a/builtin/ls-remote.c\n+++ b/builtin/ls-remote.c\n@@ -112,7 +112,7 @@ int cmd_ls_remote(int argc,\n \t * depending on what object hash the remote uses.\n \t */\n \tif (!the_repository->hash_algo)\n-\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n+\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n \n \tpacket_trace_identity(\"ls-remote\");\n \ndiff --git a/builtin/patch-id.c b/builtin/patch-id.c\nindex cdef2ec10a..26f04b0335 100644\n--- a/builtin/patch-id.c\n+++ b/builtin/patch-id.c\n@@ -254,7 +254,7 @@ int cmd_patch_id(int argc,\n \t * the code that computes patch IDs to always use SHA1.\n \t */\n \tif (!the_hash_algo)\n-\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n+\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n \n \tgenerate_id_list(opts ? opts > 1 : config.stable,\n \t\t\t opts ? opts == 3 : config.verbatim);\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex fe15e11497..60adc5e7a5 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -419,7 +419,7 @@ int cmd_shortlog(int argc,\n \t * git/nongit so that we do not have to do this.\n \t */\n \tif (nongit && !the_hash_algo)\n-\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n+\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n \n \tgit_config(git_default_config, NULL);\n \tshortlog_init(&log);\ndiff --git a/builtin/show-index.c b/builtin/show-index.c\nindex 9d4ecf5e7b..2c3e2940ce 100644\n--- a/builtin/show-index.c\n+++ b/builtin/show-index.c\n@@ -47,7 +47,7 @@ int cmd_show_index(int argc,\n \t *       the index file passed in and use that instead.\n \t */\n \tif (!the_hash_algo)\n-\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n+\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n \n \thashsz = the_hash_algo->rawsz;\n \n"},{"id":"520451","messageId":"20250620011943.586596-1-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":null,"subject":"[PATCH 00/10] Add SHA-256 by default as a breaking change","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-06-20T01:19:32Z","receivedAt":"2025-06-20T01:19:55Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Our breaking changes document outlines that Git 3.0 will make SHA-256\nthe default hash algorithm, which is a sensible and prudent approach,\nespecially from a security perspective.  However, we haven't tested this\nadequately and it would be helpful to allow users to test this behaviour\nso their code and environments are ready for it.\n\nFortunately, c5bc9a7f94 (Makefile: wire up build option for deprecated\nfeatures, 2025-01-22) introduces a build option that we can use for\ntesting breaking changes: WITH_BREAKING_CHANGES.\n\nThis series first introduces two new hash constants: one for the default\nhash algorithm and one for the original algorithm (SHA-1).  The utility\nof the former should be obvious; the latter[0] exists to allow us to\nclearly document those places in the code that we are using SHA-1\nbecause older versions of Git did not support SHA-1 and we are\nmaintaining backward compatibility, as opposed to those where SHA-1 is\nan intentional and deliberate choice (such as when a file format allows\nthe user specify an algorithm).  We then have several patches that use\nthese constants throughout our code; fortunately, the need for them is\nlimited due to provident design decisions.\n\nThere is then a small amount of code to adjust the setup code to use the\ndefault hash and adjust fallbacks to SHA-1 where necessary.\n\nWe then set up the testsuite to support a default algorithm.  It is\ncalled the built-in algorithm since \"default\" is already used for the\nalgorithm we're running in the testsuite.  There are then a few tests\nthat need fixing.  Again, there aren't too many thanks to good decisions\non the part of contributors.\n\nFinally, we wire up SHA-256 by default when WITH_BREAKING_CHANGES is\nset with a rather anticlimactic amount of code.  All in all, this series\nwas far less work and far smaller than I expected.  Many thanks to\neveryone who contributed to that being the case.\n\nI have tested this series not only on CI but three different ways on my\nlocal machine: without any GIT_TEST_DEFAULT_HASH, with that option set\nto \"sha256\", and with that option set to \"sha1\".  It passed in every\ncase and all three options were useful in finding bugs in this series.\n\nThere was some discussion about adding support for SHA-256\ninteroperability for Git 3.0 as well.  Since my talk on that for Git\nMerge was accepted, I've started on some of that work so the talk can\nhave more concrete and less handwavy content.\n\nOver the past week, I've gotten down from 133 to 96 failing tests in\ncompatibility mode, which is definitely an improvement.  To be clear: I\nam not committing to finishing that work 100%[1], but I am at least\nworking on it right now and it is at least mostly functional if you\nignore submodules, partial clone, and shallow clone.  My incentive to\nwork on it has also been helped by a new Framework 13 laptop that can\nrun the testsuite with -j12 in 65-70 seconds without making the\nremainder of the system laggy, so testing is less painful and the\nfeedback loop is much shorter.\n\n[0] This is, in my view, the most controversial part of the series, but\nI think it's nice for documentary purposes.  If people really hate it,\nwe can drop it.\n[1] I had previously said I wasn't going to do it at all, and clearly I\nlied, but since this is in my free time, I may decide to do something\nelse instead at some point.  I'll make the code available for people to\nwork with if I don't get a chance to send all of the patches.\n\nbrian m. carlson (10):\n  hash: add a constant for the default hash algorithm\n  hash: add a constant for the original hash algorithm\n  builtin: use default hash when outside a repository\n  Use original hash for legacy formats\n  setup: use the default algorithm to initialize repo format\n  t: default to compile-time default hash if not set\n  t1007: choose the built-in hash outside of a repo\n  t4042: choose the built-in hash outside of a repo\n  t5300: choose the built-in hash outside of a repo\n  Enable SHA-256 by default in breaking changes mode\n\n builtin/apply.c                  |  2 +-\n builtin/diff.c                   |  2 +-\n builtin/hash-object.c            |  2 +-\n builtin/index-pack.c             |  2 +-\n builtin/ls-remote.c              |  2 +-\n builtin/patch-id.c               |  2 +-\n builtin/receive-pack.c           |  2 +-\n builtin/shortlog.c               |  2 +-\n builtin/show-index.c             |  2 +-\n bundle.c                         |  4 ++--\n connect.c                        |  6 +++---\n fetch-pack.c                     |  2 +-\n hash.h                           | 10 ++++++++++\n pkt-line.c                       |  2 +-\n remote-curl.c                    |  2 +-\n serve.c                          |  2 +-\n setup.c                          |  9 ++++++---\n setup.h                          |  2 +-\n t/t1007-hash-object.sh           |  4 ++--\n t/t4042-diff-textconv-caching.sh | 12 ++++++++++--\n t/t5300-pack-object.sh           |  6 +++---\n t/test-lib-functions.sh          |  5 ++++-\n t/test-lib.sh                    | 12 +++++++++++-\n transport.c                      |  2 +-\n 24 files changed, 66 insertions(+), 32 deletions(-)\n\n"},{"id":"520452","messageId":"20250620011943.586596-5-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250620011943.586596-1-sandals@crustytoothpaste.net","subject":"[PATCH 04/10] Use original hash for legacy formats","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-06-20T01:19:36Z","receivedAt":"2025-06-20T01:19:55Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"We have a large variety of data formats and protocols where no hash\nalgorithm was defined and the default was assumed to always be SHA-1.\nInstead of explicitly stating SHA-1, let's use the constant to represent\nthe original hash algorithm (which is still SHA-1) so that it's clear\nfor documentary purposes that it's a legacy fallback option and not an\nintentional choice to use SHA-1.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n builtin/receive-pack.c | 2 +-\n bundle.c               | 4 ++--\n connect.c              | 6 +++---\n fetch-pack.c           | 2 +-\n pkt-line.c             | 2 +-\n remote-curl.c          | 2 +-\n serve.c                | 2 +-\n setup.c                | 4 ++--\n transport.c            | 2 +-\n 9 files changed, 13 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex a317d6c278..6c9b246895 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -2136,7 +2136,7 @@ static struct command *read_head_info(struct packet_reader *reader,\n \t\t\t\tuse_push_options = 1;\n \t\t\thash = parse_feature_value(feature_list, \"object-format\", &len, NULL);\n \t\t\tif (!hash) {\n-\t\t\t\thash = hash_algos[GIT_HASH_SHA1].name;\n+\t\t\t\thash = hash_algos[GIT_HASH_ORIGINAL].name;\n \t\t\t\tlen = strlen(hash);\n \t\t\t}\n \t\t\tif (xstrncmpz(the_hash_algo->name, hash, len))\ndiff --git a/bundle.c b/bundle.c\nindex b0a3fee2ef..359cbc44d0 100644\n--- a/bundle.c\n+++ b/bundle.c\n@@ -95,7 +95,7 @@ int read_bundle_header_fd(int fd, struct bundle_header *header,\n \t * by an \"object-format=\" capability, which is being handled in\n \t * `parse_capability()`.\n \t */\n-\theader->hash_algo = &hash_algos[GIT_HASH_SHA1];\n+\theader->hash_algo = &hash_algos[GIT_HASH_ORIGINAL];\n \n \t/* The bundle header ends with an empty line */\n \twhile (!strbuf_getwholeline_fd(&buf, fd, '\\n') &&\n@@ -507,7 +507,7 @@ int create_bundle(struct repository *r, const char *path,\n \t *    SHA1.\n \t * 2. @filter is required because we parsed an object filter.\n \t */\n-\tif (the_hash_algo != &hash_algos[GIT_HASH_SHA1] || revs.filter.choice)\n+\tif (the_hash_algo != &hash_algos[GIT_HASH_ORIGINAL] || revs.filter.choice)\n \t\tmin_version = 3;\n \n \tif (argc > 1) {\ndiff --git a/connect.c b/connect.c\nindex 3280435331..04415d8eed 100644\n--- a/connect.c\n+++ b/connect.c\n@@ -251,7 +251,7 @@ static void process_capabilities(struct packet_reader *reader, size_t *linelen)\n \t\t\treader->hash_algo = &hash_algos[hash_algo];\n \t\tfree(hash_name);\n \t} else {\n-\t\treader->hash_algo = &hash_algos[GIT_HASH_SHA1];\n+\t\treader->hash_algo = &hash_algos[GIT_HASH_ORIGINAL];\n \t}\n }\n \n@@ -500,7 +500,7 @@ static void send_capabilities(int fd_out, struct packet_reader *reader)\n \t\treader->hash_algo = &hash_algos[hash_algo];\n \t\tpacket_write_fmt(fd_out, \"object-format=%s\", reader->hash_algo->name);\n \t} else {\n-\t\treader->hash_algo = &hash_algos[GIT_HASH_SHA1];\n+\t\treader->hash_algo = &hash_algos[GIT_HASH_ORIGINAL];\n \t}\n \tif (server_feature_v2(\"promisor-remote\", &promisor_remote_info)) {\n \t\tchar *reply = promisor_remote_reply(promisor_remote_info);\n@@ -665,7 +665,7 @@ int server_supports_hash(const char *desired, int *feature_supported)\n \tif (feature_supported)\n \t\t*feature_supported = !!hash;\n \tif (!hash) {\n-\t\thash = hash_algos[GIT_HASH_SHA1].name;\n+\t\thash = hash_algos[GIT_HASH_ORIGINAL].name;\n \t\tlen = strlen(hash);\n \t}\n \twhile (hash) {\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex fa4231fee7..970de4f7dc 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -1342,7 +1342,7 @@ static void write_fetch_command_and_capabilities(struct strbuf *req_buf,\n \t\t\tdie(_(\"mismatched algorithms: client %s; server %s\"),\n \t\t\t    the_hash_algo->name, hash_name);\n \t\tpacket_buf_write(req_buf, \"object-format=%s\", the_hash_algo->name);\n-\t} else if (hash_algo_by_ptr(the_hash_algo) != GIT_HASH_SHA1) {\n+\t} else if (hash_algo_by_ptr(the_hash_algo) != GIT_HASH_ORIGINAL) {\n \t\tdie(_(\"the server does not support algorithm '%s'\"),\n \t\t    the_hash_algo->name);\n \t}\ndiff --git a/pkt-line.c b/pkt-line.c\nindex a5bcbc96fb..e7c18da5e0 100644\n--- a/pkt-line.c\n+++ b/pkt-line.c\n@@ -617,7 +617,7 @@ void packet_reader_init(struct packet_reader *reader, int fd,\n \treader->buffer_size = sizeof(packet_buffer);\n \treader->options = options;\n \treader->me = \"git\";\n-\treader->hash_algo = &hash_algos[GIT_HASH_SHA1];\n+\treader->hash_algo = &hash_algos[GIT_HASH_ORIGINAL];\n \tstrbuf_init(&reader->scratch, 0);\n }\n \ndiff --git a/remote-curl.c b/remote-curl.c\nindex b8bc3a80cf..961ecd4655 100644\n--- a/remote-curl.c\n+++ b/remote-curl.c\n@@ -285,7 +285,7 @@ static const struct git_hash_algo *detect_hash_algo(struct discovery *heads)\n \t * back to SHA1, which may or may not be correct.\n \t */\n \tif (!p)\n-\t\treturn &hash_algos[GIT_HASH_SHA1];\n+\t\treturn &hash_algos[GIT_HASH_ORIGINAL];\n \n \talgo = hash_algo_by_length((p - heads->buf) / 2);\n \tif (algo == GIT_HASH_UNKNOWN)\ndiff --git a/serve.c b/serve.c\nindex e3ccf1505c..6a47170247 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -14,7 +14,7 @@\n \n static int advertise_sid = -1;\n static int advertise_object_info = -1;\n-static int client_hash_algo = GIT_HASH_SHA1;\n+static int client_hash_algo = GIT_HASH_ORIGINAL;\n \n static int always_advertise(struct repository *r UNUSED,\n \t\t\t    struct strbuf *value UNUSED)\ndiff --git a/setup.c b/setup.c\nindex f93bd6a24a..641c857ed5 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -2222,11 +2222,11 @@ void initialize_repository_version(int hash_algo,\n \t * version will get adjusted by git-clone(1) once it has learned about\n \t * the remote repository's format.\n \t */\n-\tif (hash_algo != GIT_HASH_SHA1 ||\n+\tif (hash_algo != GIT_HASH_ORIGINAL ||\n \t    ref_storage_format != REF_STORAGE_FORMAT_FILES)\n \t\ttarget_version = GIT_REPO_VERSION_READ;\n \n-\tif (hash_algo != GIT_HASH_SHA1 && hash_algo != GIT_HASH_UNKNOWN)\n+\tif (hash_algo != GIT_HASH_ORIGINAL && hash_algo != GIT_HASH_UNKNOWN)\n \t\tgit_config_set(\"extensions.objectformat\",\n \t\t\t       hash_algos[hash_algo].name);\n \telse if (reinit)\ndiff --git a/transport.c b/transport.c\nindex 6c2801bcbd..a019435918 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1243,7 +1243,7 @@ struct transport *transport_get(struct remote *remote, const char *url)\n \t\t\tret->smart_options->receivepack = remote->receivepack;\n \t}\n \n-\tret->hash_algo = &hash_algos[GIT_HASH_SHA1];\n+\tret->hash_algo = &hash_algos[GIT_HASH_ORIGINAL];\n \n \treturn ret;\n }\n"},{"id":"520453","messageId":"20250620011943.586596-3-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250620011943.586596-1-sandals@crustytoothpaste.net","subject":"[PATCH 02/10] hash: add a constant for the original hash algorithm","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-06-20T01:19:34Z","receivedAt":"2025-06-20T01:19:55Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"We have a a variety of uses of GIT_HASH_SHA1 littered throughout our\ncode.  Some of these really mean to represent specifically SHA-1, but\nsome actually represent the original hash algorithm used in Git which is\nimplied by older formats and protocols which do not contain hash\ninformation.  For instance, the bundle v1 and v2 formats do not contain\nhash algorithm information, and thus SHA-1 is implied by the use of\nthese formats.\n\nAdd a constant for documentary purposes which indicates this value.  It\nwill always be the same as SHA-1, since this is an essential part of\nthese formats, but its use indicates this particular reason and not any\nother reason why SHA-1 might be used.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n hash.h | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/hash.h b/hash.h\nindex 0d3d85e04c..0e14cade4e 100644\n--- a/hash.h\n+++ b/hash.h\n@@ -176,6 +176,8 @@ static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *s\n #define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)\n /* Default hash algorithm if unspecified. */\n #define GIT_HASH_DEFAULT GIT_HASH_SHA1\n+/* Original hash algorithm. Implied for older data formats which don't specify. */\n+#define GIT_HASH_ORIGINAL GIT_HASH_SHA1\n \n /* \"sha1\", big-endian */\n #define GIT_SHA1_FORMAT_ID 0x73686131\n"},{"id":"520454","messageId":"20250620011943.586596-6-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250620011943.586596-1-sandals@crustytoothpaste.net","subject":"[PATCH 05/10] setup: use the default algorithm to initialize repo format","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-06-20T01:19:37Z","receivedAt":"2025-06-20T01:19:58Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"When we define a new repository format with REPOSITORY_FORMAT_INIT, we\nalways use GIT_HASH_SHA1, and this value ends up getting used as the\ndefault value to initialize a repository if none of the command line,\nenvironment, or config tell us to do otherwise.\n\nBecause we might not always want to use SHA-1 as the default, let's\ninstead specify the default hash algorithm constant so that we will use\nwhatever the specified default is.  However, to make sure we continue to\nread repositories without a specified hash algorithm as SHA-1, default\nthe repository format to the original hash algorithm (SHA-1) when\nreading the repository format.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n setup.c | 5 ++++-\n setup.h | 2 +-\n 2 files changed, 5 insertions(+), 2 deletions(-)\n\ndiff --git a/setup.c b/setup.c\nindex 641c857ed5..fb38824a2b 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -835,9 +835,12 @@ static void init_repository_format(struct repository_format *format)\n int read_repository_format(struct repository_format *format, const char *path)\n {\n \tclear_repository_format(format);\n+\tformat->hash_algo = GIT_HASH_ORIGINAL;\n \tgit_config_from_file(check_repo_format, path, format);\n-\tif (format->version == -1)\n+\tif (format->version == -1) {\n \t\tclear_repository_format(format);\n+\t\tformat->hash_algo = GIT_HASH_ORIGINAL;\n+\t}\n \treturn format->version;\n }\n \ndiff --git a/setup.h b/setup.h\nindex 18dc3b7368..8522fa8575 100644\n--- a/setup.h\n+++ b/setup.h\n@@ -149,7 +149,7 @@ struct repository_format {\n { \\\n \t.version = -1, \\\n \t.is_bare = -1, \\\n-\t.hash_algo = GIT_HASH_SHA1, \\\n+\t.hash_algo = GIT_HASH_DEFAULT, \\\n \t.ref_storage_format = REF_STORAGE_FORMAT_FILES, \\\n \t.unknown_extensions = STRING_LIST_INIT_DUP, \\\n \t.v1_only_extensions = STRING_LIST_INIT_DUP, \\\n"},{"id":"520455","messageId":"20250620011943.586596-9-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250620011943.586596-1-sandals@crustytoothpaste.net","subject":"[PATCH 08/10] t4042: choose the built-in hash outside of a repo","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-06-20T01:19:40Z","receivedAt":"2025-06-20T01:19:58Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Right now, the built-in default hash is always SHA-1, but that will\nchange in a future commit.  Instead of assuming that operating outside\nof a repository will always use SHA-1, provide constants for both\nalgorithms and then simply ask test_oid for the built-in hash instead,\nwhich will always be correct.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t4042-diff-textconv-caching.sh | 12 ++++++++++--\n 1 file changed, 10 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t4042-diff-textconv-caching.sh b/t/t4042-diff-textconv-caching.sh\nindex ff0e73531b..31018ceba2 100755\n--- a/t/t4042-diff-textconv-caching.sh\n+++ b/t/t4042-diff-textconv-caching.sh\n@@ -120,6 +120,14 @@ test_expect_success 'log notes cache and still use cache for -p' '\n '\n \n test_expect_success 'caching is silently ignored outside repo' '\n+\ttest_oid_cache <<-\\EOM &&\n+\toid1 sha1:5626abf\n+\toid1 sha256:a4ed1f3\n+\toid2 sha1:f719efd\n+\toid2 sha256:aa9e7dc\n+\tEOM\n+\toid1=$(test_oid --hash=builtin oid1) &&\n+\toid2=$(test_oid --hash=builtin oid2) &&\n \tmkdir -p non-repo &&\n \techo one >non-repo/one &&\n \techo two >non-repo/two &&\n@@ -129,9 +137,9 @@ test_expect_success 'caching is silently ignored outside repo' '\n \t\t   -c diff.test.textconv=\"tr a-z A-Z <\" \\\n \t\t   -c diff.test.cachetextconv=true \\\n \t\t   diff --no-index one two >actual &&\n-\tcat >expect <<-\\EOF &&\n+\tcat >expect <<-EOF &&\n \tdiff --git a/one b/two\n-\tindex 5626abf..f719efd 100644\n+\tindex $oid1..$oid2 100644\n \t--- a/one\n \t+++ b/two\n \t@@ -1 +1 @@\n"},{"id":"520456","messageId":"20250620011943.586596-8-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250620011943.586596-1-sandals@crustytoothpaste.net","subject":"[PATCH 07/10] t1007: choose the built-in hash outside of a repo","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-06-20T01:19:39Z","receivedAt":"2025-06-20T01:19:58Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Right now, the built-in default hash is always SHA-1, but that will\nchange in a future commit.  Instead of assuming that operating outside\nof a repository will always use SHA-1, simply ask test_oid for the\nbuilt-in hash instead, which will always be correct.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t1007-hash-object.sh | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh\nindex 64658b3ba5..de076293b6 100755\n--- a/t/t1007-hash-object.sh\n+++ b/t/t1007-hash-object.sh\n@@ -252,9 +252,9 @@ test_expect_success '--literally complains about non-standard types' '\n \ttest_must_fail git hash-object -t bogus --literally --stdin\n '\n \n-test_expect_success '--stdin outside of repository (uses SHA-1)' '\n+test_expect_success '--stdin outside of repository (uses default hash)' '\n \tnongit git hash-object --stdin <hello >actual &&\n-\techo \"$(test_oid --hash=sha1 hello)\" >expect &&\n+\techo \"$(test_oid --hash=builtin hello)\" >expect &&\n \ttest_cmp expect actual\n '\n \n"},{"id":"520457","messageId":"20250620011943.586596-7-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250620011943.586596-1-sandals@crustytoothpaste.net","subject":"[PATCH 06/10] t: default to compile-time default hash if not set","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-06-20T01:19:38Z","receivedAt":"2025-06-20T01:19:58Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Right now, the default compile-time hash is SHA-1.  However, in the\nfuture, this might change and it would be helpful to gracefully handle\nthis case in our testsuite.\n\nTo avoid making these assumptions, let's introduce a variable that\ncontains the built-in default hash and use it in our setup code as the\nfallback value if no hash was explicitly set.  For now, this is always\nSHA-1, but in a future commit, we'll allow adjusting this and the\nvariable will be more useful.\n\nTo allow us to make our tests more robust, allow test_oid to take the\n--hash=builtin option to specify this hash, whatever it is.\n\nAdditionally, add a DEFAULT_HASH_ALGORITHM prerequisite to check for the\ncompile-time hash.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/test-lib-functions.sh | 5 ++++-\n t/test-lib.sh           | 7 ++++++-\n 2 files changed, 10 insertions(+), 2 deletions(-)\n\ndiff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh\nindex bee4a2ca34..6ec95ea51f 100644\n--- a/t/test-lib-functions.sh\n+++ b/t/test-lib-functions.sh\n@@ -1695,7 +1695,7 @@ test_set_hash () {\n \n # Detect the hash algorithm in use.\n test_detect_hash () {\n-\tcase \"$GIT_TEST_DEFAULT_HASH\" in\n+\tcase \"${GIT_TEST_DEFAULT_HASH:-$GIT_TEST_BUILTIN_HASH}\" in\n \t\"sha256\")\n \t    test_hash_algo=sha256\n \t    test_compat_hash_algo=sha1\n@@ -1767,6 +1767,9 @@ test_oid () {\n \t--hash=compat)\n \t\talgo=\"$test_compat_hash_algo\" &&\n \t\tshift;;\n+\t--hash=builtin)\n+\t\talgo=\"$GIT_TEST_BUILTIN_HASH\" &&\n+\t\tshift;;\n \t--hash=*)\n \t\talgo=\"${1#--hash=}\" &&\n \t\tshift;;\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 51370a201c..ef3759ec80 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -536,7 +536,8 @@ export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME\n export GIT_COMMITTER_DATE GIT_AUTHOR_DATE\n export EDITOR\n \n-GIT_DEFAULT_HASH=\"${GIT_TEST_DEFAULT_HASH:-sha1}\"\n+GIT_TEST_BUILTIN_HASH=sha1\n+GIT_DEFAULT_HASH=\"${GIT_TEST_DEFAULT_HASH:-$GIT_TEST_BUILTIN_HASH}\"\n export GIT_DEFAULT_HASH\n GIT_DEFAULT_REF_FORMAT=\"${GIT_TEST_DEFAULT_REF_FORMAT:-files}\"\n export GIT_DEFAULT_REF_FORMAT\n@@ -1908,6 +1909,10 @@ test_lazy_prereq SHA1 '\n \tesac\n '\n \n+test_lazy_prereq DEFAULT_HASH_ALGORITHM '\n+\ttest \"$GIT_TEST_BUILTIN_HASH\" = \"$GIT_DEFAULT_HASH\"\n+'\n+\n test_lazy_prereq DEFAULT_REPO_FORMAT '\n \ttest_have_prereq SHA1,REFFILES\n '\n"},{"id":"520458","messageId":"20250620011943.586596-10-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250620011943.586596-1-sandals@crustytoothpaste.net","subject":"[PATCH 09/10] t5300: choose the built-in hash outside of a repo","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-06-20T01:19:41Z","receivedAt":"2025-06-20T01:19:59Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Right now, the built-in default hash is always SHA-1, but that will\nchange in a future commit.  Instead of assuming that operating outside\nof a repository will always use SHA-1, look up the default hash\nalgorithm for operating outside of a repository using an appropriate\nenvironment variable, which will always be correct.\n\nAdditionally, for operations outside of a repository, use the\nDEFAULT_HASH_ALGORITHM prerequisite rather than SHA1.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t5300-pack-object.sh | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh\nindex ae72158b94..73445782e7 100755\n--- a/t/t5300-pack-object.sh\n+++ b/t/t5300-pack-object.sh\n@@ -525,7 +525,7 @@ test_expect_success 'index-pack --strict <pack> works in non-repo' '\n \ttest_path_is_file foo.idx\n '\n \n-test_expect_success SHA1 'show-index works OK outside a repository' '\n+test_expect_success DEFAULT_HASH_ALGORITHM 'show-index works OK outside a repository' '\n \tnongit git show-index <foo.idx\n '\n \n@@ -658,7 +658,7 @@ do\n \t\ttest_commit -C repo initial &&\n \t\tgit -C repo repack -ad &&\n \t\tgit -C repo verify-pack \"$(pwd)\"/repo/.git/objects/pack/*.idx &&\n-\t\tif test $hash = sha1\n+\t\tif test $hash = $GIT_TEST_BUILTIN_HASH\n \t\tthen\n \t\t\tnongit git verify-pack \"$(pwd)\"/repo/.git/objects/pack/*.idx\n \t\telse\n@@ -676,7 +676,7 @@ do\n \t\ttest_commit -C repo initial &&\n \t\tgit -C repo repack -ad &&\n \t\tgit -C repo index-pack --verify \"$(pwd)\"/repo/.git/objects/pack/*.pack &&\n-\t\tif test $hash = sha1\n+\t\tif test $hash = $GIT_TEST_BUILTIN_HASH\n \t\tthen\n \t\t\tnongit git index-pack --verify \"$(pwd)\"/repo/.git/objects/pack/*.pack\n \t\telse\n"},{"id":"520459","messageId":"20250620011943.586596-11-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250620011943.586596-1-sandals@crustytoothpaste.net","subject":"[PATCH 10/10] Enable SHA-256 by default in breaking changes mode","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-06-20T01:19:42Z","receivedAt":"2025-06-20T01:19:59Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Our document on breaking changes indicates that we intend to default to\nSHA-256 in Git 3.0.  Since most people choose the default option, this\nis an important security upgrade to our defaults.\n\nTo allow people to test this case, when WITH_BREAKING_CHANGES is set in\nthe configuration, build Git with SHA-256 as the default hash.  Update\nthe testsuite to reflect this configuration so that the tests pass.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n hash.h        | 6 ++++++\n t/test-lib.sh | 7 ++++++-\n 2 files changed, 12 insertions(+), 1 deletion(-)\n\ndiff --git a/hash.h b/hash.h\nindex 0e14cade4e..144b53b7d6 100644\n--- a/hash.h\n+++ b/hash.h\n@@ -174,8 +174,14 @@ static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *s\n #define GIT_HASH_SHA256 2\n /* Number of algorithms supported (including unknown). */\n #define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)\n+\n /* Default hash algorithm if unspecified. */\n+#ifdef WITH_BREAKING_CHANGES\n+#define GIT_HASH_DEFAULT GIT_HASH_SHA256\n+#else\n #define GIT_HASH_DEFAULT GIT_HASH_SHA1\n+#endif\n+\n /* Original hash algorithm. Implied for older data formats which don't specify. */\n #define GIT_HASH_ORIGINAL GIT_HASH_SHA1\n \ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex ef3759ec80..bb18dd0606 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -536,7 +536,12 @@ export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME\n export GIT_COMMITTER_DATE GIT_AUTHOR_DATE\n export EDITOR\n \n-GIT_TEST_BUILTIN_HASH=sha1\n+if test -n \"$WITH_BREAKING_CHANGES\"\n+then\n+\tGIT_TEST_BUILTIN_HASH=sha256\n+else\n+\tGIT_TEST_BUILTIN_HASH=sha1\n+fi\n GIT_DEFAULT_HASH=\"${GIT_TEST_DEFAULT_HASH:-$GIT_TEST_BUILTIN_HASH}\"\n export GIT_DEFAULT_HASH\n GIT_DEFAULT_REF_FORMAT=\"${GIT_TEST_DEFAULT_REF_FORMAT:-files}\"\n"},{"id":"520460","messageId":"xmqq1prf89cd.fsf@gitster.g","threadId":"63675","inReplyTo":"20250620011943.586596-3-sandals@crustytoothpaste.net","subject":"Re: [PATCH 02/10] hash: add a constant for the original hash algorithm","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-20T01:56:02Z","receivedAt":"2025-06-20T01:56:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n> We have a a variety of uses of GIT_HASH_SHA1 littered throughout our\n> code.  Some of these really mean to represent specifically SHA-1, but\n> some actually represent the original hash algorithm used in Git which is\n> implied by older formats and protocols which do not contain hash\n> information.  For instance, the bundle v1 and v2 formats do not contain\n> hash algorithm information, and thus SHA-1 is implied by the use of\n> these formats.\n\nDoes that mean use of _ORIGINAL is a sign that these places should\nkeep using SHA-1 and should not change?\n\nI am having a hard time guessing/assessing the value of having _ORIGINAL\nthat is a synonym for _SHA1; with redirection, it pretends as if the\nunderlying value can be updated from SHA-1 to SHA-256 (and that is\nthe very intention behind GIT_HASH_DEFAULT symbol that gives us a\nlevel of indirection), but it is hard to imagine we would ever want\nto change what _ORIGINAL means, as that word talks about a historical\nfact that will never change over time.\n\n> Add a constant for documentary purposes which indicates this value.  It\n> will always be the same as SHA-1, since this is an essential part of\n> these formats, but its use indicates this particular reason and not any\n> other reason why SHA-1 might be used.\n\nI am not sure what this means.  If we use GIT_HASH_SHA1 in such\nplaces explicitly (as opposed to GIT_HASH_DEFAULT), isn't it a sign\nenough that with different versions of Git, that particular code\npath should keep using SHA-1 no matter what the default is?\n\n> Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n> ---\n>  hash.h | 2 ++\n>  1 file changed, 2 insertions(+)\n>\n> diff --git a/hash.h b/hash.h\n> index 0d3d85e04c..0e14cade4e 100644\n> --- a/hash.h\n> +++ b/hash.h\n> @@ -176,6 +176,8 @@ static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *s\n>  #define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)\n>  /* Default hash algorithm if unspecified. */\n>  #define GIT_HASH_DEFAULT GIT_HASH_SHA1\n> +/* Original hash algorithm. Implied for older data formats which don't specify. */\n> +#define GIT_HASH_ORIGINAL GIT_HASH_SHA1\n>  \n>  /* \"sha1\", big-endian */\n>  #define GIT_SHA1_FORMAT_ID 0x73686131\n"},{"id":"520490","messageId":"xmqqikkq7awy.fsf@gitster.g","threadId":"63675","inReplyTo":"20250620011943.586596-4-sandals@crustytoothpaste.net","subject":"Re: [PATCH 03/10] builtin: use default hash when outside a repository","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-20T14:19:41Z","receivedAt":"2025-06-20T14:19:44Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n> We have some commands that can operate inside or outside a repository.\n> If we're operating outside a repository, we clearly cannot use the\n> repository's hash algorithm as a default since it doesn't exist, so\n> instead, let's pick the default instead of specifically SHA-1.  Right\n> now this results in no functional change since the default is SHA-1, but\n> that may change in the future.\n\nNicely explained and this step shows why having GIT_HASH_DEFAULT\nmakes sense very well.\n\n>\n> Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n> ---\n>  builtin/apply.c       | 2 +-\n>  builtin/diff.c        | 2 +-\n>  builtin/hash-object.c | 2 +-\n>  builtin/index-pack.c  | 2 +-\n>  builtin/ls-remote.c   | 2 +-\n>  builtin/patch-id.c    | 2 +-\n>  builtin/shortlog.c    | 2 +-\n>  builtin/show-index.c  | 2 +-\n>  8 files changed, 8 insertions(+), 8 deletions(-)\n>\n> diff --git a/builtin/apply.c b/builtin/apply.c\n> index a1e20c593d..d642a40251 100644\n> --- a/builtin/apply.c\n> +++ b/builtin/apply.c\n> @@ -29,7 +29,7 @@ int cmd_apply(int argc,\n>  \t * cf. https://lore.kernel.org/git/xmqqcypfcmn4.fsf@gitster.g/\n>  \t */\n>  \tif (!the_hash_algo)\n> -\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n> +\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n>  \n>  \targc = apply_parse_options(argc, argv,\n>  \t\t\t\t   &state, &force_apply, &options,\n> diff --git a/builtin/diff.c b/builtin/diff.c\n> index c6231edce4..eebffe36cc 100644\n> --- a/builtin/diff.c\n> +++ b/builtin/diff.c\n> @@ -483,7 +483,7 @@ int cmd_diff(int argc,\n>  \t * configurable via a command line option.\n>  \t */\n>  \tif (nongit)\n> -\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n> +\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n>  \n>  \tinit_diff_ui_defaults();\n>  \tgit_config(git_diff_ui_config, NULL);\n> diff --git a/builtin/hash-object.c b/builtin/hash-object.c\n> index 6a99ec250d..213a302e05 100644\n> --- a/builtin/hash-object.c\n> +++ b/builtin/hash-object.c\n> @@ -104,7 +104,7 @@ int cmd_hash_object(int argc,\n>  \t\tprefix = setup_git_directory_gently(&nongit);\n>  \n>  \tif (nongit && !the_hash_algo)\n> -\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n> +\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n>  \n>  \tif (vpath && prefix) {\n>  \t\tvpath_free = prefix_filename(prefix, vpath);\n> diff --git a/builtin/index-pack.c b/builtin/index-pack.c\n> index bb7925bd29..352ce7f88a 100644\n> --- a/builtin/index-pack.c\n> +++ b/builtin/index-pack.c\n> @@ -2034,7 +2034,7 @@ int cmd_index_pack(int argc,\n>  \t * choice but to guess the object hash.\n>  \t */\n>  \tif (!the_repository->hash_algo)\n> -\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n> +\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n>  \n>  \topts.flags &= ~(WRITE_REV | WRITE_REV_VERIFY);\n>  \tif (rev_index) {\n> diff --git a/builtin/ls-remote.c b/builtin/ls-remote.c\n> index 01a4d4daa1..df09000b30 100644\n> --- a/builtin/ls-remote.c\n> +++ b/builtin/ls-remote.c\n> @@ -112,7 +112,7 @@ int cmd_ls_remote(int argc,\n>  \t * depending on what object hash the remote uses.\n>  \t */\n>  \tif (!the_repository->hash_algo)\n> -\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n> +\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n>  \n>  \tpacket_trace_identity(\"ls-remote\");\n>  \n> diff --git a/builtin/patch-id.c b/builtin/patch-id.c\n> index cdef2ec10a..26f04b0335 100644\n> --- a/builtin/patch-id.c\n> +++ b/builtin/patch-id.c\n> @@ -254,7 +254,7 @@ int cmd_patch_id(int argc,\n>  \t * the code that computes patch IDs to always use SHA1.\n>  \t */\n>  \tif (!the_hash_algo)\n> -\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n> +\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n>  \n>  \tgenerate_id_list(opts ? opts > 1 : config.stable,\n>  \t\t\t opts ? opts == 3 : config.verbatim);\n> diff --git a/builtin/shortlog.c b/builtin/shortlog.c\n> index fe15e11497..60adc5e7a5 100644\n> --- a/builtin/shortlog.c\n> +++ b/builtin/shortlog.c\n> @@ -419,7 +419,7 @@ int cmd_shortlog(int argc,\n>  \t * git/nongit so that we do not have to do this.\n>  \t */\n>  \tif (nongit && !the_hash_algo)\n> -\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n> +\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n>  \n>  \tgit_config(git_default_config, NULL);\n>  \tshortlog_init(&log);\n> diff --git a/builtin/show-index.c b/builtin/show-index.c\n> index 9d4ecf5e7b..2c3e2940ce 100644\n> --- a/builtin/show-index.c\n> +++ b/builtin/show-index.c\n> @@ -47,7 +47,7 @@ int cmd_show_index(int argc,\n>  \t *       the index file passed in and use that instead.\n>  \t */\n>  \tif (!the_hash_algo)\n> -\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n> +\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n>  \n>  \thashsz = the_hash_algo->rawsz;\n>  \n"},{"id":"520491","messageId":"xmqqcyay7ale.fsf@gitster.g","threadId":"63675","inReplyTo":"20250620011943.586596-5-sandals@crustytoothpaste.net","subject":"Re: [PATCH 04/10] Use original hash for legacy formats","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-20T14:26:37Z","receivedAt":"2025-06-20T14:26:40Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n> We have a large variety of data formats and protocols where no hash\n> algorithm was defined and the default was assumed to always be SHA-1.\n> Instead of explicitly stating SHA-1, let's use the constant to represent\n> the original hash algorithm (which is still SHA-1) so that it's clear\n> for documentary purposes that it's a legacy fallback option and not an\n> intentional choice to use SHA-1.\n>\n> Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n> ---\n>  builtin/receive-pack.c | 2 +-\n>  bundle.c               | 4 ++--\n>  connect.c              | 6 +++---\n>  fetch-pack.c           | 2 +-\n>  pkt-line.c             | 2 +-\n>  remote-curl.c          | 2 +-\n>  serve.c                | 2 +-\n>  setup.c                | 4 ++--\n>  transport.c            | 2 +-\n>  9 files changed, 13 insertions(+), 13 deletions(-)\n\nI earlier expressed my puzzlement, but this step shows how\nGIT_HASH_ORIGINAL may make sense as a transitional measure, letting\nus tell between \"This place in the code uses GIT_HASH_SHA1 simply\nbecause we haven't examined and inspected it for the purpose of\nallowing us to eventually switch the default\" and \"This place in the\ncode we determined need to keep using SHA1 even when the default is\ndifferent\".\n\nIf that is what is going on, after the whole-code transition\nfinishes, we would want to rename _ORIGINAL back to _SHA1 for\nreadability, as in such a future, developers should not have to\nremember that we originally used SHA-1.\n\nIf we call use a name with SHA-1 in it (e.g., GIT_HASH_MUST_BE_SHA1)\nfrom the beginning, perhaps we do not have to rename _ORIGINAL later?\n"},{"id":"520493","messageId":"xmqqtt4a5upb.fsf@gitster.g","threadId":"63675","inReplyTo":"20250620011943.586596-6-sandals@crustytoothpaste.net","subject":"Re: [PATCH 05/10] setup: use the default algorithm to initialize repo format","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-20T14:55:12Z","receivedAt":"2025-06-20T14:55:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n> When we define a new repository format with REPOSITORY_FORMAT_INIT, we\n> always use GIT_HASH_SHA1, and this value ends up getting used as the\n> default value to initialize a repository if none of the command line,\n> environment, or config tell us to do otherwise.\n>\n> Because we might not always want to use SHA-1 as the default, let's\n> instead specify the default hash algorithm constant so that we will use\n> whatever the specified default is.  \n\nAll of the above hints the use of _DEFAULT instead of _SHA1 but ...\n\n> However, to make sure we continue to\n> read repositories without a specified hash algorithm as SHA-1, default\n> the repository format to the original hash algorithm (SHA-1) when\n> reading the repository format.\n\n... this explains why we may want to\n\n - expect that we would be able to read the hash from repository\n - read from repository\n - if the repository specifies the hash, happily use it\n - otherwise it is a lagacy repository so use the SHA-1\n\nIs that what is going on here?  Because I find some things that\nhappens in the code somewhat questionable.\n\n> Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n> ---\n>  setup.c | 5 ++++-\n>  setup.h | 2 +-\n>  2 files changed, 5 insertions(+), 2 deletions(-)\n>\n> diff --git a/setup.c b/setup.c\n> index 641c857ed5..fb38824a2b 100644\n> --- a/setup.c\n> +++ b/setup.c\n> @@ -835,9 +835,12 @@ static void init_repository_format(struct repository_format *format)\n>  int read_repository_format(struct repository_format *format, const char *path)\n>  {\n>  \tclear_repository_format(format);\n> +\tformat->hash_algo = GIT_HASH_ORIGINAL;\n\nIf we expect we can read from the config, and otherwise we fall back\nto the hardcoded legacy SHA-1, do we need this assignment?  We\ncleared and version is set to -1, and then we read from the config ...\n\n>  \tgit_config_from_file(check_repo_format, path, format);\n\n... so if the file said anything about \"extensions.objectformat\", we\nwould know it by now.  If not, wouldn't version be left to -1 as our\nprevious clal to clear_repository_format() set it via its call to\ninit_repository_format()?\n\nAhh, this code is prepared to handle a repository that claims to use\nversion 1 but does not set extensions.objectformat at all.  And in\nsuch a case, we do want to use SHA-1.  OK, the above code makes\nsense for that case.\n\n> -\tif (format->version == -1)\n> +\tif (format->version == -1) {\n\nAnd if there is no core.repositoryformatversion set, we will come\nhere.  According to the comment before handle_extension_v0(), some\nextensions.* should still be honored even in such a repository, and\nthe above call to git_config_from_file() should have handled them\njust fine.\n\nHowever, I do not understand why we clear all of what we read with\nanother call to clear_repository_format() here.\n\n>  \t\tclear_repository_format(format);\n> +\t\tformat->hash_algo = GIT_HASH_ORIGINAL;\n> +\t}\n>  \treturn format->version;\n>  }\n\nAdmittedly, I find that the way how check_repo_format() does its\nthing somewhat questionable.  Even though it reads into the .version\nmember the value of core.repositoryformatversion, it slurps in all\nthe extensions.* regardless of what .version the repository claims\nto be in.  So even though there are two separate functions to handle\n\"historical compatibility\" handle_extension_v0() and other extensions,\nwe still end up honoring extensions.objectformat in a repository that\ndoes not say what format version it uses.  And clearing them with a\ncall to clear_repository_format() may make sense, but do we want to\nclear things we read with handle_extension_v0() as well?\n\n> diff --git a/setup.h b/setup.h\n> index 18dc3b7368..8522fa8575 100644\n> --- a/setup.h\n> +++ b/setup.h\n> @@ -149,7 +149,7 @@ struct repository_format {\n>  { \\\n>  \t.version = -1, \\\n>  \t.is_bare = -1, \\\n> -\t.hash_algo = GIT_HASH_SHA1, \\\n> +\t.hash_algo = GIT_HASH_DEFAULT, \\\n>  \t.ref_storage_format = REF_STORAGE_FORMAT_FILES, \\\n>  \t.unknown_extensions = STRING_LIST_INIT_DUP, \\\n>  \t.v1_only_extensions = STRING_LIST_INIT_DUP, \\\n"},{"id":"520494","messageId":"xmqqmsa25ukg.fsf@gitster.g","threadId":"63675","inReplyTo":"20250620011943.586596-11-sandals@crustytoothpaste.net","subject":"Re: [PATCH 10/10] Enable SHA-256 by default in breaking changes mode","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-20T14:58:07Z","receivedAt":"2025-06-20T14:58:09Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n> Our document on breaking changes indicates that we intend to default to\n> SHA-256 in Git 3.0.  Since most people choose the default option, this\n> is an important security upgrade to our defaults.\n>\n> To allow people to test this case, when WITH_BREAKING_CHANGES is set in\n> the configuration, build Git with SHA-256 as the default hash.  Update\n> the testsuite to reflect this configuration so that the tests pass.\n\nNice.\n\n\n> Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n> ---\n>  hash.h        | 6 ++++++\n>  t/test-lib.sh | 7 ++++++-\n>  2 files changed, 12 insertions(+), 1 deletion(-)\n>\n> diff --git a/hash.h b/hash.h\n> index 0e14cade4e..144b53b7d6 100644\n> --- a/hash.h\n> +++ b/hash.h\n> @@ -174,8 +174,14 @@ static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *s\n>  #define GIT_HASH_SHA256 2\n>  /* Number of algorithms supported (including unknown). */\n>  #define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)\n> +\n>  /* Default hash algorithm if unspecified. */\n> +#ifdef WITH_BREAKING_CHANGES\n> +#define GIT_HASH_DEFAULT GIT_HASH_SHA256\n> +#else\n>  #define GIT_HASH_DEFAULT GIT_HASH_SHA1\n> +#endif\n\nI think we decided to format the above this way.\n\n    #ifdef WITH_BREAKING_CHANGES\n    # define GIT_HASH_DEFAULT GIT_HASH_SHA256\n    #else\n    # define GIT_HASH_DEFAULT GIT_HASH_SHA1\n    #endif\n\ncf. Documentation/CodingGuidelines\n\n - Nested C preprocessor directives are indented after the hash by one\n   space per nesting level.\n"},{"id":"520495","messageId":"xmqqfrfu5ubo.fsf@gitster.g","threadId":"63675","inReplyTo":"20250620011943.586596-11-sandals@crustytoothpaste.net","subject":"Re: [PATCH 10/10] Enable SHA-256 by default in breaking changes mode","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-20T15:03:23Z","receivedAt":"2025-06-20T15:03:26Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n> Our document on breaking changes indicates that we intend to default to\n> SHA-256 in Git 3.0.  Since most people choose the default option, this\n> is an important security upgrade to our defaults.\n>\n> To allow people to test this case, when WITH_BREAKING_CHANGES is set in\n> the configuration, build Git with SHA-256 as the default hash.  Update\n> the testsuite to reflect this configuration so that the tests pass.\n\nAnother thing that I suspect nobody wrote tests for, but we must be\nabsolutely certain, is that the post-3.0 Git can still interoperate\nwell with historical SHA-1 repositories (I am not talking about\n\"fetch from SHA-1 into SHA-256\", but \"the binary does not lose\nability to work in SHA-1 repositories or fetch/push between SHA-1\nrepositories, only because the default is set to SHA-256\"), even in\nold repositories people have been using for ages without the\ncore.repositoryformatversion defined.\n\nThanks.\n"},{"id":"520509","messageId":"aFWzTv5-AjUvXWsg@fruit.crustytoothpaste.net","threadId":"63675","inReplyTo":"xmqqfrfu5ubo.fsf@gitster.g","subject":"Re: [PATCH 10/10] Enable SHA-256 by default in breaking changes mode","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-06-20T19:15:26Z","receivedAt":"2025-06-20T19:15:28Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2025-06-20 at 15:03:23, Junio C Hamano wrote:\n> \"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n> \n> > Our document on breaking changes indicates that we intend to default to\n> > SHA-256 in Git 3.0.  Since most people choose the default option, this\n> > is an important security upgrade to our defaults.\n> >\n> > To allow people to test this case, when WITH_BREAKING_CHANGES is set in\n> > the configuration, build Git with SHA-256 as the default hash.  Update\n> > the testsuite to reflect this configuration so that the tests pass.\n> \n> Another thing that I suspect nobody wrote tests for, but we must be\n> absolutely certain, is that the post-3.0 Git can still interoperate\n> well with historical SHA-1 repositories (I am not talking about\n> \"fetch from SHA-1 into SHA-256\", but \"the binary does not lose\n> ability to work in SHA-1 repositories or fetch/push between SHA-1\n> repositories, only because the default is set to SHA-256\"), even in\n> old repositories people have been using for ages without the\n> core.repositoryformatversion defined.\n\nYes, I have definitely tested that here before sending it out.  When Git\n3.0 comes out, we can switch our GIT_TEST_DEFAULT_HASH test from sha256\nto sha1 to continue to verify that those work.  As I learned when\nwriting the SHA-256 functionality and as I'm experiencing today writing\nthe interop code, if clones, fetches, and pushes do not work properly,\nthe testsuite is completely broken with at the very least fifty-some-odd\ntests failing, so I feel confident that functionality will continue to\nwork for SHA-1 as long as we do run an appropriate test job.\n\nAlso, when we initialize a SHA-1 repository with the files ref backend,\nwe still use repository format version 0 without any extensions, so the\ncases that cover older-style configs will still be adequately tested. We\nalso have some tests that even test that things work properly without a\nconfig file, which caught a bug in this series (that I fixed before\nsending it out).\n-- \nbrian m. carlson (they/them)\nToronto, Ontario, CA\n"},{"id":"520510","messageId":"aFWz7UGQUAIC7h0v@fruit.crustytoothpaste.net","threadId":"63675","inReplyTo":"xmqqmsa25ukg.fsf@gitster.g","subject":"Re: [PATCH 10/10] Enable SHA-256 by default in breaking changes mode","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-06-20T19:18:05Z","receivedAt":"2025-06-20T19:18:06Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2025-06-20 at 14:58:07, Junio C Hamano wrote:\n> \"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n> > Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n> > ---\n> >  hash.h        | 6 ++++++\n> >  t/test-lib.sh | 7 ++++++-\n> >  2 files changed, 12 insertions(+), 1 deletion(-)\n> >\n> > diff --git a/hash.h b/hash.h\n> > index 0e14cade4e..144b53b7d6 100644\n> > --- a/hash.h\n> > +++ b/hash.h\n> > @@ -174,8 +174,14 @@ static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *s\n> >  #define GIT_HASH_SHA256 2\n> >  /* Number of algorithms supported (including unknown). */\n> >  #define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)\n> > +\n> >  /* Default hash algorithm if unspecified. */\n> > +#ifdef WITH_BREAKING_CHANGES\n> > +#define GIT_HASH_DEFAULT GIT_HASH_SHA256\n> > +#else\n> >  #define GIT_HASH_DEFAULT GIT_HASH_SHA1\n> > +#endif\n> \n> I think we decided to format the above this way.\n> \n>     #ifdef WITH_BREAKING_CHANGES\n>     # define GIT_HASH_DEFAULT GIT_HASH_SHA256\n>     #else\n>     # define GIT_HASH_DEFAULT GIT_HASH_SHA1\n>     #endif\n\nGreat, I'll put this in a v2.\n-- \nbrian m. carlson (they/them)\nToronto, Ontario, CA\n"},{"id":"520512","messageId":"aFXEgLRLRdbaPOb2@fruit.crustytoothpaste.net","threadId":"63675","inReplyTo":"xmqqtt4a5upb.fsf@gitster.g","subject":"Re: [PATCH 05/10] setup: use the default algorithm to initialize repo format","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-06-20T20:28:48Z","receivedAt":"2025-06-20T20:28:50Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2025-06-20 at 14:55:12, Junio C Hamano wrote:\n> \"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n> \n> > When we define a new repository format with REPOSITORY_FORMAT_INIT, we\n> > always use GIT_HASH_SHA1, and this value ends up getting used as the\n> > default value to initialize a repository if none of the command line,\n> > environment, or config tell us to do otherwise.\n> >\n> > Because we might not always want to use SHA-1 as the default, let's\n> > instead specify the default hash algorithm constant so that we will use\n> > whatever the specified default is.  \n> \n> All of the above hints the use of _DEFAULT instead of _SHA1 but ...\n> \n> > However, to make sure we continue to\n> > read repositories without a specified hash algorithm as SHA-1, default\n> > the repository format to the original hash algorithm (SHA-1) when\n> > reading the repository format.\n> \n> ... this explains why we may want to\n> \n>  - expect that we would be able to read the hash from repository\n>  - read from repository\n>  - if the repository specifies the hash, happily use it\n>  - otherwise it is a lagacy repository so use the SHA-1\n> \n> Is that what is going on here?  Because I find some things that\n> happens in the code somewhat questionable.\n\nYes, that's roughly it.  I'll explain this better in v2.\n\n> > Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n> > ---\n> >  setup.c | 5 ++++-\n> >  setup.h | 2 +-\n> >  2 files changed, 5 insertions(+), 2 deletions(-)\n> >\n> > diff --git a/setup.c b/setup.c\n> > index 641c857ed5..fb38824a2b 100644\n> > --- a/setup.c\n> > +++ b/setup.c\n> > @@ -835,9 +835,12 @@ static void init_repository_format(struct repository_format *format)\n> >  int read_repository_format(struct repository_format *format, const char *path)\n> >  {\n> >  \tclear_repository_format(format);\n> > +\tformat->hash_algo = GIT_HASH_ORIGINAL;\n> \n> If we expect we can read from the config, and otherwise we fall back\n> to the hardcoded legacy SHA-1, do we need this assignment?  We\n> cleared and version is set to -1, and then we read from the config ...\n> \n> >  \tgit_config_from_file(check_repo_format, path, format);\n> \n> ... so if the file said anything about \"extensions.objectformat\", we\n> would know it by now.  If not, wouldn't version be left to -1 as our\n> previous clal to clear_repository_format() set it via its call to\n> init_repository_format()?\n\nThe subtle behaviour here is that -1 means that either there is no\nversion specified or that there's no config file.  I was surprised to\nlearn that we do not require a configuration file, but we have tests for\nthat case with `git branch`.\n\n> Ahh, this code is prepared to handle a repository that claims to use\n> version 1 but does not set extensions.objectformat at all.  And in\n> such a case, we do want to use SHA-1.  OK, the above code makes\n> sense for that case.\n\nCorrect.  We can set v1 because we want reftable, for instance, and we\nnever set extensions.objectFormat to \"sha1\".  We always rely on the\ndefault behaviour since that's more compatible.\n\n> > -\tif (format->version == -1)\n> > +\tif (format->version == -1) {\n> \n> And if there is no core.repositoryformatversion set, we will come\n> here.  According to the comment before handle_extension_v0(), some\n> extensions.* should still be honored even in such a repository, and\n> the above call to git_config_from_file() should have handled them\n> just fine.\n> \n> However, I do not understand why we clear all of what we read with\n> another call to clear_repository_format() here.\n\nBecause this is the case where there's no config file.  If nobody\nbothered to write a configuration file, then we want to reset everything\nto the default.\n\nI don't know what we do if we have a repository with a config file and\nno version, but literally every repository since Git 0.99.3 (I believe)\nhas core.repositoryformatversion written into the repo.  I'm certain\nthat the behaviour we'd want if nobody specified one was to do the most\ncompatible thing, so the defaults seem prudent.\n\n> Admittedly, I find that the way how check_repo_format() does its\n> thing somewhat questionable.  Even though it reads into the .version\n> member the value of core.repositoryformatversion, it slurps in all\n> the extensions.* regardless of what .version the repository claims\n> to be in.  So even though there are two separate functions to handle\n> \"historical compatibility\" handle_extension_v0() and other extensions,\n> we still end up honoring extensions.objectformat in a repository that\n> does not say what format version it uses.  And clearing them with a\n> call to clear_repository_format() may make sense, but do we want to\n> clear things we read with handle_extension_v0() as well?\n\nNo, it looks like that at first, but I don't think that's correct.\n`verify_repository_format` complains if we have any v1-only extensions\n(such as extensions.objectFormat) and we have version 0.  That's also\nwhere we check whether there are unknown extensions in v1, since we must\nrefuse to read the repository in that case.  There are tests for this\ncase: I added some that v0 with extensions.objectFormat is rejected when\nI did the SHA-256 work, and I think Peff and Patrick may have done some\nadditional cleanup here (and maybe others; my apologies if I've\nforgotten anyone).\n\nThe reason we need to read all the extensions is that different config\noptions aren't ordered and the config callback processes items we see in\nthe order they're in in the config file.  We might first have an\nextensions block and only then a core block, so when reading the\nextensions we don't know yet what the repository version is.\n-- \nbrian m. carlson (they/them)\nToronto, Ontario, CA\n"},{"id":"520513","messageId":"xmqqa5622lgz.fsf@gitster.g","threadId":"63675","inReplyTo":"aFWzTv5-AjUvXWsg@fruit.crustytoothpaste.net","subject":"Re: [PATCH 10/10] Enable SHA-256 by default in breaking changes mode","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-20T20:42:52Z","receivedAt":"2025-06-20T20:42:56Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n> On 2025-06-20 at 15:03:23, Junio C Hamano wrote:\n>> Another thing that I suspect nobody wrote tests for, but we must be\n>> absolutely certain, is that the post-3.0 Git can still interoperate\n>> well with historical SHA-1 repositories (I am not talking about\n>> \"fetch from SHA-1 into SHA-256\", but \"the binary does not lose\n>> ability to work in SHA-1 repositories or fetch/push between SHA-1\n>> repositories, only because the default is set to SHA-256\"), even in\n>> old repositories people have been using for ages without the\n>> core.repositoryformatversion defined.\n>\n> Yes, I have definitely tested that here before sending it out.\n\nIs there a single t/tXXXX-*.sh test that is dedicated to that\ninteroperability, or is it spread across commands (like,\nt????-clone-*.sh has a test that explicitly prepares an SHA-1 and an\nSHA-256 repositories and then tries to clone them with the current\nbinary to make sure the result look reasonable, and t????-push-*.sh\nhas a test to push between a pair of SHA-1 repositories, and a pair\nof SHA-256 repositories, with the current binary)?\n\n> When Git\n> 3.0 comes out, we can switch our GIT_TEST_DEFAULT_HASH test from sha256\n> to sha1 to continue to verify that those work.  As I learned when\n> writing the SHA-256 functionality and as I'm experiencing today writing\n> the interop code, if clones, fetches, and pushes do not work properly,\n> the testsuite is completely broken with at the very least fifty-some-odd\n> tests failing, so I feel confident that functionality will continue to\n> work for SHA-1 as long as we do run an appropriate test job.\n\nOK.\n\n> Also, when we initialize a SHA-1 repository with the files ref backend,\n> we still use repository format version 0 without any extensions, so the\n> cases that cover older-style configs will still be adequately tested. We\n> also have some tests that even test that things work properly without a\n> config file, which caught a bug in this series (that I fixed before\n> sending it out).\n\nVery nice.\n\nThanks.\n"},{"id":"520514","messageId":"aFXH2_PpZrJxJRCs@fruit.crustytoothpaste.net","threadId":"63675","inReplyTo":"xmqq1prf89cd.fsf@gitster.g","subject":"Re: [PATCH 02/10] hash: add a constant for the original hash algorithm","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-06-20T20:43:07Z","receivedAt":"2025-06-20T20:43:09Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2025-06-20 at 01:56:02, Junio C Hamano wrote:\n> \"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n> \n> > We have a a variety of uses of GIT_HASH_SHA1 littered throughout our\n> > code.  Some of these really mean to represent specifically SHA-1, but\n> > some actually represent the original hash algorithm used in Git which is\n> > implied by older formats and protocols which do not contain hash\n> > information.  For instance, the bundle v1 and v2 formats do not contain\n> > hash algorithm information, and thus SHA-1 is implied by the use of\n> > these formats.\n> \n> Does that mean use of _ORIGINAL is a sign that these places should\n> keep using SHA-1 and should not change?\n\nYes.\n\n> I am having a hard time guessing/assessing the value of having _ORIGINAL\n> that is a synonym for _SHA1; with redirection, it pretends as if the\n> underlying value can be updated from SHA-1 to SHA-256 (and that is\n> the very intention behind GIT_HASH_DEFAULT symbol that gives us a\n> level of indirection), but it is hard to imagine we would ever want\n> to change what _ORIGINAL means, as that word talks about a historical\n> fact that will never change over time.\n\nI agree.  _ORIGINAL indicates that this is a use of SHA-1 which is a\nhistorical fact and is a legacy decision as opposed to one specified\nexplicitly.\n\nFor instance, if we're setting the algorithm for bundle v1 and v2, then\nwe'd use _ORIGINAL because those formats did not specify a hash value\nwhen they were designed and, for legacy reasons, we cannot change that\nfact.  However, if with bundle v3, a user specified @object-format=sha1,\nthen we'd use _SHA1, since that was an explicit decision documented.\nSimilarly, _SHA1 represents extensions.objectFormat=sha1, which is an\nintentional decision to use the older algorithm.\n\n> > Add a constant for documentary purposes which indicates this value.  It\n> > will always be the same as SHA-1, since this is an essential part of\n> > these formats, but its use indicates this particular reason and not any\n> > other reason why SHA-1 might be used.\n> \n> I am not sure what this means.  If we use GIT_HASH_SHA1 in such\n> places explicitly (as opposed to GIT_HASH_DEFAULT), isn't it a sign\n> enough that with different versions of Git, that particular code\n> path should keep using SHA-1 no matter what the default is?\n\nIf we have a test helper that computes hashes and someone specified\n\"sha1\" on the command line, that's GIT_HASH_SHA1.  Someone said, \"I'd\nlike to use SHA-1.\"  Similarly, in the reftable code, we can read the\nbyte value indicating that the reftable is in SHA-1 and that's an\nexplicit decision.\n\nIf we default to SHA-1 because nobody specified extensions.objectformat,\nthen that's GIT_HASH_ORIGINAL.  Nobody made a decision or opted into an\nalgorithm; we just didn't think hard enough about cryptographic agility\nin the original Git and we assumed SHA-1.\n\nThey're both the same numeric constant here and always will be (even if,\nin a future version of Git, we get rid of SHA-1 altogether and we\notherwise die on that code).  But there's a difference in intention: one\nexplicitly stated SHA-1 as opposed to a different algorithm and one just\ngot a default because that's the compatible legacy behaviour.\n-- \nbrian m. carlson (they/them)\nToronto, Ontario, CA\n"},{"id":"520515","messageId":"aFXJunWKq28BeX-a@fruit.crustytoothpaste.net","threadId":"63675","inReplyTo":"xmqqcyay7ale.fsf@gitster.g","subject":"Re: [PATCH 04/10] Use original hash for legacy formats","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-06-20T20:51:06Z","receivedAt":"2025-06-20T20:51:08Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2025-06-20 at 14:26:37, Junio C Hamano wrote:\n> If we call use a name with SHA-1 in it (e.g., GIT_HASH_MUST_BE_SHA1)\n> from the beginning, perhaps we do not have to rename _ORIGINAL later?\n\nWe could call it GIT_HASH_LEGACY_SHA1 if you prefer that.  I originally\nconsidered something like GIT_HASH_GOOD_OLD_REV (GOOD_OLD_REV comes from\next2's much more rigid and less extendable v0 rather than its newer v1\nformat), but I felt like that would be too esoteric and not document\nthings well enough.\n\nI'm also open to other ideas for naming if someone has them.  After all,\nnaming things is one of the hard problems in computer science.\n-- \nbrian m. carlson (they/them)\nToronto, Ontario, CA\n"},{"id":"520516","messageId":"xmqqqzze15ug.fsf@gitster.g","threadId":"63675","inReplyTo":"aFXEgLRLRdbaPOb2@fruit.crustytoothpaste.net","subject":"Re: [PATCH 05/10] setup: use the default algorithm to initialize repo format","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-20T21:05:43Z","receivedAt":"2025-06-20T21:05:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n>> > -\tif (format->version == -1)\n>> > +\tif (format->version == -1) {\n>> \n>> And if there is no core.repositoryformatversion set, we will come\n>> here.  According to the comment before handle_extension_v0(), some\n>> extensions.* should still be honored even in such a repository, and\n>> the above call to git_config_from_file() should have handled them\n>> just fine.\n>> \n>> However, I do not understand why we clear all of what we read with\n>> another call to clear_repository_format() here.\n>\n> Because this is the case where there's no config file.  \n\nBut my worries come from that .version == -1 does not necessarily\nmean a missing config.  Missing config will give .version == -1 but\nthe opposite may not be true, no?\n\n> If nobody\n> bothered to write a configuration file, then we want to reset everything\n> to the default.\n\nTrue.  If config file is missing, yes, .version will be -1 and\nclearing may make sense.  But if the file is missing, we wouldn't\nhave anything to \"reset to the default\" because we wouldn't have\nread anything, so what clear_repository_format() call initialized to\nthe default before we read config from file would still be there,\nno?\n\n> I don't know what we do if we have a repository with a config file and\n> no version, but literally every repository since Git 0.99.3 (I believe)\n> has core.repositoryformatversion written into the repo.  I'm certain\n> that the behaviour we'd want if nobody specified one was to do the most\n> compatible thing, so the defaults seem prudent.\n\nIf our assumption is that no config file in repositories we care\nabout should lack core.repositoryformatversion, then what you wrote\nabove makes perfect sense, but then we should probably update the\ncomment before the handle_extension_v0() because it is stale.  The\nnew semantics is that any extension.* found in a config file that\nlacks core.repositoryformatversion will be ignored with the new\ncode, right?  If that is our intention, it should be documented.\n\nThe current code will not clear, so there is a change in behaviour.\nI do not know if we particularly care about this behaviour change,\nthough.\n\n> The reason we need to read all the extensions is that different config\n> options aren't ordered ...\n\nYes, that is where my \"somewhat questionable\" comes from.  We'd have\nto read everything in and then refrain from touching a repository\nwith extensions that should not exist (i.e., the ones we do not\nunderstand, or the ones that should not be used with the stated\nformat version) in verify_repository_format() as you said.\n\nThanks.\n"},{"id":"520517","messageId":"aFXNaItMBiPm8t-_@fruit.crustytoothpaste.net","threadId":"63675","inReplyTo":"xmqqa5622lgz.fsf@gitster.g","subject":"Re: [PATCH 10/10] Enable SHA-256 by default in breaking changes mode","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-06-20T21:06:48Z","receivedAt":"2025-06-20T21:06:50Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2025-06-20 at 20:42:52, Junio C Hamano wrote:\n> \"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n> \n> > On 2025-06-20 at 15:03:23, Junio C Hamano wrote:\n> >> Another thing that I suspect nobody wrote tests for, but we must be\n> >> absolutely certain, is that the post-3.0 Git can still interoperate\n> >> well with historical SHA-1 repositories (I am not talking about\n> >> \"fetch from SHA-1 into SHA-256\", but \"the binary does not lose\n> >> ability to work in SHA-1 repositories or fetch/push between SHA-1\n> >> repositories, only because the default is set to SHA-256\"), even in\n> >> old repositories people have been using for ages without the\n> >> core.repositoryformatversion defined.\n> >\n> > Yes, I have definitely tested that here before sending it out.\n> \n> Is there a single t/tXXXX-*.sh test that is dedicated to that\n> interoperability, or is it spread across commands (like,\n> t????-clone-*.sh has a test that explicitly prepares an SHA-1 and an\n> SHA-256 repositories and then tries to clone them with the current\n> binary to make sure the result look reasonable, and t????-push-*.sh\n> has a test to push between a pair of SHA-1 repositories, and a pair\n> of SHA-256 repositories, with the current binary)?\n\nFor the dual-hash case, I will add interoperability tests in the future\nwhen we get the interoperability code working and I'll include\nsame-hash, different-hash, and dual-hash cases.  I'll also make sure\nthat interop produces the same results in terms of object IDs that a\nnative single-hash implementation provides.  Right now on that code, I'm\nusing GIT_DEFAULT_HASH=sha256:sha1 (which I added) to run the testsuite,\nwhich sets up SHA-256 as the main hash and SHA-1 as the compat hash.\nI'll add a CI job for that in the future.  (If you're interested, this\ncode is living in my sha256-interop branch on GitHub and my local\nserver.)\n\nAs far as the tests we have right now that apply to this series, it's\nspread across a lot of tests.  There are lots of places in the code that\nwe clone a repository to make some changes that we don't want to make in\nthe current repository, for instance, and if clones don't work then\nthose tests are all broken.  The submodule tests actually add a wide\nvariety of nested repositories and push and pull them all over the\nplace, so those also exercise all the cases very well.  We do have clone\nand fetch tests, as well as push tests, for local, HTTP, and SSH, so we\ndo get very comprehensive tests.  Most of those don't specifically\nchoose different hash algorithms, though (although a few do); they only\nwork with whatever the testsuite is doing.\n\nAlso, while I think some basic interoperability tests are helpful,\nthere's no substitute for running the entire testsuite in SHA-1 mode\nbecause there are subtle variations in the protocol (e.g., HTTP is\nstateless) and there are a lot of non-protocol cases we need to\nadequately cover as well (like initializing repositories).  We're not\ngoing to catch all the weird edge cases with a few interoperability\ntests.\n-- \nbrian m. carlson (they/them)\nToronto, Ontario, CA\n"},{"id":"520518","messageId":"xmqqmsa215gi.fsf@gitster.g","threadId":"63675","inReplyTo":"aFXJunWKq28BeX-a@fruit.crustytoothpaste.net","subject":"Re: [PATCH 04/10] Use original hash for legacy formats","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-06-20T21:14:05Z","receivedAt":"2025-06-20T21:14:08Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n> On 2025-06-20 at 14:26:37, Junio C Hamano wrote:\n>> If we call use a name with SHA-1 in it (e.g., GIT_HASH_MUST_BE_SHA1)\n>> from the beginning, perhaps we do not have to rename _ORIGINAL later?\n>\n> We could call it GIT_HASH_LEGACY_SHA1 if you prefer that.  I originally\n> considered something like GIT_HASH_GOOD_OLD_REV (GOOD_OLD_REV comes from\n> ext2's much more rigid and less extendable v0 rather than its newer v1\n> format), but I felt like that would be too esoteric and not document\n> things well enough.\n>\n> I'm also open to other ideas for naming if someone has them.  After all,\n> naming things is one of the hard problems in computer science.\n\nYup, legacy-sha1 is good enough.  I just did not want a name that\ndoes not have sha1 in it.\n\nThanks.\n"},{"id":"521013","messageId":"aGPH-W6htzG0Vwtv@pks.im","threadId":"63675","inReplyTo":"aFXH2_PpZrJxJRCs@fruit.crustytoothpaste.net","subject":"Re: [PATCH 02/10] hash: add a constant for the original hash algorithm","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-01T11:35:21Z","receivedAt":"2025-07-01T11:35:34Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, Jun 20, 2025 at 08:43:07PM +0000, brian m. carlson wrote:\n> On 2025-06-20 at 01:56:02, Junio C Hamano wrote:\n> > \"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n> > \n> > > We have a a variety of uses of GIT_HASH_SHA1 littered throughout our\n> > > code.  Some of these really mean to represent specifically SHA-1, but\n> > > some actually represent the original hash algorithm used in Git which is\n> > > implied by older formats and protocols which do not contain hash\n> > > information.  For instance, the bundle v1 and v2 formats do not contain\n> > > hash algorithm information, and thus SHA-1 is implied by the use of\n> > > these formats.\n> > \n> > Does that mean use of _ORIGINAL is a sign that these places should\n> > keep using SHA-1 and should not change?\n> \n> Yes.\n\nI think this makes sense. There have been a bunch of locations in our\ncode base where I was left wondering whether the use of SHA1 is\nintentional or not. Making these explicit should make it a lot more\nobvious into which of these categories a callsite falls into.\n\n[snip]\n> > > Add a constant for documentary purposes which indicates this value.  It\n> > > will always be the same as SHA-1, since this is an essential part of\n> > > these formats, but its use indicates this particular reason and not any\n> > > other reason why SHA-1 might be used.\n> > \n> > I am not sure what this means.  If we use GIT_HASH_SHA1 in such\n> > places explicitly (as opposed to GIT_HASH_DEFAULT), isn't it a sign\n> > enough that with different versions of Git, that particular code\n> > path should keep using SHA-1 no matter what the default is?\n> \n> If we have a test helper that computes hashes and someone specified\n> \"sha1\" on the command line, that's GIT_HASH_SHA1.  Someone said, \"I'd\n> like to use SHA-1.\"  Similarly, in the reftable code, we can read the\n> byte value indicating that the reftable is in SHA-1 and that's an\n> explicit decision.\n\nTiny nit: even for the reftable format it is not always clear whether it\nis GIT_HASH_SHA1 or GIT_HASH_ORIGINAL. There are two versions of the\nformat:\n\n  - The first version implicitly uses SHA1, so this would be\n    GIT_HASH_ORIGINAL.\n\n  - The second version specifies the hash format, so it would be either\n    GIT_HASH_SHA1 or GIT_HASH_SHA256.\n\nBut again, I think that this distinction is actually useful.\n\n> If we default to SHA-1 because nobody specified extensions.objectformat,\n> then that's GIT_HASH_ORIGINAL.  Nobody made a decision or opted into an\n> algorithm; we just didn't think hard enough about cryptographic agility\n> in the original Git and we assumed SHA-1.\n> \n> They're both the same numeric constant here and always will be (even if,\n> in a future version of Git, we get rid of SHA-1 altogether and we\n> otherwise die on that code).  But there's a difference in intention: one\n> explicitly stated SHA-1 as opposed to a different algorithm and one just\n> got a default because that's the compatible legacy behaviour.\n\nYup.\n\nPatrick\n"},{"id":"521014","messageId":"aGPIBa-1xjqk8EEE@pks.im","threadId":"63675","inReplyTo":"20250620011943.586596-4-sandals@crustytoothpaste.net","subject":"Re: [PATCH 03/10] builtin: use default hash when outside a repository","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-01T11:35:33Z","receivedAt":"2025-07-01T11:35:39Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, Jun 20, 2025 at 01:19:35AM +0000, brian m. carlson wrote:\n> We have some commands that can operate inside or outside a repository.\n> If we're operating outside a repository, we clearly cannot use the\n> repository's hash algorithm as a default since it doesn't exist, so\n> instead, let's pick the default instead of specifically SHA-1.  Right\n> now this results in no functional change since the default is SHA-1, but\n> that may change in the future.\n\nWith the preceding commit in mind that introduced GIT_HASH_ORIGINAL you\ncould also argue that those callsites should be converted to use that\ndefine instead. We always used to treat them as SHA1 repositories, and\nwe have no better way of telling otherwise, so we use the historical\nvalue of SHA1 so that scripts aren't dependent on how exactly Git was\nbuilt.\n\nPatrick\n"},{"id":"521015","messageId":"aGPICjCpQMN4cMKy@pks.im","threadId":"63675","inReplyTo":"xmqqmsa215gi.fsf@gitster.g","subject":"Re: [PATCH 04/10] Use original hash for legacy formats","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-01T11:35:38Z","receivedAt":"2025-07-01T11:35:44Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, Jun 20, 2025 at 02:14:05PM -0700, Junio C Hamano wrote:\n> \"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n> \n> > On 2025-06-20 at 14:26:37, Junio C Hamano wrote:\n> >> If we call use a name with SHA-1 in it (e.g., GIT_HASH_MUST_BE_SHA1)\n> >> from the beginning, perhaps we do not have to rename _ORIGINAL later?\n> >\n> > We could call it GIT_HASH_LEGACY_SHA1 if you prefer that.  I originally\n> > considered something like GIT_HASH_GOOD_OLD_REV (GOOD_OLD_REV comes from\n> > ext2's much more rigid and less extendable v0 rather than its newer v1\n> > format), but I felt like that would be too esoteric and not document\n> > things well enough.\n> >\n> > I'm also open to other ideas for naming if someone has them.  After all,\n> > naming things is one of the hard problems in computer science.\n> \n> Yup, legacy-sha1 is good enough.  I just did not want a name that\n> does not have sha1 in it.\n\nAgreed. I almost started bikeshedding in the patch where you introduced\nthe define, but refrained from doing so. I myself would have proposed\nGIT_HASH_SHA1_HISTORICAL, but calling it \"legacy\" is even better.\n\nPatrick\n"},{"id":"521016","messageId":"aGPIEHG8e0i9nzxV@pks.im","threadId":"63675","inReplyTo":"20250620011943.586596-11-sandals@crustytoothpaste.net","subject":"Re: [PATCH 10/10] Enable SHA-256 by default in breaking changes mode","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-01T11:35:44Z","receivedAt":"2025-07-01T11:35:50Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Fri, Jun 20, 2025 at 01:19:42AM +0000, brian m. carlson wrote:\n> Our document on breaking changes indicates that we intend to default to\n> SHA-256 in Git 3.0.  Since most people choose the default option, this\n> is an important security upgrade to our defaults.\n> \n> To allow people to test this case, when WITH_BREAKING_CHANGES is set in\n> the configuration, build Git with SHA-256 as the default hash.  Update\n> the testsuite to reflect this configuration so that the tests pass.\n\nAwesome. Thanks for advancing our migration towards SHA256!\n\n> diff --git a/t/test-lib.sh b/t/test-lib.sh\n> index ef3759ec80..bb18dd0606 100644\n> --- a/t/test-lib.sh\n> +++ b/t/test-lib.sh\n> @@ -536,7 +536,12 @@ export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME\n>  export GIT_COMMITTER_DATE GIT_AUTHOR_DATE\n>  export EDITOR\n>  \n> -GIT_TEST_BUILTIN_HASH=sha1\n> +if test -n \"$WITH_BREAKING_CHANGES\"\n> +then\n> +\tGIT_TEST_BUILTIN_HASH=sha256\n> +else\n> +\tGIT_TEST_BUILTIN_HASH=sha1\n> +fi\n\nThere should probably be an option somewhere in Git to ask it what its\ncurrent builtin hash is. If so, you wouldn't have to hardcode the hash\nover here but could ask for example `git version --builtin-hash`.\n\nPatrick\n"},{"id":"521110","messageId":"aGRP0WiG8ZRzgilp@fruit.crustytoothpaste.net","threadId":"63675","inReplyTo":"aGPIBa-1xjqk8EEE@pks.im","subject":"Re: [PATCH 03/10] builtin: use default hash when outside a repository","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-07-01T21:14:57Z","receivedAt":"2025-07-01T21:15:00Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2025-07-01 at 11:35:33, Patrick Steinhardt wrote:\n> On Fri, Jun 20, 2025 at 01:19:35AM +0000, brian m. carlson wrote:\n> > We have some commands that can operate inside or outside a repository.\n> > If we're operating outside a repository, we clearly cannot use the\n> > repository's hash algorithm as a default since it doesn't exist, so\n> > instead, let's pick the default instead of specifically SHA-1.  Right\n> > now this results in no functional change since the default is SHA-1, but\n> > that may change in the future.\n> \n> With the preceding commit in mind that introduced GIT_HASH_ORIGINAL you\n> could also argue that those callsites should be converted to use that\n> define instead. We always used to treat them as SHA1 repositories, and\n> we have no better way of telling otherwise, so we use the historical\n> value of SHA1 so that scripts aren't dependent on how exactly Git was\n> built.\n\nI don't think I want to do that.  A lot of the functionality people use\noutside of repositories, such as index-pack and ls-remote, actually\noperates on repository objects and so it makes sense to use the default.\n\nFor instance, it will be a major inconvenience to still have to specify\na custom object format three to five years after the switchover.\n-- \nbrian m. carlson (they/them)\nToronto, Ontario, CA\n"},{"id":"521111","messageId":"20250701212237.766774-3-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250701212237.766774-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 02/11] hash: add a constant for the legacy hash algorithm","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-07-01T21:22:28Z","receivedAt":"2025-07-01T21:22:42Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"We have a a variety of uses of GIT_HASH_SHA1 littered throughout our\ncode.  Some of these really mean to represent specifically SHA-1, but\nsome actually represent the original hash algorithm used in Git which is\nimplied by older, legacy formats and protocols which do not contain hash\ninformation.  For instance, the bundle v1 and v2 formats do not contain\nhash algorithm information, and thus SHA-1 is implied by the use of\nthese formats.\n\nAdd a constant for documentary purposes which indicates this value.  It\nwill always be the same as SHA-1, since this is an essential part of\nthese formats, but its use indicates this particular reason and not any\nother reason why SHA-1 might be used.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n hash.h | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/hash.h b/hash.h\nindex 0d3d85e04c..953e840d15 100644\n--- a/hash.h\n+++ b/hash.h\n@@ -176,6 +176,8 @@ static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *s\n #define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)\n /* Default hash algorithm if unspecified. */\n #define GIT_HASH_DEFAULT GIT_HASH_SHA1\n+/* Legacy hash algorithm. Implied for older data formats which don't specify. */\n+#define GIT_HASH_SHA1_LEGACY GIT_HASH_SHA1\n \n /* \"sha1\", big-endian */\n #define GIT_SHA1_FORMAT_ID 0x73686131\n"},{"id":"521112","messageId":"20250701212237.766774-2-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250701212237.766774-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 01/11] hash: add a constant for the default hash algorithm","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-07-01T21:22:27Z","receivedAt":"2025-07-01T21:22:42Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Right now, SHA-1 is the default hash algorithm in Git.  However, this\nmay change in the future.\n\nWe have many places in our code that use the SHA-1 constant to indicate\nthe default hash if none is specified, but it will end up being more\npractical to specify this explicitly and clearly using a constant for\nwhatever the default hash algorithm is.  Then, if we decide to change it\nin the future, we can simply replace the constant representing the\ndefault with a new value.\n\nFor these reasons, introduce GIT_HASH_DEFAULT to represent the default\nhash algorithm.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n hash.h | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/hash.h b/hash.h\nindex d6422ddf45..0d3d85e04c 100644\n--- a/hash.h\n+++ b/hash.h\n@@ -174,6 +174,8 @@ static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *s\n #define GIT_HASH_SHA256 2\n /* Number of algorithms supported (including unknown). */\n #define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)\n+/* Default hash algorithm if unspecified. */\n+#define GIT_HASH_DEFAULT GIT_HASH_SHA1\n \n /* \"sha1\", big-endian */\n #define GIT_SHA1_FORMAT_ID 0x73686131\n"},{"id":"521113","messageId":"20250701212237.766774-5-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250701212237.766774-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 04/11] Use legacy hash for legacy formats","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-07-01T21:22:30Z","receivedAt":"2025-07-01T21:22:42Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"We have a large variety of data formats and protocols where no hash\nalgorithm was defined and the default was assumed to always be SHA-1.\nInstead of explicitly stating SHA-1, let's use the constant to represent\nthe legacy hash algorithm (which is still SHA-1) so that it's clear\nfor documentary purposes that it's a legacy fallback option and not an\nintentional choice to use SHA-1.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n builtin/receive-pack.c | 2 +-\n bundle.c               | 4 ++--\n connect.c              | 6 +++---\n fetch-pack.c           | 2 +-\n pkt-line.c             | 2 +-\n remote-curl.c          | 2 +-\n serve.c                | 2 +-\n setup.c                | 4 ++--\n transport.c            | 2 +-\n 9 files changed, 13 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex a317d6c278..24b33a3a5c 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -2136,7 +2136,7 @@ static struct command *read_head_info(struct packet_reader *reader,\n \t\t\t\tuse_push_options = 1;\n \t\t\thash = parse_feature_value(feature_list, \"object-format\", &len, NULL);\n \t\t\tif (!hash) {\n-\t\t\t\thash = hash_algos[GIT_HASH_SHA1].name;\n+\t\t\t\thash = hash_algos[GIT_HASH_SHA1_LEGACY].name;\n \t\t\t\tlen = strlen(hash);\n \t\t\t}\n \t\t\tif (xstrncmpz(the_hash_algo->name, hash, len))\ndiff --git a/bundle.c b/bundle.c\nindex b0a3fee2ef..61e81bb0c3 100644\n--- a/bundle.c\n+++ b/bundle.c\n@@ -95,7 +95,7 @@ int read_bundle_header_fd(int fd, struct bundle_header *header,\n \t * by an \"object-format=\" capability, which is being handled in\n \t * `parse_capability()`.\n \t */\n-\theader->hash_algo = &hash_algos[GIT_HASH_SHA1];\n+\theader->hash_algo = &hash_algos[GIT_HASH_SHA1_LEGACY];\n \n \t/* The bundle header ends with an empty line */\n \twhile (!strbuf_getwholeline_fd(&buf, fd, '\\n') &&\n@@ -507,7 +507,7 @@ int create_bundle(struct repository *r, const char *path,\n \t *    SHA1.\n \t * 2. @filter is required because we parsed an object filter.\n \t */\n-\tif (the_hash_algo != &hash_algos[GIT_HASH_SHA1] || revs.filter.choice)\n+\tif (the_hash_algo != &hash_algos[GIT_HASH_SHA1_LEGACY] || revs.filter.choice)\n \t\tmin_version = 3;\n \n \tif (argc > 1) {\ndiff --git a/connect.c b/connect.c\nindex 3280435331..e77287f426 100644\n--- a/connect.c\n+++ b/connect.c\n@@ -251,7 +251,7 @@ static void process_capabilities(struct packet_reader *reader, size_t *linelen)\n \t\t\treader->hash_algo = &hash_algos[hash_algo];\n \t\tfree(hash_name);\n \t} else {\n-\t\treader->hash_algo = &hash_algos[GIT_HASH_SHA1];\n+\t\treader->hash_algo = &hash_algos[GIT_HASH_SHA1_LEGACY];\n \t}\n }\n \n@@ -500,7 +500,7 @@ static void send_capabilities(int fd_out, struct packet_reader *reader)\n \t\treader->hash_algo = &hash_algos[hash_algo];\n \t\tpacket_write_fmt(fd_out, \"object-format=%s\", reader->hash_algo->name);\n \t} else {\n-\t\treader->hash_algo = &hash_algos[GIT_HASH_SHA1];\n+\t\treader->hash_algo = &hash_algos[GIT_HASH_SHA1_LEGACY];\n \t}\n \tif (server_feature_v2(\"promisor-remote\", &promisor_remote_info)) {\n \t\tchar *reply = promisor_remote_reply(promisor_remote_info);\n@@ -665,7 +665,7 @@ int server_supports_hash(const char *desired, int *feature_supported)\n \tif (feature_supported)\n \t\t*feature_supported = !!hash;\n \tif (!hash) {\n-\t\thash = hash_algos[GIT_HASH_SHA1].name;\n+\t\thash = hash_algos[GIT_HASH_SHA1_LEGACY].name;\n \t\tlen = strlen(hash);\n \t}\n \twhile (hash) {\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex fa4231fee7..95f66ffc1d 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -1342,7 +1342,7 @@ static void write_fetch_command_and_capabilities(struct strbuf *req_buf,\n \t\t\tdie(_(\"mismatched algorithms: client %s; server %s\"),\n \t\t\t    the_hash_algo->name, hash_name);\n \t\tpacket_buf_write(req_buf, \"object-format=%s\", the_hash_algo->name);\n-\t} else if (hash_algo_by_ptr(the_hash_algo) != GIT_HASH_SHA1) {\n+\t} else if (hash_algo_by_ptr(the_hash_algo) != GIT_HASH_SHA1_LEGACY) {\n \t\tdie(_(\"the server does not support algorithm '%s'\"),\n \t\t    the_hash_algo->name);\n \t}\ndiff --git a/pkt-line.c b/pkt-line.c\nindex a5bcbc96fb..fc583feb26 100644\n--- a/pkt-line.c\n+++ b/pkt-line.c\n@@ -617,7 +617,7 @@ void packet_reader_init(struct packet_reader *reader, int fd,\n \treader->buffer_size = sizeof(packet_buffer);\n \treader->options = options;\n \treader->me = \"git\";\n-\treader->hash_algo = &hash_algos[GIT_HASH_SHA1];\n+\treader->hash_algo = &hash_algos[GIT_HASH_SHA1_LEGACY];\n \tstrbuf_init(&reader->scratch, 0);\n }\n \ndiff --git a/remote-curl.c b/remote-curl.c\nindex b8bc3a80cf..84f4694780 100644\n--- a/remote-curl.c\n+++ b/remote-curl.c\n@@ -285,7 +285,7 @@ static const struct git_hash_algo *detect_hash_algo(struct discovery *heads)\n \t * back to SHA1, which may or may not be correct.\n \t */\n \tif (!p)\n-\t\treturn &hash_algos[GIT_HASH_SHA1];\n+\t\treturn &hash_algos[GIT_HASH_SHA1_LEGACY];\n \n \talgo = hash_algo_by_length((p - heads->buf) / 2);\n \tif (algo == GIT_HASH_UNKNOWN)\ndiff --git a/serve.c b/serve.c\nindex e3ccf1505c..53ecab3b42 100644\n--- a/serve.c\n+++ b/serve.c\n@@ -14,7 +14,7 @@\n \n static int advertise_sid = -1;\n static int advertise_object_info = -1;\n-static int client_hash_algo = GIT_HASH_SHA1;\n+static int client_hash_algo = GIT_HASH_SHA1_LEGACY;\n \n static int always_advertise(struct repository *r UNUSED,\n \t\t\t    struct strbuf *value UNUSED)\ndiff --git a/setup.c b/setup.c\nindex f93bd6a24a..3d2b3e745b 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -2222,11 +2222,11 @@ void initialize_repository_version(int hash_algo,\n \t * version will get adjusted by git-clone(1) once it has learned about\n \t * the remote repository's format.\n \t */\n-\tif (hash_algo != GIT_HASH_SHA1 ||\n+\tif (hash_algo != GIT_HASH_SHA1_LEGACY ||\n \t    ref_storage_format != REF_STORAGE_FORMAT_FILES)\n \t\ttarget_version = GIT_REPO_VERSION_READ;\n \n-\tif (hash_algo != GIT_HASH_SHA1 && hash_algo != GIT_HASH_UNKNOWN)\n+\tif (hash_algo != GIT_HASH_SHA1_LEGACY && hash_algo != GIT_HASH_UNKNOWN)\n \t\tgit_config_set(\"extensions.objectformat\",\n \t\t\t       hash_algos[hash_algo].name);\n \telse if (reinit)\ndiff --git a/transport.c b/transport.c\nindex 6c2801bcbd..c123ac1e38 100644\n--- a/transport.c\n+++ b/transport.c\n@@ -1243,7 +1243,7 @@ struct transport *transport_get(struct remote *remote, const char *url)\n \t\t\tret->smart_options->receivepack = remote->receivepack;\n \t}\n \n-\tret->hash_algo = &hash_algos[GIT_HASH_SHA1];\n+\tret->hash_algo = &hash_algos[GIT_HASH_SHA1_LEGACY];\n \n \treturn ret;\n }\n"},{"id":"521114","messageId":"20250701212237.766774-4-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250701212237.766774-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 03/11] builtin: use default hash when outside a repository","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-07-01T21:22:29Z","receivedAt":"2025-07-01T21:22:43Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"We have some commands that can operate inside or outside a repository.\nIf we're operating outside a repository, we clearly cannot use the\nrepository's hash algorithm as a default since it doesn't exist, so\ninstead, let's pick the default instead of specifically SHA-1.  Right\nnow this results in no functional change since the default is SHA-1, but\nthat may change in the future.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n builtin/apply.c       | 2 +-\n builtin/diff.c        | 2 +-\n builtin/hash-object.c | 2 +-\n builtin/index-pack.c  | 2 +-\n builtin/ls-remote.c   | 2 +-\n builtin/patch-id.c    | 2 +-\n builtin/shortlog.c    | 2 +-\n builtin/show-index.c  | 2 +-\n 8 files changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/apply.c b/builtin/apply.c\nindex a1e20c593d..d642a40251 100644\n--- a/builtin/apply.c\n+++ b/builtin/apply.c\n@@ -29,7 +29,7 @@ int cmd_apply(int argc,\n \t * cf. https://lore.kernel.org/git/xmqqcypfcmn4.fsf@gitster.g/\n \t */\n \tif (!the_hash_algo)\n-\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n+\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n \n \targc = apply_parse_options(argc, argv,\n \t\t\t\t   &state, &force_apply, &options,\ndiff --git a/builtin/diff.c b/builtin/diff.c\nindex c6231edce4..eebffe36cc 100644\n--- a/builtin/diff.c\n+++ b/builtin/diff.c\n@@ -483,7 +483,7 @@ int cmd_diff(int argc,\n \t * configurable via a command line option.\n \t */\n \tif (nongit)\n-\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n+\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n \n \tinit_diff_ui_defaults();\n \tgit_config(git_diff_ui_config, NULL);\ndiff --git a/builtin/hash-object.c b/builtin/hash-object.c\nindex 6a99ec250d..213a302e05 100644\n--- a/builtin/hash-object.c\n+++ b/builtin/hash-object.c\n@@ -104,7 +104,7 @@ int cmd_hash_object(int argc,\n \t\tprefix = setup_git_directory_gently(&nongit);\n \n \tif (nongit && !the_hash_algo)\n-\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n+\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n \n \tif (vpath && prefix) {\n \t\tvpath_free = prefix_filename(prefix, vpath);\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex bb7925bd29..352ce7f88a 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -2034,7 +2034,7 @@ int cmd_index_pack(int argc,\n \t * choice but to guess the object hash.\n \t */\n \tif (!the_repository->hash_algo)\n-\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n+\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n \n \topts.flags &= ~(WRITE_REV | WRITE_REV_VERIFY);\n \tif (rev_index) {\ndiff --git a/builtin/ls-remote.c b/builtin/ls-remote.c\nindex 01a4d4daa1..df09000b30 100644\n--- a/builtin/ls-remote.c\n+++ b/builtin/ls-remote.c\n@@ -112,7 +112,7 @@ int cmd_ls_remote(int argc,\n \t * depending on what object hash the remote uses.\n \t */\n \tif (!the_repository->hash_algo)\n-\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n+\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n \n \tpacket_trace_identity(\"ls-remote\");\n \ndiff --git a/builtin/patch-id.c b/builtin/patch-id.c\nindex cdef2ec10a..26f04b0335 100644\n--- a/builtin/patch-id.c\n+++ b/builtin/patch-id.c\n@@ -254,7 +254,7 @@ int cmd_patch_id(int argc,\n \t * the code that computes patch IDs to always use SHA1.\n \t */\n \tif (!the_hash_algo)\n-\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n+\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n \n \tgenerate_id_list(opts ? opts > 1 : config.stable,\n \t\t\t opts ? opts == 3 : config.verbatim);\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex fe15e11497..60adc5e7a5 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -419,7 +419,7 @@ int cmd_shortlog(int argc,\n \t * git/nongit so that we do not have to do this.\n \t */\n \tif (nongit && !the_hash_algo)\n-\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n+\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n \n \tgit_config(git_default_config, NULL);\n \tshortlog_init(&log);\ndiff --git a/builtin/show-index.c b/builtin/show-index.c\nindex 9d4ecf5e7b..2c3e2940ce 100644\n--- a/builtin/show-index.c\n+++ b/builtin/show-index.c\n@@ -47,7 +47,7 @@ int cmd_show_index(int argc,\n \t *       the index file passed in and use that instead.\n \t */\n \tif (!the_hash_algo)\n-\t\trepo_set_hash_algo(the_repository, GIT_HASH_SHA1);\n+\t\trepo_set_hash_algo(the_repository, GIT_HASH_DEFAULT);\n \n \thashsz = the_hash_algo->rawsz;\n \n"},{"id":"521115","messageId":"20250701212237.766774-1-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250620011943.586596-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 00/11] Add SHA-256 by default as a breaking change","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-07-01T21:22:26Z","receivedAt":"2025-07-01T21:22:43Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Our breaking changes document outlines that Git 3.0 will make SHA-256\nthe default hash algorithm, which is a sensible and prudent approach,\nespecially from a security perspective.  However, we haven't tested this\nadequately and it would be helpful to allow users to test this behaviour\nso their code and environments are ready for it.\n\nFortunately, c5bc9a7f94 (Makefile: wire up build option for deprecated\nfeatures, 2025-01-22) introduces a build option that we can use for\ntesting breaking changes: WITH_BREAKING_CHANGES.  This series introduces\nfunctionality for SHA-256 by default in this mode so we can test it out.\n\nChanges since v1:\n* Add a build option for the default hash and use it in the tests.\n* Rename GIT_HASH_ORIGINAL to GIT_HASH_SHA1_LEGACY.\n* Improve some of the commit messages to better explain questions that\n  have come up for review.\n* Improve formatting of nested C preprocessor directives.\n\nbrian m. carlson (11):\n  hash: add a constant for the default hash algorithm\n  hash: add a constant for the legacy hash algorithm\n  builtin: use default hash when outside a repository\n  Use legacy hash for legacy formats\n  setup: use the default algorithm to initialize repo format\n  t: default to compile-time default hash if not set\n  t1007: choose the built-in hash outside of a repo\n  t4042: choose the built-in hash outside of a repo\n  t5300: choose the built-in hash outside of a repo\n  help: add a build option for default hash\n  Enable SHA-256 by default in breaking changes mode\n\n builtin/apply.c                  |  2 +-\n builtin/diff.c                   |  2 +-\n builtin/hash-object.c            |  2 +-\n builtin/index-pack.c             |  2 +-\n builtin/ls-remote.c              |  2 +-\n builtin/patch-id.c               |  2 +-\n builtin/receive-pack.c           |  2 +-\n builtin/shortlog.c               |  2 +-\n builtin/show-index.c             |  2 +-\n bundle.c                         |  4 ++--\n connect.c                        |  6 +++---\n fetch-pack.c                     |  2 +-\n hash.h                           | 10 ++++++++++\n help.c                           |  1 +\n pkt-line.c                       |  2 +-\n remote-curl.c                    |  2 +-\n serve.c                          |  2 +-\n setup.c                          |  9 ++++++---\n setup.h                          |  2 +-\n t/t1007-hash-object.sh           |  4 ++--\n t/t4042-diff-textconv-caching.sh | 12 ++++++++++--\n t/t5300-pack-object.sh           |  6 +++---\n t/test-lib-functions.sh          |  5 ++++-\n t/test-lib.sh                    |  7 ++++++-\n transport.c                      |  2 +-\n 25 files changed, 62 insertions(+), 32 deletions(-)\n\n"},{"id":"521116","messageId":"20250701212237.766774-7-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250701212237.766774-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 06/11] t: default to compile-time default hash if not set","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-07-01T21:22:32Z","receivedAt":"2025-07-01T21:22:45Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Right now, the default compile-time hash is SHA-1.  However, in the\nfuture, this might change and it would be helpful to gracefully handle\nthis case in our testsuite.\n\nTo avoid making these assumptions, let's introduce a variable that\ncontains the built-in default hash and use it in our setup code as the\nfallback value if no hash was explicitly set.  For now, this is always\nSHA-1, but in a future commit, we'll allow adjusting this and the\nvariable will be more useful.\n\nTo allow us to make our tests more robust, allow test_oid to take the\n--hash=builtin option to specify this hash, whatever it is.\n\nAdditionally, add a DEFAULT_HASH_ALGORITHM prerequisite to check for the\ncompile-time hash.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/test-lib-functions.sh | 5 ++++-\n t/test-lib.sh           | 7 ++++++-\n 2 files changed, 10 insertions(+), 2 deletions(-)\n\ndiff --git a/t/test-lib-functions.sh b/t/test-lib-functions.sh\nindex bee4a2ca34..6ec95ea51f 100644\n--- a/t/test-lib-functions.sh\n+++ b/t/test-lib-functions.sh\n@@ -1695,7 +1695,7 @@ test_set_hash () {\n \n # Detect the hash algorithm in use.\n test_detect_hash () {\n-\tcase \"$GIT_TEST_DEFAULT_HASH\" in\n+\tcase \"${GIT_TEST_DEFAULT_HASH:-$GIT_TEST_BUILTIN_HASH}\" in\n \t\"sha256\")\n \t    test_hash_algo=sha256\n \t    test_compat_hash_algo=sha1\n@@ -1767,6 +1767,9 @@ test_oid () {\n \t--hash=compat)\n \t\talgo=\"$test_compat_hash_algo\" &&\n \t\tshift;;\n+\t--hash=builtin)\n+\t\talgo=\"$GIT_TEST_BUILTIN_HASH\" &&\n+\t\tshift;;\n \t--hash=*)\n \t\talgo=\"${1#--hash=}\" &&\n \t\tshift;;\ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex 51370a201c..ef3759ec80 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -536,7 +536,8 @@ export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME\n export GIT_COMMITTER_DATE GIT_AUTHOR_DATE\n export EDITOR\n \n-GIT_DEFAULT_HASH=\"${GIT_TEST_DEFAULT_HASH:-sha1}\"\n+GIT_TEST_BUILTIN_HASH=sha1\n+GIT_DEFAULT_HASH=\"${GIT_TEST_DEFAULT_HASH:-$GIT_TEST_BUILTIN_HASH}\"\n export GIT_DEFAULT_HASH\n GIT_DEFAULT_REF_FORMAT=\"${GIT_TEST_DEFAULT_REF_FORMAT:-files}\"\n export GIT_DEFAULT_REF_FORMAT\n@@ -1908,6 +1909,10 @@ test_lazy_prereq SHA1 '\n \tesac\n '\n \n+test_lazy_prereq DEFAULT_HASH_ALGORITHM '\n+\ttest \"$GIT_TEST_BUILTIN_HASH\" = \"$GIT_DEFAULT_HASH\"\n+'\n+\n test_lazy_prereq DEFAULT_REPO_FORMAT '\n \ttest_have_prereq SHA1,REFFILES\n '\n"},{"id":"521117","messageId":"20250701212237.766774-6-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250701212237.766774-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 05/11] setup: use the default algorithm to initialize repo format","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-07-01T21:22:31Z","receivedAt":"2025-07-01T21:22:45Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"When we define a new repository format with REPOSITORY_FORMAT_INIT, we\nalways use GIT_HASH_SHA1, and this value ends up getting used as the\ndefault value to initialize a repository if none of the command line,\nenvironment, or config tell us to do otherwise.\n\nBecause we might not always want to use SHA-1 as the default, let's\ninstead specify the default hash algorithm constant so that we will use\nwhatever the specified default is.\n\nHowever, we also need to continue to read older repositories.  If we're\nin a v0 repository or extensions.objectformat is not set, then we must\ncontinue to default to the original hash algorithm: SHA-1.  If an\nalgorithm is set explicitly, however, it will override the hash_algo\nmember of the repository_format struct and we'll get the right value.\n\nSimilarly, if the repository was initialized before Git 0.99.3, then it\nmay lack a core.repositoryformatversion key, and some repositories lack\na config file altogether.  In both cases, format->version is -1 and we\nneed to assume that SHA-1 is in use.\n\nBecause clear_repository_format reinitializes the struct\nrepository_format and therefore sets the hash_algo member to the default\n(which could in the future not be SHA-1), we need to reset this member\nexplicitly.  We know, however, that at the point we call\nread_repository_format, we are actually reading an existing repository\nand not initializing a new one or operating outside of a repository, so\nwe are not changing the default behavior back to SHA-1 if the default\nalgorithm is different.\n\nIt is potentially questionable that we ignore all repository\nconfiguration if there is a config file but it doesn't have\ncore.repositoryformatversion set, in which case we reset all of the\nconfiguration to the default.  However, it is unclear what the right\nthing to do instead with such an old repository is and a simple git init\nwill add the missing entry, so for now, we simply honor what the\nexisting code does and reset the value to the default, simply adding our\ninitialization to SHA-1.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n setup.c | 5 ++++-\n setup.h | 2 +-\n 2 files changed, 5 insertions(+), 2 deletions(-)\n\ndiff --git a/setup.c b/setup.c\nindex 3d2b3e745b..03a61bd06a 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -835,9 +835,12 @@ static void init_repository_format(struct repository_format *format)\n int read_repository_format(struct repository_format *format, const char *path)\n {\n \tclear_repository_format(format);\n+\tformat->hash_algo = GIT_HASH_SHA1_LEGACY;\n \tgit_config_from_file(check_repo_format, path, format);\n-\tif (format->version == -1)\n+\tif (format->version == -1) {\n \t\tclear_repository_format(format);\n+\t\tformat->hash_algo = GIT_HASH_SHA1_LEGACY;\n+\t}\n \treturn format->version;\n }\n \ndiff --git a/setup.h b/setup.h\nindex 18dc3b7368..8522fa8575 100644\n--- a/setup.h\n+++ b/setup.h\n@@ -149,7 +149,7 @@ struct repository_format {\n { \\\n \t.version = -1, \\\n \t.is_bare = -1, \\\n-\t.hash_algo = GIT_HASH_SHA1, \\\n+\t.hash_algo = GIT_HASH_DEFAULT, \\\n \t.ref_storage_format = REF_STORAGE_FORMAT_FILES, \\\n \t.unknown_extensions = STRING_LIST_INIT_DUP, \\\n \t.v1_only_extensions = STRING_LIST_INIT_DUP, \\\n"},{"id":"521118","messageId":"20250701212237.766774-9-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250701212237.766774-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 08/11] t4042: choose the built-in hash outside of a repo","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-07-01T21:22:34Z","receivedAt":"2025-07-01T21:22:45Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Right now, the built-in default hash is always SHA-1, but that will\nchange in a future commit.  Instead of assuming that operating outside\nof a repository will always use SHA-1, provide constants for both\nalgorithms and then simply ask test_oid for the built-in hash instead,\nwhich will always be correct.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t4042-diff-textconv-caching.sh | 12 ++++++++++--\n 1 file changed, 10 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t4042-diff-textconv-caching.sh b/t/t4042-diff-textconv-caching.sh\nindex ff0e73531b..31018ceba2 100755\n--- a/t/t4042-diff-textconv-caching.sh\n+++ b/t/t4042-diff-textconv-caching.sh\n@@ -120,6 +120,14 @@ test_expect_success 'log notes cache and still use cache for -p' '\n '\n \n test_expect_success 'caching is silently ignored outside repo' '\n+\ttest_oid_cache <<-\\EOM &&\n+\toid1 sha1:5626abf\n+\toid1 sha256:a4ed1f3\n+\toid2 sha1:f719efd\n+\toid2 sha256:aa9e7dc\n+\tEOM\n+\toid1=$(test_oid --hash=builtin oid1) &&\n+\toid2=$(test_oid --hash=builtin oid2) &&\n \tmkdir -p non-repo &&\n \techo one >non-repo/one &&\n \techo two >non-repo/two &&\n@@ -129,9 +137,9 @@ test_expect_success 'caching is silently ignored outside repo' '\n \t\t   -c diff.test.textconv=\"tr a-z A-Z <\" \\\n \t\t   -c diff.test.cachetextconv=true \\\n \t\t   diff --no-index one two >actual &&\n-\tcat >expect <<-\\EOF &&\n+\tcat >expect <<-EOF &&\n \tdiff --git a/one b/two\n-\tindex 5626abf..f719efd 100644\n+\tindex $oid1..$oid2 100644\n \t--- a/one\n \t+++ b/two\n \t@@ -1 +1 @@\n"},{"id":"521121","messageId":"20250701212237.766774-8-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250701212237.766774-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 07/11] t1007: choose the built-in hash outside of a repo","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-07-01T21:22:33Z","receivedAt":"2025-07-01T21:22:45Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Right now, the built-in default hash is always SHA-1, but that will\nchange in a future commit.  Instead of assuming that operating outside\nof a repository will always use SHA-1, simply ask test_oid for the\nbuilt-in hash instead, which will always be correct.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t1007-hash-object.sh | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh\nindex 64658b3ba5..de076293b6 100755\n--- a/t/t1007-hash-object.sh\n+++ b/t/t1007-hash-object.sh\n@@ -252,9 +252,9 @@ test_expect_success '--literally complains about non-standard types' '\n \ttest_must_fail git hash-object -t bogus --literally --stdin\n '\n \n-test_expect_success '--stdin outside of repository (uses SHA-1)' '\n+test_expect_success '--stdin outside of repository (uses default hash)' '\n \tnongit git hash-object --stdin <hello >actual &&\n-\techo \"$(test_oid --hash=sha1 hello)\" >expect &&\n+\techo \"$(test_oid --hash=builtin hello)\" >expect &&\n \ttest_cmp expect actual\n '\n \n"},{"id":"521119","messageId":"20250701212237.766774-11-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250701212237.766774-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 10/11] help: add a build option for default hash","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-07-01T21:22:36Z","receivedAt":"2025-07-01T21:22:46Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"We'd like users to be able to determine the hash algorithm that is the\nbuiltin default in their version of Git.  This is useful for\ntroubleshooting, especially when we decide to change the default.  Add\nan entry for the default hash in the output of git version\n--build-options so that users can easily access that information and\ninclude it in bug reports.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n help.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/help.c b/help.c\nindex 21b778707a..bd0be2ee57 100644\n--- a/help.c\n+++ b/help.c\n@@ -810,6 +810,7 @@ void get_version_info(struct strbuf *buf, int show_build_options)\n \t\t\t    SHA1_UNSAFE_BACKEND);\n #endif\n \t\tstrbuf_addf(buf, \"SHA-256: %s\\n\", SHA256_BACKEND);\n+\t\tstrbuf_addf(buf, \"default-hash: %s\\n\", hash_algos[GIT_HASH_DEFAULT].name);\n \t}\n }\n \n"},{"id":"521120","messageId":"20250701212237.766774-12-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250701212237.766774-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 11/11] Enable SHA-256 by default in breaking changes mode","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-07-01T21:22:37Z","receivedAt":"2025-07-01T21:22:46Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Our document on breaking changes indicates that we intend to default to\nSHA-256 in Git 3.0.  Since most people choose the default option, this\nis an important security upgrade to our defaults.\n\nTo allow people to test this case, when WITH_BREAKING_CHANGES is set in\nthe configuration, build Git with SHA-256 as the default hash.  Update\nthe testsuite to use the build options information to automatically\nchoose the right value.\n\nNote that if the command substitution for GIT_TEST_BUILTIN_HASH fails,\nso does the testsuite—and quite spectacularly at that.  Thus, the case\nwhere the Git binary is somehow subtly broken will not go undetected.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n hash.h        | 8 +++++++-\n t/test-lib.sh | 2 +-\n 2 files changed, 8 insertions(+), 2 deletions(-)\n\ndiff --git a/hash.h b/hash.h\nindex 953e840d15..3fcbe9bcba 100644\n--- a/hash.h\n+++ b/hash.h\n@@ -174,8 +174,14 @@ static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *s\n #define GIT_HASH_SHA256 2\n /* Number of algorithms supported (including unknown). */\n #define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)\n+\n /* Default hash algorithm if unspecified. */\n-#define GIT_HASH_DEFAULT GIT_HASH_SHA1\n+#ifdef WITH_BREAKING_CHANGES\n+# define GIT_HASH_DEFAULT GIT_HASH_SHA256\n+#else\n+# define GIT_HASH_DEFAULT GIT_HASH_SHA1\n+#endif\n+\n /* Legacy hash algorithm. Implied for older data formats which don't specify. */\n #define GIT_HASH_SHA1_LEGACY GIT_HASH_SHA1\n \ndiff --git a/t/test-lib.sh b/t/test-lib.sh\nindex ef3759ec80..6dc2022ee1 100644\n--- a/t/test-lib.sh\n+++ b/t/test-lib.sh\n@@ -536,7 +536,7 @@ export GIT_COMMITTER_EMAIL GIT_COMMITTER_NAME\n export GIT_COMMITTER_DATE GIT_AUTHOR_DATE\n export EDITOR\n \n-GIT_TEST_BUILTIN_HASH=sha1\n+GIT_TEST_BUILTIN_HASH=$(\"$GIT_BUILD_DIR/git\" version --build-options | sed -ne 's/^default-hash: //p')\n GIT_DEFAULT_HASH=\"${GIT_TEST_DEFAULT_HASH:-$GIT_TEST_BUILTIN_HASH}\"\n export GIT_DEFAULT_HASH\n GIT_DEFAULT_REF_FORMAT=\"${GIT_TEST_DEFAULT_REF_FORMAT:-files}\"\n"},{"id":"521122","messageId":"20250701212237.766774-10-sandals@crustytoothpaste.net","threadId":"63675","inReplyTo":"20250701212237.766774-1-sandals@crustytoothpaste.net","subject":"[PATCH v2 09/11] t5300: choose the built-in hash outside of a repo","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2025-07-01T21:22:35Z","receivedAt":"2025-07-01T21:22:46Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Right now, the built-in default hash is always SHA-1, but that will\nchange in a future commit.  Instead of assuming that operating outside\nof a repository will always use SHA-1, look up the default hash\nalgorithm for operating outside of a repository using an appropriate\nenvironment variable, which will always be correct.\n\nAdditionally, for operations outside of a repository, use the\nDEFAULT_HASH_ALGORITHM prerequisite rather than SHA1.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n t/t5300-pack-object.sh | 6 +++---\n 1 file changed, 3 insertions(+), 3 deletions(-)\n\ndiff --git a/t/t5300-pack-object.sh b/t/t5300-pack-object.sh\nindex ae72158b94..73445782e7 100755\n--- a/t/t5300-pack-object.sh\n+++ b/t/t5300-pack-object.sh\n@@ -525,7 +525,7 @@ test_expect_success 'index-pack --strict <pack> works in non-repo' '\n \ttest_path_is_file foo.idx\n '\n \n-test_expect_success SHA1 'show-index works OK outside a repository' '\n+test_expect_success DEFAULT_HASH_ALGORITHM 'show-index works OK outside a repository' '\n \tnongit git show-index <foo.idx\n '\n \n@@ -658,7 +658,7 @@ do\n \t\ttest_commit -C repo initial &&\n \t\tgit -C repo repack -ad &&\n \t\tgit -C repo verify-pack \"$(pwd)\"/repo/.git/objects/pack/*.idx &&\n-\t\tif test $hash = sha1\n+\t\tif test $hash = $GIT_TEST_BUILTIN_HASH\n \t\tthen\n \t\t\tnongit git verify-pack \"$(pwd)\"/repo/.git/objects/pack/*.idx\n \t\telse\n@@ -676,7 +676,7 @@ do\n \t\ttest_commit -C repo initial &&\n \t\tgit -C repo repack -ad &&\n \t\tgit -C repo index-pack --verify \"$(pwd)\"/repo/.git/objects/pack/*.pack &&\n-\t\tif test $hash = sha1\n+\t\tif test $hash = $GIT_TEST_BUILTIN_HASH\n \t\tthen\n \t\t\tnongit git index-pack --verify \"$(pwd)\"/repo/.git/objects/pack/*.pack\n \t\telse\n"},{"id":"521126","messageId":"xmqqikkbimu3.fsf@gitster.g","threadId":"63675","inReplyTo":"20250701212237.766774-1-sandals@crustytoothpaste.net","subject":"Re: [PATCH v2 00/11] Add SHA-256 by default as a breaking change","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2025-07-01T22:10:44Z","receivedAt":"2025-07-01T22:10:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n\n> Changes since v1:\n> * Add a build option for the default hash and use it in the tests.\n\nWITH_BREAKING_CHANGES flips GIT_HASH_DEFAULT between SHA-1 and\nSHA-256 as before, but the choice is now exposed via \"git help\n--build-options\", so test-lib.sh does not have to switch based\non WITH_BREAKING_CHANGES and use the build-options embedded in\nthe binary.\n\nVery nice.\n"},{"id":"521173","messageId":"aGVGVIryDXD576iG@pks.im","threadId":"63675","inReplyTo":"20250701212237.766774-1-sandals@crustytoothpaste.net","subject":"Re: [PATCH v2 00/11] Add SHA-256 by default as a breaking change","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-02T14:46:44Z","receivedAt":"2025-07-02T14:46:52Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, Jul 01, 2025 at 09:22:26PM +0000, brian m. carlson wrote:\n> Our breaking changes document outlines that Git 3.0 will make SHA-256\n> the default hash algorithm, which is a sensible and prudent approach,\n> especially from a security perspective.  However, we haven't tested this\n> adequately and it would be helpful to allow users to test this behaviour\n> so their code and environments are ready for it.\n> \n> Fortunately, c5bc9a7f94 (Makefile: wire up build option for deprecated\n> features, 2025-01-22) introduces a build option that we can use for\n> testing breaking changes: WITH_BREAKING_CHANGES.  This series introduces\n> functionality for SHA-256 by default in this mode so we can test it out.\n> \n> Changes since v1:\n> * Add a build option for the default hash and use it in the tests.\n> * Rename GIT_HASH_ORIGINAL to GIT_HASH_SHA1_LEGACY.\n> * Improve some of the commit messages to better explain questions that\n>   have come up for review.\n> * Improve formatting of nested C preprocessor directives.\n\nI looked specifically for the things that I commented on, all of which\nseem to have been addressed. Given that there is no range diff I trust\nthat there aren't any other unexpected changes.\n\nSo this iteration looks good to me, and I think that this series is a\nstep into the right direction overall.\n\nPatrick\n"},{"id":"521174","messageId":"0120a6c7-8ec9-487e-ab5a-0ad1b684166a@app.fastmail.com","threadId":"63675","inReplyTo":"aGVGVIryDXD576iG@pks.im","subject":"Re: [PATCH v2 00/11] Add SHA-256 by default as a breaking change","fromName":"Kristoffer Haugsbakk","fromEmail":"kristofferhaugsbakk@fastmail.com","sentAt":"2025-07-02T15:01:04Z","receivedAt":"2025-07-02T15:01:30Z","isPatch":true,"sender":{"key":"kristofferhaugsbakk@fastmail.com","avatar":null},"body":"On Wed, Jul 2, 2025, at 16:46, Patrick Steinhardt wrote:\n> I looked specifically for the things that I commented on, all of which\n> seem to have been addressed. Given that there is no range diff I trust\n> that there aren't any other unexpected changes.\n>\n> So this iteration looks good to me, and I think that this series is a\n> step into the right direction overall.\n\nRange diff with trimmed/mangled whitespace:\n\n```\n 1:  aced16ec164 =  1:  ca6daa1368e hash: add a constant for the default hash algorithm\n 2:  291d6f26bdd !  2:  1f68f3da877 hash: add a constant for the original hash algorithm\n    @@ Metadata\n     Author: brian m. carlson <sandals@crustytoothpaste.net>\n     \n      ## Commit message ##\n    -    hash: add a constant for the original hash algorithm\n    +    hash: add a constant for the legacy hash algorithm\n     \n         We have a a variety of uses of GIT_HASH_SHA1 littered throughout our\n         code.  Some of these really mean to represent specifically SHA-1, but\n         some actually represent the original hash algorithm used in Git which is\n    -    implied by older formats and protocols which do not contain hash\n    +    implied by older, legacy formats and protocols which do not contain hash\n         information.  For instance, the bundle v1 and v2 formats do not contain\n         hash algorithm information, and thus SHA-1 is implied by the use of\n         these formats.\n    @@ hash.h: static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA25\n      #define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)\n      /* Default hash algorithm if unspecified. */\n      #define GIT_HASH_DEFAULT GIT_HASH_SHA1\n    -+/* Original hash algorithm. Implied for older data formats which don't specify. */\n    -+#define GIT_HASH_ORIGINAL GIT_HASH_SHA1\n    ++/* Legacy hash algorithm. Implied for older data formats which don't specify. */\n    ++#define GIT_HASH_SHA1_LEGACY GIT_HASH_SHA1\n      \n      /* \"sha1\", big-endian */\n      #define GIT_SHA1_FORMAT_ID 0x73686131\n 3:  d041a12031d =  3:  dc9c16c2fc8 builtin: use default hash when outside a repository\n 4:  a52bf97d8eb !  4:  667d251a04c Use original hash for legacy formats\n    @@ Metadata\n     Author: brian m. carlson <sandals@crustytoothpaste.net>\n     \n      ## Commit message ##\n    -    Use original hash for legacy formats\n    +    Use legacy hash for legacy formats\n     \n         We have a large variety of data formats and protocols where no hash\n         algorithm was defined and the default was assumed to always be SHA-1.\n         Instead of explicitly stating SHA-1, let's use the constant to represent\n    -    the original hash algorithm (which is still SHA-1) so that it's clear\n    +    the legacy hash algorithm (which is still SHA-1) so that it's clear\n         for documentary purposes that it's a legacy fallback option and not an\n         intentional choice to use SHA-1.\n     \n    @@ builtin/receive-pack.c: static struct command *read_head_info(struct packet_read\n      \t\t\thash = parse_feature_value(feature_list, \"object-format\", &len, NULL);\n      \t\t\tif (!hash) {\n     -\t\t\t\thash = hash_algos[GIT_HASH_SHA1].name;\n    -+\t\t\t\thash = hash_algos[GIT_HASH_ORIGINAL].name;\n    ++\t\t\t\thash = hash_algos[GIT_HASH_SHA1_LEGACY].name;\n      \t\t\t\tlen = strlen(hash);\n      \t\t\t}\n      \t\t\tif (xstrncmpz(the_hash_algo->name, hash, len))\n    @@ bundle.c: int read_bundle_header_fd(int fd, struct bundle_header *header,\n      \t * `parse_capability()`.\n      \t */\n     -\theader->hash_algo = &hash_algos[GIT_HASH_SHA1];\n    -+\theader->hash_algo = &hash_algos[GIT_HASH_ORIGINAL];\n    ++\theader->hash_algo = &hash_algos[GIT_HASH_SHA1_LEGACY];\n      \n      \t/* The bundle header ends with an empty line */\n      \twhile (!strbuf_getwholeline_fd(&buf, fd, '\\n') &&\n    @@ bundle.c: int create_bundle(struct repository *r, const char *path,\n      \t * 2. @filter is required because we parsed an object filter.\n      \t */\n     -\tif (the_hash_algo != &hash_algos[GIT_HASH_SHA1] || revs.filter.choice)\n    -+\tif (the_hash_algo != &hash_algos[GIT_HASH_ORIGINAL] || revs.filter.choice)\n    ++\tif (the_hash_algo != &hash_algos[GIT_HASH_SHA1_LEGACY] || revs.filter.choice)\n      \t\tmin_version = 3;\n      \n      \tif (argc > 1) {\n    @@ connect.c: static void process_capabilities(struct packet_reader *reader, size_t\n      \t\tfree(hash_name);\n      \t} else {\n     -\t\treader->hash_algo = &hash_algos[GIT_HASH_SHA1];\n    -+\t\treader->hash_algo = &hash_algos[GIT_HASH_ORIGINAL];\n    ++\t\treader->hash_algo = &hash_algos[GIT_HASH_SHA1_LEGACY];\n      \t}\n      }\n      \n    @@ connect.c: static void send_capabilities(int fd_out, struct packet_reader *reade\n      \t\tpacket_write_fmt(fd_out, \"object-format=%s\", reader->hash_algo->name);\n      \t} else {\n     -\t\treader->hash_algo = &hash_algos[GIT_HASH_SHA1];\n    -+\t\treader->hash_algo = &hash_algos[GIT_HASH_ORIGINAL];\n    ++\t\treader->hash_algo = &hash_algos[GIT_HASH_SHA1_LEGACY];\n      \t}\n      \tif (server_feature_v2(\"promisor-remote\", &promisor_remote_info)) {\n      \t\tchar *reply = promisor_remote_reply(promisor_remote_info);\n    @@ connect.c: int server_supports_hash(const char *desired, int *feature_supported)\n      \t\t*feature_supported = !!hash;\n      \tif (!hash) {\n     -\t\thash = hash_algos[GIT_HASH_SHA1].name;\n    -+\t\thash = hash_algos[GIT_HASH_ORIGINAL].name;\n    ++\t\thash = hash_algos[GIT_HASH_SHA1_LEGACY].name;\n      \t\tlen = strlen(hash);\n      \t}\n      \twhile (hash) {\n    @@ fetch-pack.c: static void write_fetch_command_and_capabilities(struct strbuf *re\n      \t\t\t    the_hash_algo->name, hash_name);\n      \t\tpacket_buf_write(req_buf, \"object-format=%s\", the_hash_algo->name);\n     -\t} else if (hash_algo_by_ptr(the_hash_algo) != GIT_HASH_SHA1) {\n    -+\t} else if (hash_algo_by_ptr(the_hash_algo) != GIT_HASH_ORIGINAL) {\n    ++\t} else if (hash_algo_by_ptr(the_hash_algo) != GIT_HASH_SHA1_LEGACY) {\n      \t\tdie(_(\"the server does not support algorithm '%s'\"),\n      \t\t    the_hash_algo->name);\n      \t}\n    @@ pkt-line.c: void packet_reader_init(struct packet_reader *reader, int fd,\n      \treader->options = options;\n      \treader->me = \"git\";\n     -\treader->hash_algo = &hash_algos[GIT_HASH_SHA1];\n    -+\treader->hash_algo = &hash_algos[GIT_HASH_ORIGINAL];\n    ++\treader->hash_algo = &hash_algos[GIT_HASH_SHA1_LEGACY];\n      \tstrbuf_init(&reader->scratch, 0);\n      }\n      \n    @@ remote-curl.c: static const struct git_hash_algo *detect_hash_algo(struct discov\n      \t */\n      \tif (!p)\n     -\t\treturn &hash_algos[GIT_HASH_SHA1];\n    -+\t\treturn &hash_algos[GIT_HASH_ORIGINAL];\n    ++\t\treturn &hash_algos[GIT_HASH_SHA1_LEGACY];\n      \n      \talgo = hash_algo_by_length((p - heads->buf) / 2);\n      \tif (algo == GIT_HASH_UNKNOWN)\n    @@ serve.c\n      static int advertise_sid = -1;\n      static int advertise_object_info = -1;\n     -static int client_hash_algo = GIT_HASH_SHA1;\n    -+static int client_hash_algo = GIT_HASH_ORIGINAL;\n    ++static int client_hash_algo = GIT_HASH_SHA1_LEGACY;\n      \n      static int always_advertise(struct repository *r UNUSED,\n      \t\t\t    struct strbuf *value UNUSED)\n    @@ setup.c: void initialize_repository_version(int hash_algo,\n      \t * the remote repository's format.\n      \t */\n     -\tif (hash_algo != GIT_HASH_SHA1 ||\n    -+\tif (hash_algo != GIT_HASH_ORIGINAL ||\n    ++\tif (hash_algo != GIT_HASH_SHA1_LEGACY ||\n      \t    ref_storage_format != REF_STORAGE_FORMAT_FILES)\n      \t\ttarget_version = GIT_REPO_VERSION_READ;\n      \n     -\tif (hash_algo != GIT_HASH_SHA1 && hash_algo != GIT_HASH_UNKNOWN)\n    -+\tif (hash_algo != GIT_HASH_ORIGINAL && hash_algo != GIT_HASH_UNKNOWN)\n    ++\tif (hash_algo != GIT_HASH_SHA1_LEGACY && hash_algo != GIT_HASH_UNKNOWN)\n      \t\tgit_config_set(\"extensions.objectformat\",\n      \t\t\t       hash_algos[hash_algo].name);\n      \telse if (reinit)\n    @@ transport.c: struct transport *transport_get(struct remote *remote, const char *\n      \t}\n      \n     -\tret->hash_algo = &hash_algos[GIT_HASH_SHA1];\n    -+\tret->hash_algo = &hash_algos[GIT_HASH_ORIGINAL];\n    ++\tret->hash_algo = &hash_algos[GIT_HASH_SHA1_LEGACY];\n      \n      \treturn ret;\n      }\n 5:  d27e4ee557d !  5:  d6e616cee74 setup: use the default algorithm to initialize repo format\n    @@ Commit message\n     \n         Because we might not always want to use SHA-1 as the default, let's\n         instead specify the default hash algorithm constant so that we will use\n    -    whatever the specified default is.  However, to make sure we continue to\n    -    read repositories without a specified hash algorithm as SHA-1, default\n    -    the repository format to the original hash algorithm (SHA-1) when\n    -    reading the repository format.\n    +    whatever the specified default is.\n    +\n    +    However, we also need to continue to read older repositories.  If we're\n    +    in a v0 repository or extensions.objectformat is not set, then we must\n    +    continue to default to the original hash algorithm: SHA-1.  If an\n    +    algorithm is set explicitly, however, it will override the hash_algo\n    +    member of the repository_format struct and we'll get the right value.\n    +\n    +    Similarly, if the repository was initialized before Git 0.99.3, then it\n    +    may lack a core.repositoryformatversion key, and some repositories lack\n    +    a config file altogether.  In both cases, format->version is -1 and we\n    +    need to assume that SHA-1 is in use.\n    +\n    +    Because clear_repository_format reinitializes the struct\n    +    repository_format and therefore sets the hash_algo member to the default\n    +    (which could in the future not be SHA-1), we need to reset this member\n    +    explicitly.  We know, however, that at the point we call\n    +    read_repository_format, we are actually reading an existing repository\n    +    and not initializing a new one or operating outside of a repository, so\n    +    we are not changing the default behavior back to SHA-1 if the default\n    +    algorithm is different.\n    +\n    +    It is potentially questionable that we ignore all repository\n    +    configuration if there is a config file but it doesn't have\n    +    core.repositoryformatversion set, in which case we reset all of the\n    +    configuration to the default.  However, it is unclear what the right\n    +    thing to do instead with such an old repository is and a simple git init\n    +    will add the missing entry, so for now, we simply honor what the\n    +    existing code does and reset the value to the default, simply adding our\n    +    initialization to SHA-1.\n     \n         Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n         Signed-off-by: Junio C Hamano <gitster@pobox.com>\n    @@ setup.c: static void init_repository_format(struct repository_format *format)\n      int read_repository_format(struct repository_format *format, const char *path)\n      {\n      \tclear_repository_format(format);\n    -+\tformat->hash_algo = GIT_HASH_ORIGINAL;\n    ++\tformat->hash_algo = GIT_HASH_SHA1_LEGACY;\n      \tgit_config_from_file(check_repo_format, path, format);\n     -\tif (format->version == -1)\n     +\tif (format->version == -1) {\n      \t\tclear_repository_format(format);\n    -+\t\tformat->hash_algo = GIT_HASH_ORIGINAL;\n    ++\t\tformat->hash_algo = GIT_HASH_SHA1_LEGACY;\n     +\t}\n      \treturn format->version;\n      }\n 6:  9bb3c06d5b7 =  6:  c470ac4ac41 t: default to compile-time default hash if not set\n 7:  8670b4fc7b0 =  7:  6866b422608 t1007: choose the built-in hash outside of a repo\n 8:  8804bfbfa2c =  8:  f957ce078f6 t4042: choose the built-in hash outside of a repo\n 9:  231c9ff200f =  9:  9d619f2ef8c t5300: choose the built-in hash outside of a repo\n10:  53554e53b35 <  -:  ----------- Enable SHA-256 by default in breaking changes mode\n -:  ----------- > 10:  39153c80971 help: add a build option for default hash\n -:  ----------- > 11:  c79bb70a2e7 Enable SHA-256 by default in breaking changes mode\n```\n\nFrom the repo.\n"},{"id":"521175","messageId":"aGVLYey4Hemu7YFW@pks.im","threadId":"63675","inReplyTo":"aGRP0WiG8ZRzgilp@fruit.crustytoothpaste.net","subject":"Re: [PATCH 03/10] builtin: use default hash when outside a repository","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2025-07-02T15:08:17Z","receivedAt":"2025-07-02T15:08:24Z","isPatch":true,"sender":{"key":"ps@pks.im","avatar":"https://avatars.githubusercontent.com/u/4056630?v=4"},"body":"On Tue, Jul 01, 2025 at 09:14:57PM +0000, brian m. carlson wrote:\n> On 2025-07-01 at 11:35:33, Patrick Steinhardt wrote:\n> > On Fri, Jun 20, 2025 at 01:19:35AM +0000, brian m. carlson wrote:\n> > > We have some commands that can operate inside or outside a repository.\n> > > If we're operating outside a repository, we clearly cannot use the\n> > > repository's hash algorithm as a default since it doesn't exist, so\n> > > instead, let's pick the default instead of specifically SHA-1.  Right\n> > > now this results in no functional change since the default is SHA-1, but\n> > > that may change in the future.\n> > \n> > With the preceding commit in mind that introduced GIT_HASH_ORIGINAL you\n> > could also argue that those callsites should be converted to use that\n> > define instead. We always used to treat them as SHA1 repositories, and\n> > we have no better way of telling otherwise, so we use the historical\n> > value of SHA1 so that scripts aren't dependent on how exactly Git was\n> > built.\n> \n> I don't think I want to do that.  A lot of the functionality people use\n> outside of repositories, such as index-pack and ls-remote, actually\n> operates on repository objects and so it makes sense to use the default.\n> \n> For instance, it will be a major inconvenience to still have to specify\n> a custom object format three to five years after the switchover.\n\nTrue. Initially it will be a pain for those edge cases, but the longer\nthe new default is in place the more useful it will become.\n\nPatrick\n"}]}