{"thread":{"id":"55472","subject":"[PATCH 00/15] SHA-256 / SHA-1 interop, part 1","startedAt":"2021-04-10T15:22:35Z","lastAt":"2022-02-21T16:05:39Z","messageCount":71,"participants":["brian m. carlson","Chris Torek","Ævar Arnfjörð Bjarmason","Junio C Hamano","Bagas Sanjaya","Jeff King","Derrick Stolee","Denton Liu","Jonathan Nieder"],"isPatch":true,"patchVersion":1,"patchTotal":15},"messages":[{"id":"421535","messageId":"20210410152140.3525040-1-sandals@crustytoothpaste.net","threadId":"55472","inReplyTo":null,"subject":"[PATCH 00/15] SHA-256 / SHA-1 interop, part 1","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-10T15:21:25Z","receivedAt":"2021-04-10T15:22:35Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Because I know the first twenty-something weren't enough, here's another\nseries of SHA-256-related patches.  This is the beginning of the work to\nmake SHA-256 and SHA-1 repositories interoperate, which of course is\npart of the transition plan.\n\nThis series introduces code to make struct object_id take a hash\nalgorithm member and use that for printing the object ID.  This is the\nfirst step to handling multiple hash algorithms in the same binary,\nwhich we'll need if we're going to process both SHA-1 and SHA-256\nversions of objects.\n\nThe major contributor to the size of this series is the patch to switch\nto per-algorithm null OIDs via a null_oid function.  It's certainly\npossible there are conflicts with topics in flight, but those should be\neasy to resolve (just change \"&null_oid\" to \"null_oid()\").\n\nIn addition, there's an initial set of patches to allow hashing an\nobject literally with any algorithm.  Such objects cannot be written\ninto the object store, but their values can be printed.\n\nThis series is available as transition-interop-part-1 from the usual\nplaces and is part of the transition-interop branch (the entirety of the\nwork) as well.  Be aware that the latter is very, very broken and its\nuse is not advised at this time.\n\nFuture series will likely include some fixes to our testsuite and code\nto map objects across hash algorithms.\n\nbrian m. carlson (15):\n  sha1-file: allow hashing objects literally with any algorithm\n  builtin/hash-object: allow literally hashing with a given algorithm\n  cache: add an algo member to struct object_id\n  Always use oidread to read into struct object_id\n  hash: add a function to finalize object IDs\n  Use the final_oid_fn to finalize hashing of object IDs\n  builtin/pack-redundant: avoid casting buffers to struct object_id\n  cache: compare the entire buffer for struct object_id\n  hash: set and copy algo field in struct object_id\n  hash: provide per-algorithm null OIDs\n  builtin/show-index: set the algorithm for object IDs\n  commit-graph: don't store file hashes as struct object_id\n  builtin/pack-objects: avoid using struct object_id for pack hash\n  hex: default to the_hash_algo on zero algorithm value\n  hex: print objects using the hash algorithm member\n\n archive.c                                    |  6 +-\n blame.c                                      |  2 +-\n branch.c                                     |  2 +-\n builtin/checkout.c                           |  6 +-\n builtin/clone.c                              |  2 +-\n builtin/describe.c                           |  2 +-\n builtin/diff.c                               |  2 +-\n builtin/fast-export.c                        | 10 +--\n builtin/fast-import.c                        |  8 +-\n builtin/grep.c                               |  2 +-\n builtin/hash-object.c                        | 47 ++++++++----\n builtin/index-pack.c                         |  6 +-\n builtin/ls-files.c                           |  2 +-\n builtin/pack-objects.c                       | 20 ++---\n builtin/pack-redundant.c                     | 28 +++----\n builtin/rebase.c                             |  4 +-\n builtin/receive-pack.c                       |  2 +-\n builtin/show-index.c                         |  4 +-\n builtin/submodule--helper.c                  | 29 +++----\n builtin/unpack-objects.c                     |  7 +-\n builtin/worktree.c                           |  4 +-\n bulk-checkin.c                               |  2 +-\n combine-diff.c                               |  2 +-\n commit-graph.c                               | 25 ++++---\n connect.c                                    |  2 +-\n diff-lib.c                                   |  6 +-\n diff-no-index.c                              |  2 +-\n diff.c                                       |  6 +-\n dir.c                                        |  6 +-\n grep.c                                       |  2 +-\n hash.h                                       | 73 +++++++++++-------\n hex.c                                        | 20 +++--\n http-walker.c                                |  2 +-\n http.c                                       |  2 +-\n log-tree.c                                   |  2 +-\n match-trees.c                                |  2 +-\n merge-ort.c                                  | 20 ++---\n merge-recursive.c                            | 10 +--\n midx.c                                       |  2 +-\n notes-merge.c                                |  2 +-\n notes.c                                      |  9 ++-\n object-file.c                                | 79 +++++++++++++++++---\n object-store.h                               |  3 +\n parse-options-cb.c                           |  2 +-\n range-diff.c                                 |  2 +-\n read-cache.c                                 |  4 +-\n refs.c                                       |  4 +-\n refs/debug.c                                 |  2 +-\n refs/files-backend.c                         |  2 +-\n reset.c                                      |  2 +-\n sequencer.c                                  |  4 +-\n split-index.c                                |  2 +-\n submodule-config.c                           |  2 +-\n submodule.c                                  | 26 ++++---\n t/helper/test-submodule-nested-repo-config.c |  2 +-\n t/t1007-hash-object.sh                       | 10 +++\n tree-diff.c                                  |  4 +-\n tree-walk.c                                  |  2 +-\n wt-status.c                                  |  4 +-\n xdiff-interface.c                            |  2 +-\n 60 files changed, 341 insertions(+), 209 deletions(-)\n\n"},{"id":"421536","messageId":"20210410152140.3525040-2-sandals@crustytoothpaste.net","threadId":"55472","inReplyTo":"20210410152140.3525040-1-sandals@crustytoothpaste.net","subject":"[PATCH 01/15] sha1-file: allow hashing objects literally with any algorithm","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-10T15:21:26Z","receivedAt":"2021-04-10T15:22:36Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"In order to perform suitable testing with multiple algorithms and\ninteroperability, we'll need the ability to hash an object with a given\nalgorithm. Introduce this capability for now only for objects which are\nhashed literally by adding a function which does this and changing a\nstatic function to accept an algorithm pointer.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n object-file.c  | 16 ++++++++++++++--\n object-store.h |  3 +++\n 2 files changed, 17 insertions(+), 2 deletions(-)\n\ndiff --git a/object-file.c b/object-file.c\nindex 624af408cd..f5847ee20f 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -1957,6 +1957,15 @@ int write_object_file(const void *buf, unsigned long len, const char *type,\n int hash_object_file_literally(const void *buf, unsigned long len,\n \t\t\t       const char *type, struct object_id *oid,\n \t\t\t       unsigned flags)\n+{\n+\treturn hash_object_file_literally_algop(buf, len, type, oid, flags,\n+\t\t\t\t\t\tthe_hash_algo);\n+}\n+\n+int hash_object_file_literally_algop(const void *buf, unsigned long len,\n+\t\t\t\t     const char *type, struct object_id *oid,\n+\t\t\t\t     unsigned flags,\n+\t\t\t\t     const struct git_hash_algo *algo)\n {\n \tchar *header;\n \tint hdrlen, status = 0;\n@@ -1964,11 +1973,14 @@ int hash_object_file_literally(const void *buf, unsigned long len,\n \t/* type string, SP, %lu of the length plus NUL must fit this */\n \thdrlen = strlen(type) + MAX_HEADER_LEN;\n \theader = xmalloc(hdrlen);\n-\twrite_object_file_prepare(the_hash_algo, buf, len, type, oid, header,\n-\t\t\t\t  &hdrlen);\n+\twrite_object_file_prepare(algo, buf, len, type, oid, header, &hdrlen);\n \n \tif (!(flags & HASH_WRITE_OBJECT))\n \t\tgoto cleanup;\n+\tif (algo->format_id != the_hash_algo->format_id) {\n+\t\tstatus = -1;\n+\t\tgoto cleanup;\n+\t}\n \tif (freshen_packed_object(oid) || freshen_loose_object(oid))\n \t\tgoto cleanup;\n \tstatus = write_loose_object(oid, header, hdrlen, buf, len, 0);\ndiff --git a/object-store.h b/object-store.h\nindex ec32c23dcb..f95d03a7f5 100644\n--- a/object-store.h\n+++ b/object-store.h\n@@ -221,6 +221,9 @@ int hash_object_file_literally(const void *buf, unsigned long len,\n \t\t\t       const char *type, struct object_id *oid,\n \t\t\t       unsigned flags);\n \n+int hash_object_file_literally_algop(const void *buf, unsigned long len,\n+\t\t\t\t     const char *type, struct object_id *oid,\n+\t\t\t\t     unsigned flags, const struct git_hash_algo *algo);\n /*\n  * Add an object file to the in-memory object store, without writing it\n  * to disk.\n"},{"id":"421537","messageId":"20210410152140.3525040-5-sandals@crustytoothpaste.net","threadId":"55472","inReplyTo":"20210410152140.3525040-1-sandals@crustytoothpaste.net","subject":"[PATCH 04/15] Always use oidread to read into struct object_id","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-10T15:21:29Z","receivedAt":"2021-04-10T15:22:36Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"In the future, we'll want oidread to automatically set the hash\nalgorithm member for an object ID we read into it, so ensure we use\noidread instead of hashcpy everywhere we're copying a hash value into a\nstruct object_id.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n archive.c                |  2 +-\n builtin/fast-import.c    |  4 ++--\n builtin/index-pack.c     |  4 ++--\n builtin/unpack-objects.c |  2 +-\n commit-graph.c           | 12 ++++++------\n dir.c                    |  4 ++--\n http-walker.c            |  2 +-\n match-trees.c            |  2 +-\n midx.c                   |  2 +-\n notes.c                  |  4 ++--\n read-cache.c             |  4 ++--\n split-index.c            |  2 +-\n tree-walk.c              |  2 +-\n 13 files changed, 23 insertions(+), 23 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 295615580d..6cfb9e42d6 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -203,7 +203,7 @@ static void queue_directory(const unsigned char *sha1,\n \td->mode\t   = mode;\n \tc->bottom  = d;\n \td->len = xsnprintf(d->path, len, \"%.*s%s/\", (int)base->len, base->buf, filename);\n-\thashcpy(d->oid.hash, sha1);\n+\toidread(&d->oid, sha1);\n }\n \n static int write_directory(struct archiver_context *c)\ndiff --git a/builtin/fast-import.c b/builtin/fast-import.c\nindex 3afa81cf9a..9d2a058a66 100644\n--- a/builtin/fast-import.c\n+++ b/builtin/fast-import.c\n@@ -1276,8 +1276,8 @@ static void load_tree(struct tree_entry *root)\n \t\te->versions[0].mode = e->versions[1].mode;\n \t\te->name = to_atom(c, strlen(c));\n \t\tc += e->name->str_len + 1;\n-\t\thashcpy(e->versions[0].oid.hash, (unsigned char *)c);\n-\t\thashcpy(e->versions[1].oid.hash, (unsigned char *)c);\n+\t\toidread(&e->versions[0].oid, (unsigned char *)c);\n+\t\toidread(&e->versions[1].oid, (unsigned char *)c);\n \t\tc += the_hash_algo->rawsz;\n \t}\n \tfree(buf);\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 15507b5cff..41e2c240b8 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -524,7 +524,7 @@ static void *unpack_raw_entry(struct object_entry *obj,\n \n \tswitch (obj->type) {\n \tcase OBJ_REF_DELTA:\n-\t\thashcpy(ref_oid->hash, fill(the_hash_algo->rawsz));\n+\t\toidread(ref_oid, fill(the_hash_algo->rawsz));\n \t\tuse(the_hash_algo->rawsz);\n \t\tbreak;\n \tcase OBJ_OFS_DELTA:\n@@ -1358,7 +1358,7 @@ static struct object_entry *append_obj_to_pack(struct hashfile *f,\n \tobj[1].idx.offset += write_compressed(f, buf, size);\n \tobj[0].idx.crc32 = crc32_end(f);\n \thashflush(f);\n-\thashcpy(obj->idx.oid.hash, sha1);\n+\toidread(&obj->idx.oid, sha1);\n \treturn obj;\n }\n \ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex 4a70b17f8f..a8b73ecf43 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -355,7 +355,7 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,\n \tstruct object_id base_oid;\n \n \tif (type == OBJ_REF_DELTA) {\n-\t\thashcpy(base_oid.hash, fill(the_hash_algo->rawsz));\n+\t\toidread(&base_oid, fill(the_hash_algo->rawsz));\n \t\tuse(the_hash_algo->rawsz);\n \t\tdelta_data = get_data(delta_size);\n \t\tif (dry_run || !delta_data) {\ndiff --git a/commit-graph.c b/commit-graph.c\nindex f18380b922..23fef56d31 100644\n--- a/commit-graph.c\n+++ b/commit-graph.c\n@@ -425,7 +425,7 @@ struct commit_graph *parse_commit_graph(struct repository *r,\n \t\tFREE_AND_NULL(graph->bloom_filter_settings);\n \t}\n \n-\thashcpy(graph->oid.hash, graph->data + graph->data_len - graph->hash_len);\n+\toidread(&graph->oid, graph->data + graph->data_len - graph->hash_len);\n \n \tif (verify_commit_graph_lite(graph))\n \t\tgoto free_and_return;\n@@ -746,7 +746,7 @@ static void load_oid_from_graph(struct commit_graph *g,\n \n \tlex_index = pos - g->num_commits_in_base;\n \n-\thashcpy(oid->hash, g->chunk_oid_lookup + g->hash_len * lex_index);\n+\toidread(oid, g->chunk_oid_lookup + g->hash_len * lex_index);\n }\n \n static struct commit_list **insert_parent_or_die(struct repository *r,\n@@ -939,7 +939,7 @@ static struct tree *load_tree_for_commit(struct repository *r,\n \tcommit_data = g->chunk_commit_data +\n \t\t\tGRAPH_DATA_WIDTH * (graph_pos - g->num_commits_in_base);\n \n-\thashcpy(oid.hash, commit_data);\n+\toidread(&oid, commit_data);\n \tset_commit_tree(c, lookup_tree(r, &oid));\n \n \treturn c->maybe_tree;\n@@ -2322,7 +2322,7 @@ int write_commit_graph(struct object_directory *odb,\n \t\tstruct commit_graph *g = ctx->r->objects->commit_graph;\n \t\tfor (i = 0; i < g->num_commits; i++) {\n \t\t\tstruct object_id oid;\n-\t\t\thashcpy(oid.hash, g->chunk_oid_lookup + g->hash_len * i);\n+\t\t\toidread(&oid, g->chunk_oid_lookup + g->hash_len * i);\n \t\t\toid_array_append(&ctx->oids, &oid);\n \t\t}\n \t}\n@@ -2453,7 +2453,7 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)\n \tfor (i = 0; i < g->num_commits; i++) {\n \t\tstruct commit *graph_commit;\n \n-\t\thashcpy(cur_oid.hash, g->chunk_oid_lookup + g->hash_len * i);\n+\t\toidread(&cur_oid, g->chunk_oid_lookup + g->hash_len * i);\n \n \t\tif (i && oidcmp(&prev_oid, &cur_oid) >= 0)\n \t\t\tgraph_report(_(\"commit-graph has incorrect OID order: %s then %s\"),\n@@ -2501,7 +2501,7 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)\n \t\ttimestamp_t generation;\n \n \t\tdisplay_progress(progress, i + 1);\n-\t\thashcpy(cur_oid.hash, g->chunk_oid_lookup + g->hash_len * i);\n+\t\toidread(&cur_oid, g->chunk_oid_lookup + g->hash_len * i);\n \n \t\tgraph_commit = lookup_commit(r, &cur_oid);\n \t\todb_commit = (struct commit *)create_object(r, &cur_oid, alloc_commit_node(r));\ndiff --git a/dir.c b/dir.c\nindex 3474e67e8f..813dd7ba53 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -3344,7 +3344,7 @@ static void read_oid(size_t pos, void *cb)\n \t\trd->data = rd->end + 1;\n \t\treturn;\n \t}\n-\thashcpy(ud->exclude_oid.hash, rd->data);\n+\toidread(&ud->exclude_oid, rd->data);\n \trd->data += the_hash_algo->rawsz;\n }\n \n@@ -3352,7 +3352,7 @@ static void load_oid_stat(struct oid_stat *oid_stat, const unsigned char *data,\n \t\t\t  const unsigned char *sha1)\n {\n \tstat_data_from_disk(&oid_stat->stat, data);\n-\thashcpy(oid_stat->oid.hash, sha1);\n+\toidread(&oid_stat->oid, sha1);\n \toid_stat->valid = 1;\n }\n \ndiff --git a/http-walker.c b/http-walker.c\nindex 4fb1235cd4..90d8ecb57e 100644\n--- a/http-walker.c\n+++ b/http-walker.c\n@@ -155,7 +155,7 @@ static void prefetch(struct walker *walker, unsigned char *sha1)\n \n \tnewreq = xmalloc(sizeof(*newreq));\n \tnewreq->walker = walker;\n-\thashcpy(newreq->oid.hash, sha1);\n+\toidread(&newreq->oid, sha1);\n \tnewreq->repo = data->alt;\n \tnewreq->state = WAITING;\n \tnewreq->req = NULL;\ndiff --git a/match-trees.c b/match-trees.c\nindex f6c194c1cc..df413989fa 100644\n--- a/match-trees.c\n+++ b/match-trees.c\n@@ -226,7 +226,7 @@ static int splice_tree(const struct object_id *oid1, const char *prefix,\n \t\t    oid_to_hex(oid1));\n \tif (*subpath) {\n \t\tstruct object_id tree_oid;\n-\t\thashcpy(tree_oid.hash, rewrite_here);\n+\t\toidread(&tree_oid, rewrite_here);\n \t\tstatus = splice_tree(&tree_oid, subpath, oid2, &subtree);\n \t\tif (status)\n \t\t\treturn status;\ndiff --git a/midx.c b/midx.c\nindex 9e86583172..21d6a05e88 100644\n--- a/midx.c\n+++ b/midx.c\n@@ -247,7 +247,7 @@ struct object_id *nth_midxed_object_oid(struct object_id *oid,\n \tif (n >= m->num_objects)\n \t\treturn NULL;\n \n-\thashcpy(oid->hash, m->chunk_oid_lookup + m->hash_len * n);\n+\toidread(oid, m->chunk_oid_lookup + m->hash_len * n);\n \treturn oid;\n }\n \ndiff --git a/notes.c b/notes.c\nindex a19e4ad794..a44b25858f 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -352,7 +352,7 @@ static void add_non_note(struct notes_tree *t, char *path,\n \tn->next = NULL;\n \tn->path = path;\n \tn->mode = mode;\n-\thashcpy(n->oid.hash, sha1);\n+\toidread(&n->oid, sha1);\n \tt->prev_non_note = n;\n \n \tif (!t->first_non_note) {\n@@ -1134,7 +1134,7 @@ int remove_note(struct notes_tree *t, const unsigned char *object_sha1)\n \tif (!t)\n \t\tt = &default_notes_tree;\n \tassert(t->initialized);\n-\thashcpy(l.key_oid.hash, object_sha1);\n+\toidread(&l.key_oid, object_sha1);\n \toidclr(&l.val_oid);\n \tnote_tree_remove(t, t->root, 0, &l);\n \tif (is_null_oid(&l.val_oid)) /* no note was removed */\ndiff --git a/read-cache.c b/read-cache.c\nindex 5a907af2fb..2944146545 100644\n--- a/read-cache.c\n+++ b/read-cache.c\n@@ -1845,7 +1845,7 @@ static struct cache_entry *create_from_disk(struct mem_pool *ce_mem_pool,\n \tce->ce_flags = flags & ~CE_NAMEMASK;\n \tce->ce_namelen = len;\n \tce->index = 0;\n-\thashcpy(ce->oid.hash, ondisk->data);\n+\toidread(&ce->oid, ondisk->data);\n \tmemcpy(ce->name, name, len);\n \tce->name[len] = '\\0';\n \n@@ -2195,7 +2195,7 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)\n \tif (verify_hdr(hdr, mmap_size) < 0)\n \t\tgoto unmap;\n \n-\thashcpy(istate->oid.hash, (const unsigned char *)hdr + mmap_size - the_hash_algo->rawsz);\n+\toidread(&istate->oid, (const unsigned char *)hdr + mmap_size - the_hash_algo->rawsz);\n \tistate->version = ntohl(hdr->hdr_version);\n \tistate->cache_nr = ntohl(hdr->hdr_entries);\n \tistate->cache_alloc = alloc_nr(istate->cache_nr);\ndiff --git a/split-index.c b/split-index.c\nindex 94937d21a3..4d6e52d46f 100644\n--- a/split-index.c\n+++ b/split-index.c\n@@ -21,7 +21,7 @@ int read_link_extension(struct index_state *istate,\n \tif (sz < the_hash_algo->rawsz)\n \t\treturn error(\"corrupt link extension (too short)\");\n \tsi = init_split_index(istate);\n-\thashcpy(si->base_oid.hash, data);\n+\toidread(&si->base_oid, data);\n \tdata += the_hash_algo->rawsz;\n \tsz -= the_hash_algo->rawsz;\n \tif (!sz)\ndiff --git a/tree-walk.c b/tree-walk.c\nindex 2d6226d5f1..3a94959d64 100644\n--- a/tree-walk.c\n+++ b/tree-walk.c\n@@ -49,7 +49,7 @@ static int decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned l\n \tdesc->entry.path = path;\n \tdesc->entry.mode = canon_mode(mode);\n \tdesc->entry.pathlen = len - 1;\n-\thashcpy(desc->entry.oid.hash, (const unsigned char *)path + len);\n+\toidread(&desc->entry.oid, (const unsigned char *)path + len);\n \n \treturn 0;\n }\n"},{"id":"421538","messageId":"20210410152140.3525040-6-sandals@crustytoothpaste.net","threadId":"55472","inReplyTo":"20210410152140.3525040-1-sandals@crustytoothpaste.net","subject":"[PATCH 05/15] hash: add a function to finalize object IDs","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-10T15:21:30Z","receivedAt":"2021-04-10T15:22:37Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"To avoid the penalty of having to branch in hash comparison functions,\nwe'll want to always compare the full hash member in a struct object_id,\nwhich will require that SHA-1 object IDs be zero-padded.  To do so, add\na function which finalizes a hash context and writes it into an object\nID that performs this padding.\n\nMove the definition of struct object_id and the constant definitions\nhigher up so we they are available for us to use.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n hash.h        | 50 +++++++++++++++++++++++++++-----------------------\n object-file.c | 25 +++++++++++++++++++++++++\n 2 files changed, 52 insertions(+), 23 deletions(-)\n\ndiff --git a/hash.h b/hash.h\nindex dafdcb3335..c8f03d8aee 100644\n--- a/hash.h\n+++ b/hash.h\n@@ -95,6 +95,29 @@ static inline void git_SHA256_Clone(git_SHA256_CTX *dst, const git_SHA256_CTX *s\n /* Number of algorithms supported (including unknown). */\n #define GIT_HASH_NALGOS (GIT_HASH_SHA256 + 1)\n \n+/* The length in bytes and in hex digits of an object name (SHA-1 value). */\n+#define GIT_SHA1_RAWSZ 20\n+#define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)\n+/* The block size of SHA-1. */\n+#define GIT_SHA1_BLKSZ 64\n+\n+/* The length in bytes and in hex digits of an object name (SHA-256 value). */\n+#define GIT_SHA256_RAWSZ 32\n+#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)\n+/* The block size of SHA-256. */\n+#define GIT_SHA256_BLKSZ 64\n+\n+/* The length in byte and in hex digits of the largest possible hash value. */\n+#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ\n+#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ\n+/* The largest possible block size for any supported hash. */\n+#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ\n+\n+struct object_id {\n+\tunsigned char hash[GIT_MAX_RAWSZ];\n+\tint algo;\n+};\n+\n /* A suitably aligned type for stack allocations of hash contexts. */\n union git_hash_ctx {\n \tgit_SHA_CTX sha1;\n@@ -106,6 +129,7 @@ typedef void (*git_hash_init_fn)(git_hash_ctx *ctx);\n typedef void (*git_hash_clone_fn)(git_hash_ctx *dst, const git_hash_ctx *src);\n typedef void (*git_hash_update_fn)(git_hash_ctx *ctx, const void *in, size_t len);\n typedef void (*git_hash_final_fn)(unsigned char *hash, git_hash_ctx *ctx);\n+typedef void (*git_hash_final_oid_fn)(struct object_id *oid, git_hash_ctx *ctx);\n \n struct git_hash_algo {\n \t/*\n@@ -138,6 +162,9 @@ struct git_hash_algo {\n \t/* The hash finalization function. */\n \tgit_hash_final_fn final_fn;\n \n+\t/* The hash finalization function for object IDs. */\n+\tgit_hash_final_oid_fn final_oid_fn;\n+\n \t/* The OID of the empty tree. */\n \tconst struct object_id *empty_tree;\n \n@@ -161,29 +188,6 @@ static inline int hash_algo_by_ptr(const struct git_hash_algo *p)\n \treturn p - hash_algos;\n }\n \n-/* The length in bytes and in hex digits of an object name (SHA-1 value). */\n-#define GIT_SHA1_RAWSZ 20\n-#define GIT_SHA1_HEXSZ (2 * GIT_SHA1_RAWSZ)\n-/* The block size of SHA-1. */\n-#define GIT_SHA1_BLKSZ 64\n-\n-/* The length in bytes and in hex digits of an object name (SHA-256 value). */\n-#define GIT_SHA256_RAWSZ 32\n-#define GIT_SHA256_HEXSZ (2 * GIT_SHA256_RAWSZ)\n-/* The block size of SHA-256. */\n-#define GIT_SHA256_BLKSZ 64\n-\n-/* The length in byte and in hex digits of the largest possible hash value. */\n-#define GIT_MAX_RAWSZ GIT_SHA256_RAWSZ\n-#define GIT_MAX_HEXSZ GIT_SHA256_HEXSZ\n-/* The largest possible block size for any supported hash. */\n-#define GIT_MAX_BLKSZ GIT_SHA256_BLKSZ\n-\n-struct object_id {\n-\tunsigned char hash[GIT_MAX_RAWSZ];\n-\tint algo;\n-};\n-\n #define the_hash_algo the_repository->hash_algo\n \n extern const struct object_id null_oid;\ndiff --git a/object-file.c b/object-file.c\nindex f5847ee20f..58d31452d8 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -89,6 +89,12 @@ static void git_hash_sha1_final(unsigned char *hash, git_hash_ctx *ctx)\n \tgit_SHA1_Final(hash, &ctx->sha1);\n }\n \n+static void git_hash_sha1_final_oid(struct object_id *oid, git_hash_ctx *ctx)\n+{\n+\tgit_SHA1_Final(oid->hash, &ctx->sha1);\n+\tmemset(oid->hash + GIT_SHA1_RAWSZ, 0, GIT_MAX_RAWSZ - GIT_SHA1_RAWSZ);\n+}\n+\n \n static void git_hash_sha256_init(git_hash_ctx *ctx)\n {\n@@ -110,6 +116,16 @@ static void git_hash_sha256_final(unsigned char *hash, git_hash_ctx *ctx)\n \tgit_SHA256_Final(hash, &ctx->sha256);\n }\n \n+static void git_hash_sha256_final_oid(struct object_id *oid, git_hash_ctx *ctx)\n+{\n+\tgit_SHA256_Final(oid->hash, &ctx->sha256);\n+\t/*\n+\t * This currently does nothing, so the compiler should optimize it out,\n+\t * but keep it in case we extend the hash size again.\n+\t */\n+\tmemset(oid->hash + GIT_SHA256_RAWSZ, 0, GIT_MAX_RAWSZ - GIT_SHA256_RAWSZ);\n+}\n+\n static void git_hash_unknown_init(git_hash_ctx *ctx)\n {\n \tBUG(\"trying to init unknown hash\");\n@@ -130,6 +146,12 @@ static void git_hash_unknown_final(unsigned char *hash, git_hash_ctx *ctx)\n \tBUG(\"trying to finalize unknown hash\");\n }\n \n+static void git_hash_unknown_final_oid(struct object_id *oid, git_hash_ctx *ctx)\n+{\n+\tBUG(\"trying to finalize unknown hash\");\n+}\n+\n+\n const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {\n \t{\n \t\tNULL,\n@@ -141,6 +163,7 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {\n \t\tgit_hash_unknown_clone,\n \t\tgit_hash_unknown_update,\n \t\tgit_hash_unknown_final,\n+\t\tgit_hash_unknown_final_oid,\n \t\tNULL,\n \t\tNULL,\n \t},\n@@ -155,6 +178,7 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {\n \t\tgit_hash_sha1_clone,\n \t\tgit_hash_sha1_update,\n \t\tgit_hash_sha1_final,\n+\t\tgit_hash_sha1_final_oid,\n \t\t&empty_tree_oid,\n \t\t&empty_blob_oid,\n \t},\n@@ -169,6 +193,7 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {\n \t\tgit_hash_sha256_clone,\n \t\tgit_hash_sha256_update,\n \t\tgit_hash_sha256_final,\n+\t\tgit_hash_sha256_final_oid,\n \t\t&empty_tree_oid_sha256,\n \t\t&empty_blob_oid_sha256,\n \t}\n"},{"id":"421539","messageId":"20210410152140.3525040-8-sandals@crustytoothpaste.net","threadId":"55472","inReplyTo":"20210410152140.3525040-1-sandals@crustytoothpaste.net","subject":"[PATCH 07/15] builtin/pack-redundant: avoid casting buffers to struct object_id","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-10T15:21:32Z","receivedAt":"2021-04-10T15:22:38Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Now that we need our instances of struct object_id to be zero padded, we\ncan no longer cast unsigned char buffers to be pointers to struct\nobject_id.  This file reads data out of the pack objects and then\ninserts it directly into a linked list item which is a pointer to struct\nobject_id.  Instead, let's have the linked list item hold its own struct\nobject_id and copy the data into it.\n\nIn addition, since these are not really pointers to struct object_id,\nstop passing them around as such, and call them what they really are:\npointers to unsigned char.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n builtin/pack-redundant.c | 28 ++++++++++++++--------------\n 1 file changed, 14 insertions(+), 14 deletions(-)\n\ndiff --git a/builtin/pack-redundant.c b/builtin/pack-redundant.c\nindex 7102996c75..8bf5c0acad 100644\n--- a/builtin/pack-redundant.c\n+++ b/builtin/pack-redundant.c\n@@ -20,7 +20,7 @@ static int load_all_packs, verbose, alt_odb;\n \n struct llist_item {\n \tstruct llist_item *next;\n-\tconst struct object_id *oid;\n+\tstruct object_id oid;\n };\n static struct llist {\n \tstruct llist_item *front;\n@@ -95,10 +95,10 @@ static struct llist * llist_copy(struct llist *list)\n \n static inline struct llist_item *llist_insert(struct llist *list,\n \t\t\t\t\t      struct llist_item *after,\n-\t\t\t\t\t      const struct object_id *oid)\n+\t\t\t\t\t      const unsigned char *oid)\n {\n \tstruct llist_item *new_item = llist_item_get();\n-\tnew_item->oid = oid;\n+\toidread(&new_item->oid, oid);\n \tnew_item->next = NULL;\n \n \tif (after != NULL) {\n@@ -118,7 +118,7 @@ static inline struct llist_item *llist_insert(struct llist *list,\n }\n \n static inline struct llist_item *llist_insert_back(struct llist *list,\n-\t\t\t\t\t\t   const struct object_id *oid)\n+\t\t\t\t\t\t   const unsigned char *oid)\n {\n \treturn llist_insert(list, list->back, oid);\n }\n@@ -130,9 +130,9 @@ static inline struct llist_item *llist_insert_sorted_unique(struct llist *list,\n \n \tl = (hint == NULL) ? list->front : hint;\n \twhile (l) {\n-\t\tint cmp = oidcmp(l->oid, oid);\n+\t\tint cmp = oidcmp(&l->oid, oid);\n \t\tif (cmp > 0) { /* we insert before this entry */\n-\t\t\treturn llist_insert(list, prev, oid);\n+\t\t\treturn llist_insert(list, prev, oid->hash);\n \t\t}\n \t\tif (!cmp) { /* already exists */\n \t\t\treturn l;\n@@ -141,11 +141,11 @@ static inline struct llist_item *llist_insert_sorted_unique(struct llist *list,\n \t\tl = l->next;\n \t}\n \t/* insert at the end */\n-\treturn llist_insert_back(list, oid);\n+\treturn llist_insert_back(list, oid->hash);\n }\n \n /* returns a pointer to an item in front of sha1 */\n-static inline struct llist_item * llist_sorted_remove(struct llist *list, const struct object_id *oid, struct llist_item *hint)\n+static inline struct llist_item * llist_sorted_remove(struct llist *list, const unsigned char *oid, struct llist_item *hint)\n {\n \tstruct llist_item *prev, *l;\n \n@@ -153,7 +153,7 @@ static inline struct llist_item * llist_sorted_remove(struct llist *list, const\n \tl = (hint == NULL) ? list->front : hint;\n \tprev = NULL;\n \twhile (l) {\n-\t\tconst int cmp = oidcmp(l->oid, oid);\n+\t\tconst int cmp = hashcmp(l->oid.hash, oid);\n \t\tif (cmp > 0) /* not in list, since sorted */\n \t\t\treturn prev;\n \t\tif (!cmp) { /* found */\n@@ -188,7 +188,7 @@ static void llist_sorted_difference_inplace(struct llist *A,\n \tb = B->front;\n \n \twhile (b) {\n-\t\thint = llist_sorted_remove(A, b->oid, hint);\n+\t\thint = llist_sorted_remove(A, b->oid.hash, hint);\n \t\tb = b->next;\n \t}\n }\n@@ -260,10 +260,10 @@ static void cmp_two_packs(struct pack_list *p1, struct pack_list *p2)\n \t\t/* cmp ~ p1 - p2 */\n \t\tif (cmp == 0) {\n \t\t\tp1_hint = llist_sorted_remove(p1->unique_objects,\n-\t\t\t\t\t(const struct object_id *)(p1_base + p1_off),\n+\t\t\t\t\tp1_base + p1_off,\n \t\t\t\t\tp1_hint);\n \t\t\tp2_hint = llist_sorted_remove(p2->unique_objects,\n-\t\t\t\t\t(const struct object_id *)(p1_base + p1_off),\n+\t\t\t\t\tp1_base + p1_off,\n \t\t\t\t\tp2_hint);\n \t\t\tp1_off += p1_step;\n \t\t\tp2_off += p2_step;\n@@ -455,7 +455,7 @@ static void load_all_objects(void)\n \t\tl = pl->remaining_objects->front;\n \t\twhile (l) {\n \t\t\thint = llist_insert_sorted_unique(all_objects,\n-\t\t\t\t\t\t\t  l->oid, hint);\n+\t\t\t\t\t\t\t  &l->oid, hint);\n \t\t\tl = l->next;\n \t\t}\n \t\tpl = pl->next;\n@@ -521,7 +521,7 @@ static struct pack_list * add_pack(struct packed_git *p)\n \tbase += 256 * 4 + ((p->index_version < 2) ? 4 : 8);\n \tstep = the_hash_algo->rawsz + ((p->index_version < 2) ? 4 : 0);\n \twhile (off < p->num_objects * step) {\n-\t\tllist_insert_back(l.remaining_objects, (const struct object_id *)(base + off));\n+\t\tllist_insert_back(l.remaining_objects, base + off);\n \t\toff += step;\n \t}\n \tl.all_objects_size = l.remaining_objects->size;\n"},{"id":"421540","messageId":"20210410152140.3525040-9-sandals@crustytoothpaste.net","threadId":"55472","inReplyTo":"20210410152140.3525040-1-sandals@crustytoothpaste.net","subject":"[PATCH 08/15] cache: compare the entire buffer for struct object_id","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-10T15:21:33Z","receivedAt":"2021-04-10T15:22:39Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Currently, when we compare two object IDs, we have to take a branch to\ndetermine what the hash size is supposed to be.  The compiler can\noptimize well for a single length, but has trouble when there are two\npossible lengths.\n\nThere is, however, an alternative: we can ensure that we always compare\nthe full length of the hash buffer, but in turn we must zero the\nremainder of the buffer when using SHA-1; otherwise, we'll end up with\nincompatible junk at the end of otherwise equivalent object IDs that\nwill prevent them from matching.  This is an acceptable tradeoff,\nbecause we generally read an object ID in once, but then compare it\nagainst others multiple times.\n\nThis latter approach also has some benefits as well: since we will have\nannotated every location in which we load an object ID into an instance\nof struct object_id, if we want to set the hash algorithm for the object\nID, we can do so at the same time.\n\nAdopt this latter approach, since it provides us greater flexibility and\nlets us read and store object IDs for multiple algorithms at once.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n hash.h        | 13 ++++++++++---\n hex.c         |  9 ++++++---\n notes.c       |  3 +++\n object-file.c |  1 +\n 4 files changed, 20 insertions(+), 6 deletions(-)\n\ndiff --git a/hash.h b/hash.h\nindex c8f03d8aee..04eba5c56b 100644\n--- a/hash.h\n+++ b/hash.h\n@@ -205,7 +205,7 @@ static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)\n \n static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)\n {\n-\treturn hashcmp(oid1->hash, oid2->hash);\n+\treturn memcmp(oid1->hash, oid2->hash, GIT_MAX_RAWSZ);\n }\n \n static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2)\n@@ -221,7 +221,7 @@ static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2)\n \n static inline int oideq(const struct object_id *oid1, const struct object_id *oid2)\n {\n-\treturn hasheq(oid1->hash, oid2->hash);\n+\treturn !memcmp(oid1->hash, oid2->hash, GIT_MAX_RAWSZ);\n }\n \n static inline int is_null_oid(const struct object_id *oid)\n@@ -258,7 +258,9 @@ static inline void oidclr(struct object_id *oid)\n \n static inline void oidread(struct object_id *oid, const unsigned char *hash)\n {\n-\tmemcpy(oid->hash, hash, the_hash_algo->rawsz);\n+\tsize_t rawsz = the_hash_algo->rawsz;\n+\tmemcpy(oid->hash, hash, rawsz);\n+\tmemset(oid->hash + rawsz, 0, GIT_MAX_RAWSZ - rawsz);\n }\n \n static inline int is_empty_blob_sha1(const unsigned char *sha1)\n@@ -281,6 +283,11 @@ static inline int is_empty_tree_oid(const struct object_id *oid)\n \treturn oideq(oid, the_hash_algo->empty_tree);\n }\n \n+static inline void oid_pad_buffer(struct object_id *oid, const struct git_hash_algo *algop)\n+{\n+\tmemset(oid->hash + algop->rawsz, 0, GIT_MAX_RAWSZ - algop->rawsz);\n+}\n+\n const char *empty_tree_oid_hex(void);\n const char *empty_blob_oid_hex(void);\n \ndiff --git a/hex.c b/hex.c\nindex da51e64929..5fa3e71cb9 100644\n--- a/hex.c\n+++ b/hex.c\n@@ -69,7 +69,10 @@ int get_sha1_hex(const char *hex, unsigned char *sha1)\n int get_oid_hex_algop(const char *hex, struct object_id *oid,\n \t\t      const struct git_hash_algo *algop)\n {\n-\treturn get_hash_hex_algop(hex, oid->hash, algop);\n+\tint ret = get_hash_hex_algop(hex, oid->hash, algop);\n+\tif (!ret)\n+\t\toid_pad_buffer(oid, algop);\n+\treturn ret;\n }\n \n /*\n@@ -80,7 +83,7 @@ int get_oid_hex_any(const char *hex, struct object_id *oid)\n {\n \tint i;\n \tfor (i = GIT_HASH_NALGOS - 1; i > 0; i--) {\n-\t\tif (!get_hash_hex_algop(hex, oid->hash, &hash_algos[i]))\n+\t\tif (!get_oid_hex_algop(hex, oid, &hash_algos[i]))\n \t\t\treturn i;\n \t}\n \treturn GIT_HASH_UNKNOWN;\n@@ -95,7 +98,7 @@ int parse_oid_hex_algop(const char *hex, struct object_id *oid,\n \t\t\tconst char **end,\n \t\t\tconst struct git_hash_algo *algop)\n {\n-\tint ret = get_hash_hex_algop(hex, oid->hash, algop);\n+\tint ret = get_oid_hex_algop(hex, oid, algop);\n \tif (!ret)\n \t\t*end = hex + algop->hexsz;\n \treturn ret;\ndiff --git a/notes.c b/notes.c\nindex a44b25858f..1dfe9e2b9f 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -455,6 +455,8 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\tCALLOC_ARRAY(l, 1);\n \t\toidcpy(&l->key_oid, &object_oid);\n \t\toidcpy(&l->val_oid, &entry.oid);\n+\t\toid_pad_buffer(&l->key_oid, the_hash_algo);\n+\t\toid_pad_buffer(&l->val_oid, the_hash_algo);\n \t\tif (note_tree_insert(t, node, n, l, type,\n \t\t\t\t     combine_notes_concatenate))\n \t\t\tdie(\"Failed to load %s %s into notes tree \"\n@@ -484,6 +486,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n \t\t\t\tstrbuf_addch(&non_note_path, '/');\n \t\t\t}\n \t\t\tstrbuf_addstr(&non_note_path, entry.path);\n+\t\t\toid_pad_buffer(&entry.oid, the_hash_algo);\n \t\t\tadd_non_note(t, strbuf_detach(&non_note_path, NULL),\n \t\t\t\t     entry.mode, entry.oid.hash);\n \t\t}\ndiff --git a/object-file.c b/object-file.c\nindex 3f43c376e7..8e338247cc 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -2352,6 +2352,7 @@ int for_each_file_in_obj_subdir(unsigned int subdir_nr,\n \t\tif (namelen == the_hash_algo->hexsz - 2 &&\n \t\t    !hex_to_bytes(oid.hash + 1, de->d_name,\n \t\t\t\t  the_hash_algo->rawsz - 1)) {\n+\t\t\toid_pad_buffer(&oid, the_hash_algo);\n \t\t\tif (obj_cb) {\n \t\t\t\tr = obj_cb(&oid, path->buf, data);\n \t\t\t\tif (r)\n"},{"id":"421542","messageId":"20210410152140.3525040-11-sandals@crustytoothpaste.net","threadId":"55472","inReplyTo":"20210410152140.3525040-1-sandals@crustytoothpaste.net","subject":"[PATCH 10/15] hash: provide per-algorithm null OIDs","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-10T15:21:35Z","receivedAt":"2021-04-10T15:22:40Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Up until recently, object IDs did not have an algorithm member, only a\nhash.  Consequently, it was possible to share one null (all-zeros)\nobject ID among all hash algorithms.  Now that we're going to be\nhandling objects from multiple hash algorithms, it's important to make\nsure that all object IDs have a correct algorithm field.\n\nIntroduce a per-algorithm null OID, and add it to struct hash_algo.\nIntroduce a wrapper function as well, and use it everywhere we used to\nuse the null_oid constant.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n archive.c                                    |  4 ++-\n blame.c                                      |  2 +-\n branch.c                                     |  2 +-\n builtin/checkout.c                           |  6 ++--\n builtin/clone.c                              |  2 +-\n builtin/describe.c                           |  2 +-\n builtin/diff.c                               |  2 +-\n builtin/fast-export.c                        | 10 +++----\n builtin/grep.c                               |  2 +-\n builtin/ls-files.c                           |  2 +-\n builtin/rebase.c                             |  4 +--\n builtin/receive-pack.c                       |  2 +-\n builtin/submodule--helper.c                  | 29 ++++++++++----------\n builtin/unpack-objects.c                     |  3 +-\n builtin/worktree.c                           |  4 +--\n combine-diff.c                               |  2 +-\n connect.c                                    |  2 +-\n diff-lib.c                                   |  6 ++--\n diff-no-index.c                              |  2 +-\n diff.c                                       |  4 +--\n dir.c                                        |  2 +-\n grep.c                                       |  2 +-\n hash.h                                       |  7 +++--\n log-tree.c                                   |  2 +-\n merge-ort.c                                  | 20 +++++++-------\n merge-recursive.c                            | 10 +++----\n notes-merge.c                                |  2 +-\n notes.c                                      |  2 +-\n object-file.c                                | 15 +++++++++-\n parse-options-cb.c                           |  2 +-\n range-diff.c                                 |  2 +-\n refs.c                                       |  4 +--\n refs/debug.c                                 |  2 +-\n refs/files-backend.c                         |  2 +-\n reset.c                                      |  2 +-\n sequencer.c                                  |  4 +--\n submodule-config.c                           |  2 +-\n submodule.c                                  | 26 ++++++++++--------\n t/helper/test-submodule-nested-repo-config.c |  2 +-\n tree-diff.c                                  |  4 +--\n wt-status.c                                  |  4 +--\n xdiff-interface.c                            |  2 +-\n 42 files changed, 117 insertions(+), 95 deletions(-)\n\ndiff --git a/archive.c b/archive.c\nindex 6cfb9e42d6..ff2bb54f62 100644\n--- a/archive.c\n+++ b/archive.c\n@@ -275,9 +275,11 @@ int write_archive_entries(struct archiver_args *args,\n \tint err;\n \tstruct strbuf path_in_archive = STRBUF_INIT;\n \tstruct strbuf content = STRBUF_INIT;\n-\tstruct object_id fake_oid = null_oid;\n+\tstruct object_id fake_oid;\n \tint i;\n \n+\toidcpy(&fake_oid, null_oid());\n+\n \tif (args->baselen > 0 && args->base[args->baselen - 1] == '/') {\n \t\tsize_t len = args->baselen;\n \ndiff --git a/blame.c b/blame.c\nindex 5018bb8fb2..206c295660 100644\n--- a/blame.c\n+++ b/blame.c\n@@ -242,7 +242,7 @@ static struct commit *fake_working_tree_commit(struct repository *r,\n \t\tswitch (st.st_mode & S_IFMT) {\n \t\tcase S_IFREG:\n \t\t\tif (opt->flags.allow_textconv &&\n-\t\t\t    textconv_object(r, read_from, mode, &null_oid, 0, &buf_ptr, &buf_len))\n+\t\t\t    textconv_object(r, read_from, mode, null_oid(), 0, &buf_ptr, &buf_len))\n \t\t\t\tstrbuf_attach(&buf, buf_ptr, buf_len, buf_len + 1);\n \t\t\telse if (strbuf_read_file(&buf, read_from, st.st_size) != st.st_size)\n \t\t\t\tdie_errno(\"cannot open or read '%s'\", read_from);\ndiff --git a/branch.c b/branch.c\nindex 9c9dae1eae..8db10f8496 100644\n--- a/branch.c\n+++ b/branch.c\n@@ -322,7 +322,7 @@ void create_branch(struct repository *r,\n \t\ttransaction = ref_transaction_begin(&err);\n \t\tif (!transaction ||\n \t\t    ref_transaction_update(transaction, ref.buf,\n-\t\t\t\t\t   &oid, forcing ? NULL : &null_oid,\n+\t\t\t\t\t   &oid, forcing ? NULL : null_oid(),\n \t\t\t\t\t   0, msg, &err) ||\n \t\t    ref_transaction_commit(transaction, &err))\n \t\t\tdie(\"%s\", err.buf);\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 4c696ef480..8a12b92c5f 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -106,8 +106,8 @@ static int post_checkout_hook(struct commit *old_commit, struct commit *new_comm\n \t\t\t      int changed)\n {\n \treturn run_hook_le(NULL, \"post-checkout\",\n-\t\t\t   oid_to_hex(old_commit ? &old_commit->object.oid : &null_oid),\n-\t\t\t   oid_to_hex(new_commit ? &new_commit->object.oid : &null_oid),\n+\t\t\t   oid_to_hex(old_commit ? &old_commit->object.oid : null_oid()),\n+\t\t\t   oid_to_hex(new_commit ? &new_commit->object.oid : null_oid()),\n \t\t\t   changed ? \"1\" : \"0\", NULL);\n \t/* \"new_commit\" can be NULL when checking out from the index before\n \t   a commit exists. */\n@@ -638,7 +638,7 @@ static int reset_tree(struct tree *tree, const struct checkout_opts *o,\n \topts.src_index = &the_index;\n \topts.dst_index = &the_index;\n \tinit_checkout_metadata(&opts.meta, info->refname,\n-\t\t\t       info->commit ? &info->commit->object.oid : &null_oid,\n+\t\t\t       info->commit ? &info->commit->object.oid : null_oid(),\n \t\t\t       NULL);\n \tparse_tree(tree);\n \tinit_tree_desc(&tree_desc, tree->buffer, tree->size);\ndiff --git a/builtin/clone.c b/builtin/clone.c\nindex f6b0c48bed..eeb74c0217 100644\n--- a/builtin/clone.c\n+++ b/builtin/clone.c\n@@ -820,7 +820,7 @@ static int checkout(int submodule_progress)\n \tif (write_locked_index(&the_index, &lock_file, COMMIT_LOCK))\n \t\tdie(_(\"unable to write new index file\"));\n \n-\terr |= run_hook_le(NULL, \"post-checkout\", oid_to_hex(&null_oid),\n+\terr |= run_hook_le(NULL, \"post-checkout\", oid_to_hex(null_oid()),\n \t\t\t   oid_to_hex(&oid), \"1\", NULL);\n \n \tif (!err && (option_recurse_submodules.nr > 0)) {\ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex 40482d8e9f..e912ba50d7 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -502,7 +502,7 @@ static void describe_blob(struct object_id oid, struct strbuf *dst)\n {\n \tstruct rev_info revs;\n \tstruct strvec args = STRVEC_INIT;\n-\tstruct process_commit_data pcd = { null_oid, oid, dst, &revs};\n+\tstruct process_commit_data pcd = { *null_oid(), oid, dst, &revs};\n \n \tstrvec_pushl(&args, \"internal: The first arg is not parsed\",\n \t\t     \"--objects\", \"--in-commit-order\", \"--reverse\", \"HEAD\",\ndiff --git a/builtin/diff.c b/builtin/diff.c\nindex 617b9a4101..2d87c37a17 100644\n--- a/builtin/diff.c\n+++ b/builtin/diff.c\n@@ -98,7 +98,7 @@ static int builtin_diff_b_f(struct rev_info *revs,\n \n \tstuff_change(&revs->diffopt,\n \t\t     blob[0]->mode, canon_mode(st.st_mode),\n-\t\t     &blob[0]->item->oid, &null_oid,\n+\t\t     &blob[0]->item->oid, null_oid(),\n \t\t     1, 0,\n \t\t     blob[0]->path ? blob[0]->path : path,\n \t\t     path);\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex 85a76e0ef8..3c20f164f0 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -870,7 +870,7 @@ static void handle_tag(const char *name, struct tag *tag)\n \t\t\t\tp = rewrite_commit((struct commit *)tagged);\n \t\t\t\tif (!p) {\n \t\t\t\t\tprintf(\"reset %s\\nfrom %s\\n\\n\",\n-\t\t\t\t\t       name, oid_to_hex(&null_oid));\n+\t\t\t\t\t       name, oid_to_hex(null_oid()));\n \t\t\t\t\tfree(buf);\n \t\t\t\t\treturn;\n \t\t\t\t}\n@@ -884,7 +884,7 @@ static void handle_tag(const char *name, struct tag *tag)\n \n \tif (tagged->type == OBJ_TAG) {\n \t\tprintf(\"reset %s\\nfrom %s\\n\\n\",\n-\t\t       name, oid_to_hex(&null_oid));\n+\t\t       name, oid_to_hex(null_oid()));\n \t}\n \tskip_prefix(name, \"refs/tags/\", &name);\n \tprintf(\"tag %s\\n\", name);\n@@ -1016,7 +1016,7 @@ static void handle_tags_and_duplicates(struct string_list *extras)\n \t\t\t\t * it.\n \t\t\t\t */\n \t\t\t\tprintf(\"reset %s\\nfrom %s\\n\\n\",\n-\t\t\t\t       name, oid_to_hex(&null_oid));\n+\t\t\t\t       name, oid_to_hex(null_oid()));\n \t\t\t\tcontinue;\n \t\t\t}\n \n@@ -1035,7 +1035,7 @@ static void handle_tags_and_duplicates(struct string_list *extras)\n \t\t\t\tif (!reference_excluded_commits) {\n \t\t\t\t\t/* delete the ref */\n \t\t\t\t\tprintf(\"reset %s\\nfrom %s\\n\\n\",\n-\t\t\t\t\t       name, oid_to_hex(&null_oid));\n+\t\t\t\t\t       name, oid_to_hex(null_oid()));\n \t\t\t\t\tcontinue;\n \t\t\t\t}\n \t\t\t\t/* set ref to commit using oid, not mark */\n@@ -1146,7 +1146,7 @@ static void handle_deletes(void)\n \t\t\tcontinue;\n \n \t\tprintf(\"reset %s\\nfrom %s\\n\\n\",\n-\t\t\t\trefspec->dst, oid_to_hex(&null_oid));\n+\t\t\t\trefspec->dst, oid_to_hex(null_oid()));\n \t}\n }\n \ndiff --git a/builtin/grep.c b/builtin/grep.c\nindex 5de725f904..e0e326004e 100644\n--- a/builtin/grep.c\n+++ b/builtin/grep.c\n@@ -421,7 +421,7 @@ static int grep_submodule(struct grep_opt *opt,\n \tstruct grep_opt subopt;\n \tint hit;\n \n-\tsub = submodule_from_path(superproject, &null_oid, path);\n+\tsub = submodule_from_path(superproject, null_oid(), path);\n \n \tif (!is_submodule_active(superproject, path))\n \t\treturn 0;\ndiff --git a/builtin/ls-files.c b/builtin/ls-files.c\nindex 60a2913a01..c589eb7f89 100644\n--- a/builtin/ls-files.c\n+++ b/builtin/ls-files.c\n@@ -210,7 +210,7 @@ static void show_submodule(struct repository *superproject,\n {\n \tstruct repository subrepo;\n \tconst struct submodule *sub = submodule_from_path(superproject,\n-\t\t\t\t\t\t\t  &null_oid, path);\n+\t\t\t\t\t\t\t  null_oid(), path);\n \n \tif (repo_submodule_init(&subrepo, superproject, sub))\n \t\treturn;\ndiff --git a/builtin/rebase.c b/builtin/rebase.c\nindex 783b526f6e..c9206b40ea 100644\n--- a/builtin/rebase.c\n+++ b/builtin/rebase.c\n@@ -485,7 +485,7 @@ static const char * const builtin_rebase_interactive_usage[] = {\n int cmd_rebase__interactive(int argc, const char **argv, const char *prefix)\n {\n \tstruct rebase_options opts = REBASE_OPTIONS_INIT;\n-\tstruct object_id squash_onto = null_oid;\n+\tstruct object_id squash_onto = *null_oid();\n \tenum action command = ACTION_NONE;\n \tstruct option options[] = {\n \t\tOPT_NEGBIT(0, \"ff\", &opts.flags, N_(\"allow fast-forward\"),\n@@ -1139,7 +1139,7 @@ static int can_fast_forward(struct commit *onto, struct commit *upstream,\n \n \tmerge_bases = get_merge_bases(onto, head);\n \tif (!merge_bases || merge_bases->next) {\n-\t\toidcpy(merge_base, &null_oid);\n+\t\toidcpy(merge_base, null_oid());\n \t\tgoto done;\n \t}\n \ndiff --git a/builtin/receive-pack.c b/builtin/receive-pack.c\nindex 6bc12c828a..a34742513a 100644\n--- a/builtin/receive-pack.c\n+++ b/builtin/receive-pack.c\n@@ -329,7 +329,7 @@ static void write_head_info(void)\n \tfor_each_alternate_ref(show_one_alternate_ref, &seen);\n \toidset_clear(&seen);\n \tif (!sent_capabilities)\n-\t\tshow_ref(\"capabilities^{}\", &null_oid);\n+\t\tshow_ref(\"capabilities^{}\", null_oid());\n \n \tadvertise_shallow_grafts(1);\n \ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 9d505a6329..d55f6262e9 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -426,7 +426,8 @@ static int module_list(int argc, const char **argv, const char *prefix)\n \t\tconst struct cache_entry *ce = list.entries[i];\n \n \t\tif (ce_stage(ce))\n-\t\t\tprintf(\"%06o %s U\\t\", ce->ce_mode, oid_to_hex(&null_oid));\n+\t\t\tprintf(\"%06o %s U\\t\", ce->ce_mode,\n+\t\t\t       oid_to_hex(null_oid()));\n \t\telse\n \t\t\tprintf(\"%06o %s %d\\t\", ce->ce_mode,\n \t\t\t       oid_to_hex(&ce->oid), ce_stage(ce));\n@@ -466,7 +467,7 @@ static void runcommand_in_submodule_cb(const struct cache_entry *list_item,\n \n \tdisplaypath = get_submodule_displaypath(path, info->prefix);\n \n-\tsub = submodule_from_path(the_repository, &null_oid, path);\n+\tsub = submodule_from_path(the_repository, null_oid(), path);\n \n \tif (!sub)\n \t\tdie(_(\"No url found for submodule path '%s' in .gitmodules\"),\n@@ -623,7 +624,7 @@ static void init_submodule(const char *path, const char *prefix,\n \n \tdisplaypath = get_submodule_displaypath(path, prefix);\n \n-\tsub = submodule_from_path(the_repository, &null_oid, path);\n+\tsub = submodule_from_path(the_repository, null_oid(), path);\n \n \tif (!sub)\n \t\tdie(_(\"No url found for submodule path '%s' in .gitmodules\"),\n@@ -783,14 +784,14 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,\n \tstruct strbuf buf = STRBUF_INIT;\n \tconst char *git_dir;\n \n-\tif (!submodule_from_path(the_repository, &null_oid, path))\n+\tif (!submodule_from_path(the_repository, null_oid(), path))\n \t\tdie(_(\"no submodule mapping found in .gitmodules for path '%s'\"),\n \t\t      path);\n \n \tdisplaypath = get_submodule_displaypath(path, prefix);\n \n \tif ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) {\n-\t\tprint_status(flags, 'U', path, &null_oid, displaypath);\n+\t\tprint_status(flags, 'U', path, null_oid(), displaypath);\n \t\tgoto cleanup;\n \t}\n \n@@ -916,7 +917,7 @@ static int module_name(int argc, const char **argv, const char *prefix)\n \tif (argc != 2)\n \t\tusage(_(\"git submodule--helper name <path>\"));\n \n-\tsub = submodule_from_path(the_repository, &null_oid, argv[1]);\n+\tsub = submodule_from_path(the_repository, null_oid(), argv[1]);\n \n \tif (!sub)\n \t\tdie(_(\"no submodule mapping found in .gitmodules for path '%s'\"),\n@@ -1040,7 +1041,7 @@ static void generate_submodule_summary(struct summary_cb *info,\n \tchar *errmsg = NULL;\n \tint total_commits = -1;\n \n-\tif (!info->cached && oideq(&p->oid_dst, &null_oid)) {\n+\tif (!info->cached && oideq(&p->oid_dst, null_oid())) {\n \t\tif (S_ISGITLINK(p->mod_dst)) {\n \t\t\tstruct ref_store *refs = get_submodule_ref_store(p->sm_path);\n \t\t\tif (refs)\n@@ -1177,7 +1178,7 @@ static void prepare_submodule_summary(struct summary_cb *info,\n \n \t\tif (info->for_status && p->status != 'A' &&\n \t\t    (sub = submodule_from_path(the_repository,\n-\t\t\t\t\t       &null_oid, p->sm_path))) {\n+\t\t\t\t\t       null_oid(), p->sm_path))) {\n \t\t\tchar *config_key = NULL;\n \t\t\tconst char *value;\n \t\t\tint ignore_all = 0;\n@@ -1373,7 +1374,7 @@ static void sync_submodule(const char *path, const char *prefix,\n \tif (!is_submodule_active(the_repository, path))\n \t\treturn;\n \n-\tsub = submodule_from_path(the_repository, &null_oid, path);\n+\tsub = submodule_from_path(the_repository, null_oid(), path);\n \n \tif (sub && sub->url) {\n \t\tif (starts_with_dot_dot_slash(sub->url) ||\n@@ -1525,7 +1526,7 @@ static void deinit_submodule(const char *path, const char *prefix,\n \tstruct strbuf sb_config = STRBUF_INIT;\n \tchar *sub_git_dir = xstrfmt(\"%s/.git\", path);\n \n-\tsub = submodule_from_path(the_repository, &null_oid, path);\n+\tsub = submodule_from_path(the_repository, null_oid(), path);\n \n \tif (!sub || !sub->name)\n \t\tgoto cleanup;\n@@ -1925,7 +1926,7 @@ static void determine_submodule_update_strategy(struct repository *r,\n \t\t\t\t\t\tconst char *update,\n \t\t\t\t\t\tstruct submodule_update_strategy *out)\n {\n-\tconst struct submodule *sub = submodule_from_path(r, &null_oid, path);\n+\tconst struct submodule *sub = submodule_from_path(r, null_oid(), path);\n \tchar *key;\n \tconst char *val;\n \n@@ -2077,7 +2078,7 @@ static int prepare_to_clone_next_submodule(const struct cache_entry *ce,\n \t\tgoto cleanup;\n \t}\n \n-\tsub = submodule_from_path(the_repository, &null_oid, ce->name);\n+\tsub = submodule_from_path(the_repository, null_oid(), ce->name);\n \n \tif (suc->recursive_prefix)\n \t\tdisplaypath = relative_path(suc->recursive_prefix,\n@@ -2395,7 +2396,7 @@ static const char *remote_submodule_branch(const char *path)\n \tconst char *branch = NULL;\n \tchar *key;\n \n-\tsub = submodule_from_path(the_repository, &null_oid, path);\n+\tsub = submodule_from_path(the_repository, null_oid(), path);\n \tif (!sub)\n \t\treturn NULL;\n \n@@ -2533,7 +2534,7 @@ static int ensure_core_worktree(int argc, const char **argv, const char *prefix)\n \n \tpath = argv[1];\n \n-\tsub = submodule_from_path(the_repository, &null_oid, path);\n+\tsub = submodule_from_path(the_repository, null_oid(), path);\n \tif (!sub)\n \t\tBUG(\"We could get the submodule handle before?\");\n \ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex 6ac90dc5f7..4a9466295b 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -421,7 +421,8 @@ static void unpack_delta_entry(enum object_type type, unsigned long delta_size,\n \t\t\t * has not been resolved yet.\n \t\t\t */\n \t\t\toidclr(&obj_list[nr].oid);\n-\t\t\tadd_delta_to_list(nr, &null_oid, base_offset, delta_data, delta_size);\n+\t\t\tadd_delta_to_list(nr, null_oid(), base_offset,\n+\t\t\t\t\t  delta_data, delta_size);\n \t\t\treturn;\n \t\t}\n \t}\ndiff --git a/builtin/worktree.c b/builtin/worktree.c\nindex 8771453493..f754978e47 100644\n--- a/builtin/worktree.c\n+++ b/builtin/worktree.c\n@@ -331,7 +331,7 @@ static int add_worktree(const char *path, const char *refname,\n \t */\n \tstrbuf_reset(&sb);\n \tstrbuf_addf(&sb, \"%s/HEAD\", sb_repo.buf);\n-\twrite_file(sb.buf, \"%s\", oid_to_hex(&null_oid));\n+\twrite_file(sb.buf, \"%s\", oid_to_hex(null_oid()));\n \tstrbuf_reset(&sb);\n \tstrbuf_addf(&sb, \"%s/commondir\", sb_repo.buf);\n \twrite_file(sb.buf, \"../..\");\n@@ -394,7 +394,7 @@ static int add_worktree(const char *path, const char *refname,\n \t\t\tcp.argv = NULL;\n \t\t\tcp.trace2_hook_name = \"post-checkout\";\n \t\t\tstrvec_pushl(&cp.args, absolute_path(hook),\n-\t\t\t\t     oid_to_hex(&null_oid),\n+\t\t\t\t     oid_to_hex(null_oid()),\n \t\t\t\t     oid_to_hex(&commit->object.oid),\n \t\t\t\t     \"1\", NULL);\n \t\t\tret = run_command(&cp);\ndiff --git a/combine-diff.c b/combine-diff.c\nindex 06635f91bc..7d925ce9ce 100644\n--- a/combine-diff.c\n+++ b/combine-diff.c\n@@ -1068,7 +1068,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,\n \t\t\t\t\t\t   &result_size, NULL, NULL);\n \t\t} else if (textconv) {\n \t\t\tstruct diff_filespec *df = alloc_filespec(elem->path);\n-\t\t\tfill_filespec(df, &null_oid, 0, st.st_mode);\n+\t\t\tfill_filespec(df, null_oid(), 0, st.st_mode);\n \t\t\tresult_size = fill_textconv(opt->repo, textconv, df, &result);\n \t\t\tfree_filespec(df);\n \t\t} else if (0 <= (fd = open(elem->path, O_RDONLY))) {\ndiff --git a/connect.c b/connect.c\nindex 40b5c15f81..70b13389ba 100644\n--- a/connect.c\n+++ b/connect.c\n@@ -254,7 +254,7 @@ static int process_dummy_ref(const struct packet_reader *reader)\n \t\treturn 0;\n \tname++;\n \n-\treturn oideq(&null_oid, &oid) && !strcmp(name, \"capabilities^{}\");\n+\treturn oideq(null_oid(), &oid) && !strcmp(name, \"capabilities^{}\");\n }\n \n static void check_no_capabilities(const char *line, int len)\ndiff --git a/diff-lib.c b/diff-lib.c\nindex e5a58c9259..c2ac9250fe 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -232,7 +232,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)\n \t\t\t\t   ce_intent_to_add(ce)) {\n \t\t\t\tnewmode = ce_mode_from_stat(ce, st.st_mode);\n \t\t\t\tdiff_addremove(&revs->diffopt, '+', newmode,\n-\t\t\t\t\t       &null_oid, 0, ce->name, 0);\n+\t\t\t\t\t       null_oid(), 0, ce->name, 0);\n \t\t\t\tcontinue;\n \t\t\t}\n \n@@ -249,7 +249,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option)\n \t\t}\n \t\toldmode = ce->ce_mode;\n \t\told_oid = &ce->oid;\n-\t\tnew_oid = changed ? &null_oid : &ce->oid;\n+\t\tnew_oid = changed ? null_oid() : &ce->oid;\n \t\tdiff_change(&revs->diffopt, oldmode, newmode,\n \t\t\t    old_oid, new_oid,\n \t\t\t    !is_null_oid(old_oid),\n@@ -307,7 +307,7 @@ static int get_stat_data(const struct index_state *istate,\n \t\t\t\t\t\t    0, dirty_submodule);\n \t\tif (changed) {\n \t\t\tmode = ce_mode_from_stat(ce, st.st_mode);\n-\t\t\toid = &null_oid;\n+\t\t\toid = null_oid();\n \t\t}\n \t}\n \ndiff --git a/diff-no-index.c b/diff-no-index.c\nindex 7814eabfe0..308922e2b3 100644\n--- a/diff-no-index.c\n+++ b/diff-no-index.c\n@@ -83,7 +83,7 @@ static struct diff_filespec *noindex_filespec(const char *name, int mode)\n \tif (!name)\n \t\tname = \"/dev/null\";\n \ts = alloc_filespec(name);\n-\tfill_filespec(s, &null_oid, 0, mode);\n+\tfill_filespec(s, null_oid(), 0, mode);\n \tif (name == file_from_standard_input)\n \t\tpopulate_from_stdin(s);\n \treturn s;\ndiff --git a/diff.c b/diff.c\nindex 97c62f47df..7c730fe644 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -4190,7 +4190,7 @@ static struct diff_tempfile *prepare_temp_file(struct repository *r,\n \t\t\t\tdie_errno(\"readlink(%s)\", name);\n \t\t\tprep_temp_blob(r->index, name, temp, sb.buf, sb.len,\n \t\t\t\t       (one->oid_valid ?\n-\t\t\t\t\t&one->oid : &null_oid),\n+\t\t\t\t\t&one->oid : null_oid()),\n \t\t\t\t       (one->oid_valid ?\n \t\t\t\t\tone->mode : S_IFLNK));\n \t\t\tstrbuf_release(&sb);\n@@ -4199,7 +4199,7 @@ static struct diff_tempfile *prepare_temp_file(struct repository *r,\n \t\t\t/* we can borrow from the file in the work tree */\n \t\t\ttemp->name = name;\n \t\t\tif (!one->oid_valid)\n-\t\t\t\toid_to_hex_r(temp->hex, &null_oid);\n+\t\t\t\toid_to_hex_r(temp->hex, null_oid());\n \t\t\telse\n \t\t\t\toid_to_hex_r(temp->hex, &one->oid);\n \t\t\t/* Even though we may sometimes borrow the\ndiff --git a/dir.c b/dir.c\nindex 813dd7ba53..037474337f 100644\n--- a/dir.c\n+++ b/dir.c\n@@ -3556,7 +3556,7 @@ static void connect_wt_gitdir_in_nested(const char *sub_worktree,\n \t\t\t */\n \t\t\ti++;\n \n-\t\tsub = submodule_from_path(&subrepo, &null_oid, ce->name);\n+\t\tsub = submodule_from_path(&subrepo, null_oid(), ce->name);\n \t\tif (!sub || !is_submodule_active(&subrepo, ce->name))\n \t\t\t/* .gitmodules broken or inactive sub */\n \t\t\tcontinue;\ndiff --git a/grep.c b/grep.c\nindex c5c348be55..8f91af1cb0 100644\n--- a/grep.c\n+++ b/grep.c\n@@ -1494,7 +1494,7 @@ static int fill_textconv_grep(struct repository *r,\n \t\tfill_filespec(df, gs->identifier, 1, 0100644);\n \t\tbreak;\n \tcase GREP_SOURCE_FILE:\n-\t\tfill_filespec(df, &null_oid, 0, 0100644);\n+\t\tfill_filespec(df, null_oid(), 0, 0100644);\n \t\tbreak;\n \tdefault:\n \t\tBUG(\"attempt to textconv something without a path?\");\ndiff --git a/hash.h b/hash.h\nindex 3b114f053e..5fabf6e1ec 100644\n--- a/hash.h\n+++ b/hash.h\n@@ -170,6 +170,9 @@ struct git_hash_algo {\n \n \t/* The OID of the empty blob. */\n \tconst struct object_id *empty_blob;\n+\n+\t/* The all-zeros OID. */\n+\tconst struct object_id *null_oid;\n };\n extern const struct git_hash_algo hash_algos[GIT_HASH_NALGOS];\n \n@@ -190,7 +193,7 @@ static inline int hash_algo_by_ptr(const struct git_hash_algo *p)\n \n #define the_hash_algo the_repository->hash_algo\n \n-extern const struct object_id null_oid;\n+const struct object_id *null_oid(void);\n \n static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)\n {\n@@ -226,7 +229,7 @@ static inline int oideq(const struct object_id *oid1, const struct object_id *oi\n \n static inline int is_null_oid(const struct object_id *oid)\n {\n-\treturn oideq(oid, &null_oid);\n+\treturn oideq(oid, null_oid());\n }\n \n static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)\ndiff --git a/log-tree.c b/log-tree.c\nindex f3178a66a9..7b823786c2 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -419,7 +419,7 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,\n {\n \tconst char *extra_headers = opt->extra_headers;\n \tconst char *name = oid_to_hex(opt->zero_commit ?\n-\t\t\t\t      &null_oid : &commit->object.oid);\n+\t\t\t\t      null_oid() : &commit->object.oid);\n \n \t*need_8bit_cte_p = 0; /* unknown */\n \ndiff --git a/merge-ort.c b/merge-ort.c\nindex 5e118a85ee..3e552cfd21 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -1291,7 +1291,7 @@ static int handle_content_merge(struct merge_options *opt,\n \t\ttwo_way = ((S_IFMT & o->mode) != (S_IFMT & a->mode));\n \n \t\tmerge_status = merge_3way(opt, path,\n-\t\t\t\t\t  two_way ? &null_oid : &o->oid,\n+\t\t\t\t\t  two_way ? null_oid() : &o->oid,\n \t\t\t\t\t  &a->oid, &b->oid,\n \t\t\t\t\t  pathnames, extra_marker_size,\n \t\t\t\t\t  &result_buf);\n@@ -1313,7 +1313,7 @@ static int handle_content_merge(struct merge_options *opt,\n \t} else if (S_ISGITLINK(a->mode)) {\n \t\tint two_way = ((S_IFMT & o->mode) != (S_IFMT & a->mode));\n \t\tclean = merge_submodule(opt, pathnames[0],\n-\t\t\t\t\ttwo_way ? &null_oid : &o->oid,\n+\t\t\t\t\ttwo_way ? null_oid() : &o->oid,\n \t\t\t\t\t&a->oid, &b->oid, &result->oid);\n \t\tif (opt->priv->call_depth && two_way && !clean) {\n \t\t\tresult->mode = o->mode;\n@@ -2123,7 +2123,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\tif (type_changed) {\n \t\t\t\t/* rename vs. typechange */\n \t\t\t\t/* Mark the original as resolved by removal */\n-\t\t\t\tmemcpy(&oldinfo->stages[0].oid, &null_oid,\n+\t\t\t\tmemcpy(&oldinfo->stages[0].oid, null_oid(),\n \t\t\t\t       sizeof(oldinfo->stages[0].oid));\n \t\t\t\toldinfo->stages[0].mode = 0;\n \t\t\t\toldinfo->filemask &= 0x06;\n@@ -2762,7 +2762,7 @@ static void process_entry(struct merge_options *opt,\n \t\t\tif (ci->filemask & (1 << i))\n \t\t\t\tcontinue;\n \t\t\tci->stages[i].mode = 0;\n-\t\t\toidcpy(&ci->stages[i].oid, &null_oid);\n+\t\t\toidcpy(&ci->stages[i].oid, null_oid());\n \t\t}\n \t} else if (ci->df_conflict && ci->merged.result.mode != 0) {\n \t\t/*\n@@ -2808,7 +2808,7 @@ static void process_entry(struct merge_options *opt,\n \t\t\t\tcontinue;\n \t\t\t/* zero out any entries related to directories */\n \t\t\tnew_ci->stages[i].mode = 0;\n-\t\t\toidcpy(&new_ci->stages[i].oid, &null_oid);\n+\t\t\toidcpy(&new_ci->stages[i].oid, null_oid());\n \t\t}\n \n \t\t/*\n@@ -2909,11 +2909,11 @@ static void process_entry(struct merge_options *opt,\n \t\t\tnew_ci->merged.result.mode = ci->stages[2].mode;\n \t\t\toidcpy(&new_ci->merged.result.oid, &ci->stages[2].oid);\n \t\t\tnew_ci->stages[1].mode = 0;\n-\t\t\toidcpy(&new_ci->stages[1].oid, &null_oid);\n+\t\t\toidcpy(&new_ci->stages[1].oid, null_oid());\n \t\t\tnew_ci->filemask = 5;\n \t\t\tif ((S_IFMT & b_mode) != (S_IFMT & o_mode)) {\n \t\t\t\tnew_ci->stages[0].mode = 0;\n-\t\t\t\toidcpy(&new_ci->stages[0].oid, &null_oid);\n+\t\t\t\toidcpy(&new_ci->stages[0].oid, null_oid());\n \t\t\t\tnew_ci->filemask = 4;\n \t\t\t}\n \n@@ -2921,11 +2921,11 @@ static void process_entry(struct merge_options *opt,\n \t\t\tci->merged.result.mode = ci->stages[1].mode;\n \t\t\toidcpy(&ci->merged.result.oid, &ci->stages[1].oid);\n \t\t\tci->stages[2].mode = 0;\n-\t\t\toidcpy(&ci->stages[2].oid, &null_oid);\n+\t\t\toidcpy(&ci->stages[2].oid, null_oid());\n \t\t\tci->filemask = 3;\n \t\t\tif ((S_IFMT & a_mode) != (S_IFMT & o_mode)) {\n \t\t\t\tci->stages[0].mode = 0;\n-\t\t\t\toidcpy(&ci->stages[0].oid, &null_oid);\n+\t\t\t\toidcpy(&ci->stages[0].oid, null_oid());\n \t\t\t\tci->filemask = 2;\n \t\t\t}\n \n@@ -3042,7 +3042,7 @@ static void process_entry(struct merge_options *opt,\n \t\t/* Deleted on both sides */\n \t\tci->merged.is_null = 1;\n \t\tci->merged.result.mode = 0;\n-\t\toidcpy(&ci->merged.result.oid, &null_oid);\n+\t\toidcpy(&ci->merged.result.oid, null_oid());\n \t\tci->merged.clean = !ci->path_conflict;\n \t}\n \ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex ed31f9496c..03f5c0769e 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -486,7 +486,7 @@ static int get_tree_entry_if_blob(struct repository *r,\n \n \tret = get_tree_entry(r, tree, path, &dfs->oid, &dfs->mode);\n \tif (S_ISDIR(dfs->mode)) {\n-\t\toidcpy(&dfs->oid, &null_oid);\n+\t\toidcpy(&dfs->oid, null_oid());\n \t\tdfs->mode = 0;\n \t}\n \treturn ret;\n@@ -1604,7 +1604,7 @@ static int handle_file_collision(struct merge_options *opt,\n \n \t/* Store things in diff_filespecs for functions that need it */\n \tnull.path = (char *)collide_path;\n-\toidcpy(&null.oid, &null_oid);\n+\toidcpy(&null.oid, null_oid());\n \tnull.mode = 0;\n \n \tif (merge_mode_and_contents(opt, &null, a, b, collide_path,\n@@ -2789,11 +2789,11 @@ static int process_renames(struct merge_options *opt,\n \t\t\tdst_other.mode = ren1->dst_entry->stages[other_stage].mode;\n \t\t\ttry_merge = 0;\n \n-\t\t\tif (oideq(&src_other.oid, &null_oid) &&\n+\t\t\tif (oideq(&src_other.oid, null_oid()) &&\n \t\t\t    ren1->dir_rename_original_type == 'A') {\n \t\t\t\tsetup_rename_conflict_info(RENAME_VIA_DIR,\n \t\t\t\t\t\t\t   opt, ren1, NULL);\n-\t\t\t} else if (oideq(&src_other.oid, &null_oid)) {\n+\t\t\t} else if (oideq(&src_other.oid, null_oid())) {\n \t\t\t\tsetup_rename_conflict_info(RENAME_DELETE,\n \t\t\t\t\t\t\t   opt, ren1, NULL);\n \t\t\t} else if ((dst_other.mode == ren1->pair->two->mode) &&\n@@ -2812,7 +2812,7 @@ static int process_renames(struct merge_options *opt,\n \t\t\t\t\t\t      1, /* update_cache */\n \t\t\t\t\t\t      0  /* update_wd    */))\n \t\t\t\t\tclean_merge = -1;\n-\t\t\t} else if (!oideq(&dst_other.oid, &null_oid)) {\n+\t\t\t} else if (!oideq(&dst_other.oid, null_oid())) {\n \t\t\t\t/*\n \t\t\t\t * Probably not a clean merge, but it's\n \t\t\t\t * premature to set clean_merge to 0 here,\ndiff --git a/notes-merge.c b/notes-merge.c\nindex d2771fa3d4..53c587f750 100644\n--- a/notes-merge.c\n+++ b/notes-merge.c\n@@ -600,7 +600,7 @@ int notes_merge(struct notes_merge_options *o,\n \t/* Find merge bases */\n \tbases = get_merge_bases(local, remote);\n \tif (!bases) {\n-\t\tbase_oid = &null_oid;\n+\t\tbase_oid = null_oid();\n \t\tbase_tree_oid = the_hash_algo->empty_tree;\n \t\tif (o->verbosity >= 4)\n \t\t\tprintf(\"No merge base found; doing history-less merge\\n\");\ndiff --git a/notes.c b/notes.c\nindex 1dfe9e2b9f..3f48a2aac3 100644\n--- a/notes.c\n+++ b/notes.c\n@@ -1327,7 +1327,7 @@ int copy_note(struct notes_tree *t,\n \tif (note)\n \t\treturn add_note(t, to_obj, note, combine_notes);\n \telse if (existing_note)\n-\t\treturn add_note(t, to_obj, &null_oid, combine_notes);\n+\t\treturn add_note(t, to_obj, null_oid(), combine_notes);\n \n \treturn 0;\n }\ndiff --git a/object-file.c b/object-file.c\nindex 5f1fa05c4e..50bb5b6ca4 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -55,7 +55,6 @@\n \t\"\\x6f\\xe1\\x41\\xf7\\x74\\x91\\x20\\xa3\\x03\\x72\" \\\n \t\"\\x18\\x13\"\n \n-const struct object_id null_oid;\n static const struct object_id empty_tree_oid = {\n \tEMPTY_TREE_SHA1_BIN_LITERAL,\n \tGIT_HASH_SHA1,\n@@ -64,6 +63,9 @@ static const struct object_id empty_blob_oid = {\n \tEMPTY_BLOB_SHA1_BIN_LITERAL,\n \tGIT_HASH_SHA1,\n };\n+const struct object_id null_oid_sha1 = {\n+\t{0}, GIT_HASH_SHA1,\n+};\n static const struct object_id empty_tree_oid_sha256 = {\n \tEMPTY_TREE_SHA256_BIN_LITERAL,\n \tGIT_HASH_SHA256,\n@@ -72,6 +74,9 @@ static const struct object_id empty_blob_oid_sha256 = {\n \tEMPTY_BLOB_SHA256_BIN_LITERAL,\n \tGIT_HASH_SHA256,\n };\n+static const struct object_id null_oid_sha256 = {\n+\t{0}, GIT_HASH_SHA256,\n+};\n \n static void git_hash_sha1_init(git_hash_ctx *ctx)\n {\n@@ -172,6 +177,7 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {\n \t\tgit_hash_unknown_final_oid,\n \t\tNULL,\n \t\tNULL,\n+\t\tNULL,\n \t},\n \t{\n \t\t\"sha1\",\n@@ -187,6 +193,7 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {\n \t\tgit_hash_sha1_final_oid,\n \t\t&empty_tree_oid,\n \t\t&empty_blob_oid,\n+\t\t&null_oid_sha1,\n \t},\n \t{\n \t\t\"sha256\",\n@@ -202,9 +209,15 @@ const struct git_hash_algo hash_algos[GIT_HASH_NALGOS] = {\n \t\tgit_hash_sha256_final_oid,\n \t\t&empty_tree_oid_sha256,\n \t\t&empty_blob_oid_sha256,\n+\t\t&null_oid_sha256,\n \t}\n };\n \n+const struct object_id *null_oid(void)\n+{\n+\treturn the_hash_algo->null_oid;\n+}\n+\n const char *empty_tree_oid_hex(void)\n {\n \tstatic char buf[GIT_MAX_HEXSZ + 1];\ndiff --git a/parse-options-cb.c b/parse-options-cb.c\nindex 4542d4d3f9..3c811e1e4a 100644\n--- a/parse-options-cb.c\n+++ b/parse-options-cb.c\n@@ -140,7 +140,7 @@ int parse_opt_object_id(const struct option *opt, const char *arg, int unset)\n \tstruct object_id *target = opt->value;\n \n \tif (unset) {\n-\t\t*target = null_oid;\n+\t\toidcpy(target, null_oid());\n \t\treturn 0;\n \t}\n \tif (!arg)\ndiff --git a/range-diff.c b/range-diff.c\nindex 116fb0735c..1a4471fe4c 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -445,7 +445,7 @@ static struct diff_filespec *get_filespec(const char *name, const char *p)\n {\n \tstruct diff_filespec *spec = alloc_filespec(name);\n \n-\tfill_filespec(spec, &null_oid, 0, 0100644);\n+\tfill_filespec(spec, null_oid(), 0, 0100644);\n \tspec->data = (char *)p;\n \tspec->size = strlen(p);\n \tspec->should_munmap = 0;\ndiff --git a/refs.c b/refs.c\nindex 261fd82beb..996063fdf4 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -1107,7 +1107,7 @@ int ref_transaction_create(struct ref_transaction *transaction,\n \tif (!new_oid || is_null_oid(new_oid))\n \t\tBUG(\"create called without valid new_oid\");\n \treturn ref_transaction_update(transaction, refname, new_oid,\n-\t\t\t\t      &null_oid, flags, msg, err);\n+\t\t\t\t      null_oid(), flags, msg, err);\n }\n \n int ref_transaction_delete(struct ref_transaction *transaction,\n@@ -1119,7 +1119,7 @@ int ref_transaction_delete(struct ref_transaction *transaction,\n \tif (old_oid && is_null_oid(old_oid))\n \t\tBUG(\"delete called with old_oid set to zeros\");\n \treturn ref_transaction_update(transaction, refname,\n-\t\t\t\t      &null_oid, old_oid,\n+\t\t\t\t      null_oid(), old_oid,\n \t\t\t\t      flags, msg, err);\n }\n \ndiff --git a/refs/debug.c b/refs/debug.c\nindex 922e64fa6a..2665f94309 100644\n--- a/refs/debug.c\n+++ b/refs/debug.c\n@@ -243,7 +243,7 @@ static int debug_read_raw_ref(struct ref_store *ref_store, const char *refname,\n \tstruct debug_ref_store *drefs = (struct debug_ref_store *)ref_store;\n \tint res = 0;\n \n-\toidcpy(oid, &null_oid);\n+\toidcpy(oid, null_oid());\n \tres = drefs->refs->be->read_raw_ref(drefs->refs, refname, oid, referent,\n \t\t\t\t\t    type);\n \ndiff --git a/refs/files-backend.c b/refs/files-backend.c\nindex 119972ee16..3f29f8c143 100644\n--- a/refs/files-backend.c\n+++ b/refs/files-backend.c\n@@ -1084,7 +1084,7 @@ static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)\n \tref_transaction_add_update(\n \t\t\ttransaction, r->name,\n \t\t\tREF_NO_DEREF | REF_HAVE_NEW | REF_HAVE_OLD | REF_IS_PRUNING,\n-\t\t\t&null_oid, &r->oid, NULL);\n+\t\t\tnull_oid(), &r->oid, NULL);\n \tif (ref_transaction_commit(transaction, &err))\n \t\tgoto cleanup;\n \ndiff --git a/reset.c b/reset.c\nindex 2f4fbd07c5..4bea758053 100644\n--- a/reset.c\n+++ b/reset.c\n@@ -128,7 +128,7 @@ int reset_head(struct repository *r, struct object_id *oid, const char *action,\n \t}\n \tif (run_hook)\n \t\trun_hook_le(NULL, \"post-checkout\",\n-\t\t\t    oid_to_hex(orig ? orig : &null_oid),\n+\t\t\t    oid_to_hex(orig ? orig : null_oid()),\n \t\t\t    oid_to_hex(oid), \"1\", NULL);\n \n leave_reset_head:\ndiff --git a/sequencer.c b/sequencer.c\nindex fd183b5593..dbee779243 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -524,7 +524,7 @@ static int fast_forward_to(struct repository *r,\n \tif (!transaction ||\n \t    ref_transaction_update(transaction, \"HEAD\",\n \t\t\t\t   to, unborn && !is_rebase_i(opts) ?\n-\t\t\t\t   &null_oid : from,\n+\t\t\t\t   null_oid() : from,\n \t\t\t\t   0, sb.buf, &err) ||\n \t    ref_transaction_commit(transaction, &err)) {\n \t\tref_transaction_free(transaction);\n@@ -1131,7 +1131,7 @@ int update_head_with_reflog(const struct commit *old_head,\n \ttransaction = ref_transaction_begin(err);\n \tif (!transaction ||\n \t    ref_transaction_update(transaction, \"HEAD\", new_head,\n-\t\t\t\t   old_head ? &old_head->object.oid : &null_oid,\n+\t\t\t\t   old_head ? &old_head->object.oid : null_oid(),\n \t\t\t\t   0, sb.buf, err) ||\n \t    ref_transaction_commit(transaction, err)) {\n \t\tret = -1;\ndiff --git a/submodule-config.c b/submodule-config.c\nindex f502505566..2026120fb3 100644\n--- a/submodule-config.c\n+++ b/submodule-config.c\n@@ -671,7 +671,7 @@ static int gitmodules_cb(const char *var, const char *value, void *data)\n \n \tparameter.cache = repo->submodule_cache;\n \tparameter.treeish_name = NULL;\n-\tparameter.gitmodules_oid = &null_oid;\n+\tparameter.gitmodules_oid = null_oid();\n \tparameter.overwrite = 1;\n \n \treturn parse_config(var, value, &parameter);\ndiff --git a/submodule.c b/submodule.c\nindex 9767ba9893..7eeaf7d203 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -113,7 +113,7 @@ int update_path_in_gitmodules(const char *oldpath, const char *newpath)\n \tif (is_gitmodules_unmerged(the_repository->index))\n \t\tdie(_(\"Cannot change unmerged .gitmodules, resolve merge conflicts first\"));\n \n-\tsubmodule = submodule_from_path(the_repository, &null_oid, oldpath);\n+\tsubmodule = submodule_from_path(the_repository, null_oid(), oldpath);\n \tif (!submodule || !submodule->name) {\n \t\twarning(_(\"Could not find section in .gitmodules where path=%s\"), oldpath);\n \t\treturn -1;\n@@ -142,7 +142,7 @@ int remove_path_from_gitmodules(const char *path)\n \tif (is_gitmodules_unmerged(the_repository->index))\n \t\tdie(_(\"Cannot change unmerged .gitmodules, resolve merge conflicts first\"));\n \n-\tsubmodule = submodule_from_path(the_repository, &null_oid, path);\n+\tsubmodule = submodule_from_path(the_repository, null_oid(), path);\n \tif (!submodule || !submodule->name) {\n \t\twarning(_(\"Could not find section in .gitmodules where path=%s\"), path);\n \t\treturn -1;\n@@ -188,7 +188,8 @@ void set_diffopt_flags_from_submodule_config(struct diff_options *diffopt,\n \t\t\t\t\t     const char *path)\n {\n \tconst struct submodule *submodule = submodule_from_path(the_repository,\n-\t\t\t\t\t\t\t\t&null_oid, path);\n+\t\t\t\t\t\t\t\tnull_oid(),\n+\t\t\t\t\t\t\t\tpath);\n \tif (submodule) {\n \t\tconst char *ignore;\n \t\tchar *key;\n@@ -244,7 +245,7 @@ int is_submodule_active(struct repository *repo, const char *path)\n \tconst struct string_list *sl;\n \tconst struct submodule *module;\n \n-\tmodule = submodule_from_path(repo, &null_oid, path);\n+\tmodule = submodule_from_path(repo, null_oid(), path);\n \n \t/* early return if there isn't a path->module mapping */\n \tif (!module)\n@@ -745,7 +746,7 @@ const struct submodule *submodule_from_ce(const struct cache_entry *ce)\n \tif (!should_update_submodules())\n \t\treturn NULL;\n \n-\treturn submodule_from_path(the_repository, &null_oid, ce->name);\n+\treturn submodule_from_path(the_repository, null_oid(), ce->name);\n }\n \n static struct oid_array *submodule_commits(struct string_list *submodules,\n@@ -1037,7 +1038,7 @@ int find_unpushed_submodules(struct repository *r,\n \t\tconst struct submodule *submodule;\n \t\tconst char *path = NULL;\n \n-\t\tsubmodule = submodule_from_name(r, &null_oid, name->string);\n+\t\tsubmodule = submodule_from_name(r, null_oid(), name->string);\n \t\tif (submodule)\n \t\t\tpath = submodule->path;\n \t\telse\n@@ -1224,7 +1225,7 @@ static void calculate_changed_submodule_paths(struct repository *r,\n \t\tconst struct submodule *submodule;\n \t\tconst char *path = NULL;\n \n-\t\tsubmodule = submodule_from_name(r, &null_oid, name->string);\n+\t\tsubmodule = submodule_from_name(r, null_oid(), name->string);\n \t\tif (submodule)\n \t\t\tpath = submodule->path;\n \t\telse\n@@ -1361,7 +1362,7 @@ static struct fetch_task *fetch_task_create(struct repository *r,\n \tstruct fetch_task *task = xmalloc(sizeof(*task));\n \tmemset(task, 0, sizeof(*task));\n \n-\ttask->sub = submodule_from_path(r, &null_oid, path);\n+\ttask->sub = submodule_from_path(r, null_oid(), path);\n \tif (!task->sub) {\n \t\t/*\n \t\t * No entry in .gitmodules? Technically not a submodule,\n@@ -1917,7 +1918,7 @@ int submodule_move_head(const char *path,\n \tif (old_head && !is_submodule_populated_gently(path, error_code_ptr))\n \t\treturn 0;\n \n-\tsub = submodule_from_path(the_repository, &null_oid, path);\n+\tsub = submodule_from_path(the_repository, null_oid(), path);\n \n \tif (!sub)\n \t\tBUG(\"could not get submodule information for '%s'\", path);\n@@ -2076,7 +2077,7 @@ static void relocate_single_git_dir_into_superproject(const char *path)\n \n \treal_old_git_dir = real_pathdup(old_git_dir, 1);\n \n-\tsub = submodule_from_path(the_repository, &null_oid, path);\n+\tsub = submodule_from_path(the_repository, null_oid(), path);\n \tif (!sub)\n \t\tdie(_(\"could not lookup name for submodule '%s'\"), path);\n \n@@ -2135,7 +2136,7 @@ void absorb_git_dir_into_superproject(const char *path,\n \t\t* superproject did not rewrite the git file links yet,\n \t\t* fix it now.\n \t\t*/\n-\t\tsub = submodule_from_path(the_repository, &null_oid, path);\n+\t\tsub = submodule_from_path(the_repository, null_oid(), path);\n \t\tif (!sub)\n \t\t\tdie(_(\"could not lookup name for submodule '%s'\"), path);\n \t\tconnect_work_tree_and_git_dir(path,\n@@ -2283,7 +2284,8 @@ int submodule_to_gitdir(struct strbuf *buf, const char *submodule)\n \t\tstrbuf_addstr(buf, git_dir);\n \t}\n \tif (!is_git_directory(buf->buf)) {\n-\t\tsub = submodule_from_path(the_repository, &null_oid, submodule);\n+\t\tsub = submodule_from_path(the_repository, null_oid(),\n+\t\t\t\t\t  submodule);\n \t\tif (!sub) {\n \t\t\tret = -1;\n \t\t\tgoto cleanup;\ndiff --git a/t/helper/test-submodule-nested-repo-config.c b/t/helper/test-submodule-nested-repo-config.c\nindex c5fd4527dc..e3f11ff5a7 100644\n--- a/t/helper/test-submodule-nested-repo-config.c\n+++ b/t/helper/test-submodule-nested-repo-config.c\n@@ -18,7 +18,7 @@ int cmd__submodule_nested_repo_config(int argc, const char **argv)\n \n \tsetup_git_directory();\n \n-\tsub = submodule_from_path(the_repository, &null_oid, argv[1]);\n+\tsub = submodule_from_path(the_repository, null_oid(), argv[1]);\n \tif (repo_submodule_init(&subrepo, the_repository, sub)) {\n \t\tdie_usage(argv, \"Submodule not found.\");\n \t}\ndiff --git a/tree-diff.c b/tree-diff.c\nindex 7cebbb327e..1572615bd9 100644\n--- a/tree-diff.c\n+++ b/tree-diff.c\n@@ -161,7 +161,7 @@ static struct combine_diff_path *path_appendnew(struct combine_diff_path *last,\n \tmemcpy(p->path + base->len, path, pathlen);\n \tp->path[len] = 0;\n \tp->mode = mode;\n-\toidcpy(&p->oid, oid ? oid : &null_oid);\n+\toidcpy(&p->oid, oid ? oid : null_oid());\n \n \treturn p;\n }\n@@ -243,7 +243,7 @@ static struct combine_diff_path *emit_path(struct combine_diff_path *p,\n \t\t\t\tmode_i = tp[i].entry.mode;\n \t\t\t}\n \t\t\telse {\n-\t\t\t\toid_i = &null_oid;\n+\t\t\t\toid_i = null_oid();\n \t\t\t\tmode_i = 0;\n \t\t\t}\n \ndiff --git a/wt-status.c b/wt-status.c\nindex 1aed68c43c..7603c1b198 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1687,10 +1687,10 @@ void wt_status_get_state(struct repository *r,\n \tif (!sequencer_get_last_command(r, &action)) {\n \t\tif (action == REPLAY_PICK) {\n \t\t\tstate->cherry_pick_in_progress = 1;\n-\t\t\toidcpy(&state->cherry_pick_head_oid, &null_oid);\n+\t\t\toidcpy(&state->cherry_pick_head_oid, null_oid());\n \t\t} else {\n \t\t\tstate->revert_in_progress = 1;\n-\t\t\toidcpy(&state->revert_head_oid, &null_oid);\n+\t\t\toidcpy(&state->revert_head_oid, null_oid());\n \t\t}\n \t}\n \tif (get_detached_from)\ndiff --git a/xdiff-interface.c b/xdiff-interface.c\nindex 4d20069302..609615db2c 100644\n--- a/xdiff-interface.c\n+++ b/xdiff-interface.c\n@@ -172,7 +172,7 @@ void read_mmblob(mmfile_t *ptr, const struct object_id *oid)\n \tunsigned long size;\n \tenum object_type type;\n \n-\tif (oideq(oid, &null_oid)) {\n+\tif (oideq(oid, null_oid())) {\n \t\tptr->ptr = xstrdup(\"\");\n \t\tptr->size = 0;\n \t\treturn;\n"},{"id":"421541","messageId":"20210410152140.3525040-10-sandals@crustytoothpaste.net","threadId":"55472","inReplyTo":"20210410152140.3525040-1-sandals@crustytoothpaste.net","subject":"[PATCH 09/15] hash: set and copy algo field in struct object_id","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-10T15:21:34Z","receivedAt":"2021-04-10T15:22:41Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Now that struct object_id has an algorithm field, we should populate it.\nThis will allow us to handle object IDs in any supported algorithm and\ndistinguish between them.  Ensure that the field is written whenever we\nwrite an object ID by storing it explicitly every time we write an\nobject.  Set values for the empty blob and tree values as well.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n hash.h        |  4 ++++\n object-file.c | 14 ++++++++++----\n 2 files changed, 14 insertions(+), 4 deletions(-)\n\ndiff --git a/hash.h b/hash.h\nindex 04eba5c56b..3b114f053e 100644\n--- a/hash.h\n+++ b/hash.h\n@@ -237,6 +237,7 @@ static inline void hashcpy(unsigned char *sha_dst, const unsigned char *sha_src)\n static inline void oidcpy(struct object_id *dst, const struct object_id *src)\n {\n \tmemcpy(dst->hash, src->hash, GIT_MAX_RAWSZ);\n+\tdst->algo = src->algo;\n }\n \n static inline struct object_id *oiddup(const struct object_id *src)\n@@ -254,6 +255,7 @@ static inline void hashclr(unsigned char *hash)\n static inline void oidclr(struct object_id *oid)\n {\n \tmemset(oid->hash, 0, GIT_MAX_RAWSZ);\n+\toid->algo = hash_algo_by_ptr(the_hash_algo);\n }\n \n static inline void oidread(struct object_id *oid, const unsigned char *hash)\n@@ -261,6 +263,7 @@ static inline void oidread(struct object_id *oid, const unsigned char *hash)\n \tsize_t rawsz = the_hash_algo->rawsz;\n \tmemcpy(oid->hash, hash, rawsz);\n \tmemset(oid->hash + rawsz, 0, GIT_MAX_RAWSZ - rawsz);\n+\toid->algo = hash_algo_by_ptr(the_hash_algo);\n }\n \n static inline int is_empty_blob_sha1(const unsigned char *sha1)\n@@ -286,6 +289,7 @@ static inline int is_empty_tree_oid(const struct object_id *oid)\n static inline void oid_pad_buffer(struct object_id *oid, const struct git_hash_algo *algop)\n {\n \tmemset(oid->hash + algop->rawsz, 0, GIT_MAX_RAWSZ - algop->rawsz);\n+\toid->algo = hash_algo_by_ptr(algop);\n }\n \n const char *empty_tree_oid_hex(void);\ndiff --git a/object-file.c b/object-file.c\nindex 8e338247cc..5f1fa05c4e 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -57,16 +57,20 @@\n \n const struct object_id null_oid;\n static const struct object_id empty_tree_oid = {\n-\tEMPTY_TREE_SHA1_BIN_LITERAL\n+\tEMPTY_TREE_SHA1_BIN_LITERAL,\n+\tGIT_HASH_SHA1,\n };\n static const struct object_id empty_blob_oid = {\n-\tEMPTY_BLOB_SHA1_BIN_LITERAL\n+\tEMPTY_BLOB_SHA1_BIN_LITERAL,\n+\tGIT_HASH_SHA1,\n };\n static const struct object_id empty_tree_oid_sha256 = {\n-\tEMPTY_TREE_SHA256_BIN_LITERAL\n+\tEMPTY_TREE_SHA256_BIN_LITERAL,\n+\tGIT_HASH_SHA256,\n };\n static const struct object_id empty_blob_oid_sha256 = {\n-\tEMPTY_BLOB_SHA256_BIN_LITERAL\n+\tEMPTY_BLOB_SHA256_BIN_LITERAL,\n+\tGIT_HASH_SHA256,\n };\n \n static void git_hash_sha1_init(git_hash_ctx *ctx)\n@@ -93,6 +97,7 @@ static void git_hash_sha1_final_oid(struct object_id *oid, git_hash_ctx *ctx)\n {\n \tgit_SHA1_Final(oid->hash, &ctx->sha1);\n \tmemset(oid->hash + GIT_SHA1_RAWSZ, 0, GIT_MAX_RAWSZ - GIT_SHA1_RAWSZ);\n+\toid->algo = GIT_HASH_SHA1;\n }\n \n \n@@ -124,6 +129,7 @@ static void git_hash_sha256_final_oid(struct object_id *oid, git_hash_ctx *ctx)\n \t * but keep it in case we extend the hash size again.\n \t */\n \tmemset(oid->hash + GIT_SHA256_RAWSZ, 0, GIT_MAX_RAWSZ - GIT_SHA256_RAWSZ);\n+\toid->algo = GIT_HASH_SHA256;\n }\n \n static void git_hash_unknown_init(git_hash_ctx *ctx)\n"},{"id":"421543","messageId":"20210410152140.3525040-12-sandals@crustytoothpaste.net","threadId":"55472","inReplyTo":"20210410152140.3525040-1-sandals@crustytoothpaste.net","subject":"[PATCH 11/15] builtin/show-index: set the algorithm for object IDs","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-10T15:21:36Z","receivedAt":"2021-04-10T15:22:41Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"In most cases, when we load the hash of an object into a struct\nobject_id, we load it using one of the oid* or *_oid_hex functions.\nHowever, for git show-index, we read it in directly using fread.  As a\nconsequence, set the algorithm correctly so the objects can be used\ncorrectly both now and in the future.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n builtin/show-index.c | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/show-index.c b/builtin/show-index.c\nindex 8106b03a6b..0e0b9fb95b 100644\n--- a/builtin/show-index.c\n+++ b/builtin/show-index.c\n@@ -71,9 +71,11 @@ int cmd_show_index(int argc, const char **argv, const char *prefix)\n \t\t\tuint32_t off;\n \t\t} *entries;\n \t\tALLOC_ARRAY(entries, nr);\n-\t\tfor (i = 0; i < nr; i++)\n+\t\tfor (i = 0; i < nr; i++) {\n \t\t\tif (fread(entries[i].oid.hash, hashsz, 1, stdin) != 1)\n \t\t\t\tdie(\"unable to read sha1 %u/%u\", i, nr);\n+\t\t\tentries[i].oid.algo = hash_algo_by_ptr(the_hash_algo);\n+\t\t}\n \t\tfor (i = 0; i < nr; i++)\n \t\t\tif (fread(&entries[i].crc, 4, 1, stdin) != 1)\n \t\t\t\tdie(\"unable to read crc %u/%u\", i, nr);\n"},{"id":"421544","messageId":"20210410152140.3525040-13-sandals@crustytoothpaste.net","threadId":"55472","inReplyTo":"20210410152140.3525040-1-sandals@crustytoothpaste.net","subject":"[PATCH 12/15] commit-graph: don't store file hashes as struct object_id","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-10T15:21:37Z","receivedAt":"2021-04-10T15:22:42Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"The idea behind struct object_id is that it is supposed to represent the\nidentifier of a standard Git object or a special pseudo-object like the\nall-zeros object ID.  In this case, we have file hashes, which, while\nsimilar, are distinct from the identifiers of objects.\n\nSwitch these code paths to use an unsigned char array.  This is both\nmore logically consistent and it means that we need not set the\nalgorithm identifier for the struct object_id.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n commit-graph.c | 13 +++++++------\n 1 file changed, 7 insertions(+), 6 deletions(-)\n\ndiff --git a/commit-graph.c b/commit-graph.c\nindex 23fef56d31..2bcb4e0f89 100644\n--- a/commit-graph.c\n+++ b/commit-graph.c\n@@ -1793,8 +1793,8 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)\n \tstruct lock_file lk = LOCK_INIT;\n \tconst unsigned hashsz = the_hash_algo->rawsz;\n \tstruct strbuf progress_title = STRBUF_INIT;\n-\tstruct object_id file_hash;\n \tstruct chunkfile *cf;\n+\tunsigned char file_hash[GIT_MAX_RAWSZ];\n \n \tif (ctx->split) {\n \t\tstruct strbuf tmp_file = STRBUF_INIT;\n@@ -1909,7 +1909,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)\n \t}\n \n \tclose_commit_graph(ctx->r->objects);\n-\tfinalize_hashfile(f, file_hash.hash, CSUM_HASH_IN_STREAM | CSUM_FSYNC);\n+\tfinalize_hashfile(f, file_hash, CSUM_HASH_IN_STREAM | CSUM_FSYNC);\n \tfree_chunkfile(cf);\n \n \tif (ctx->split) {\n@@ -1945,7 +1945,7 @@ static int write_commit_graph_file(struct write_commit_graph_context *ctx)\n \t\t\tunlink(graph_name);\n \t\t}\n \n-\t\tctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 1] = xstrdup(oid_to_hex(&file_hash));\n+\t\tctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 1] = xstrdup(hash_to_hex(file_hash));\n \t\tfinal_graph_name = get_split_graph_filename(ctx->odb,\n \t\t\t\t\tctx->commit_graph_hash_after[ctx->num_commit_graphs_after - 1]);\n \t\tctx->commit_graph_filenames_after[ctx->num_commit_graphs_after - 1] = final_graph_name;\n@@ -2425,7 +2425,8 @@ static void graph_report(const char *fmt, ...)\n int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)\n {\n \tuint32_t i, cur_fanout_pos = 0;\n-\tstruct object_id prev_oid, cur_oid, checksum;\n+\tstruct object_id prev_oid, cur_oid;\n+\tunsigned char checksum[GIT_MAX_HEXSZ];\n \tint generation_zero = 0;\n \tstruct hashfile *f;\n \tint devnull;\n@@ -2444,8 +2445,8 @@ int verify_commit_graph(struct repository *r, struct commit_graph *g, int flags)\n \tdevnull = open(\"/dev/null\", O_WRONLY);\n \tf = hashfd(devnull, NULL);\n \thashwrite(f, g->data, g->data_len - g->hash_len);\n-\tfinalize_hashfile(f, checksum.hash, CSUM_CLOSE);\n-\tif (!hasheq(checksum.hash, g->data + g->data_len - g->hash_len)) {\n+\tfinalize_hashfile(f, checksum, CSUM_CLOSE);\n+\tif (!hasheq(checksum, g->data + g->data_len - g->hash_len)) {\n \t\tgraph_report(_(\"the commit-graph file has incorrect checksum and is likely corrupt\"));\n \t\tverify_commit_graph_error = VERIFY_COMMIT_GRAPH_ERROR_HASH;\n \t}\n"},{"id":"421545","messageId":"20210410152140.3525040-16-sandals@crustytoothpaste.net","threadId":"55472","inReplyTo":"20210410152140.3525040-1-sandals@crustytoothpaste.net","subject":"[PATCH 15/15] hex: print objects using the hash algorithm member","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-10T15:21:40Z","receivedAt":"2021-04-10T15:22:44Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Now that all code paths correctly set the hash algorithm member of\nstruct object_id, write an object's hex representation using the hash\nalgorithm member embedded in it.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n hex.c | 4 ++--\n 1 file changed, 2 insertions(+), 2 deletions(-)\n\ndiff --git a/hex.c b/hex.c\nindex 43597b2dbb..4537e79d3c 100644\n--- a/hex.c\n+++ b/hex.c\n@@ -143,7 +143,7 @@ char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash,\n \n char *oid_to_hex_r(char *buffer, const struct object_id *oid)\n {\n-\treturn hash_to_hex_algop_r(buffer, oid->hash, the_hash_algo);\n+\treturn hash_to_hex_algop_r(buffer, oid->hash, &hash_algos[oid->algo]);\n }\n \n char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *algop)\n@@ -161,5 +161,5 @@ char *hash_to_hex(const unsigned char *hash)\n \n char *oid_to_hex(const struct object_id *oid)\n {\n-\treturn hash_to_hex_algop(oid->hash, the_hash_algo);\n+\treturn hash_to_hex_algop(oid->hash, &hash_algos[oid->algo]);\n }\n"},{"id":"421546","messageId":"20210410152140.3525040-3-sandals@crustytoothpaste.net","threadId":"55472","inReplyTo":"20210410152140.3525040-1-sandals@crustytoothpaste.net","subject":"[PATCH 02/15] builtin/hash-object: allow literally hashing with a given algorithm","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-10T15:21:27Z","receivedAt":"2021-04-10T15:23:04Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Add an --object-format argument to git hash-object that allows hashing\nan object with a given algorithm. Currently this options is limited to\nuse with --literally, since the index_* functions do not yet handle\nmultiple hash algorithms.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n builtin/hash-object.c  | 47 ++++++++++++++++++++++++++++++------------\n t/t1007-hash-object.sh | 10 +++++++++\n 2 files changed, 44 insertions(+), 13 deletions(-)\n\ndiff --git a/builtin/hash-object.c b/builtin/hash-object.c\nindex 640ef4ded5..0203cfbe9a 100644\n--- a/builtin/hash-object.c\n+++ b/builtin/hash-object.c\n@@ -17,7 +17,8 @@\n  * needs to bypass the data conversion performed by, and the type\n  * limitation imposed by, index_fd() and its callees.\n  */\n-static int hash_literally(struct object_id *oid, int fd, const char *type, unsigned flags)\n+static int hash_literally(struct object_id *oid, int fd, const char *type,\n+\t\t\t  unsigned flags, const struct git_hash_algo *algo)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n \tint ret;\n@@ -25,42 +26,46 @@ static int hash_literally(struct object_id *oid, int fd, const char *type, unsig\n \tif (strbuf_read(&buf, fd, 4096) < 0)\n \t\tret = -1;\n \telse\n-\t\tret = hash_object_file_literally(buf.buf, buf.len, type, oid,\n-\t\t\t\t\t\t flags);\n+\t\tret = hash_object_file_literally_algop(buf.buf, buf.len, type, oid,\n+\t\t\t\t\t\t       flags, algo);\n \tstrbuf_release(&buf);\n \treturn ret;\n }\n \n static void hash_fd(int fd, const char *type, const char *path, unsigned flags,\n-\t\t    int literally)\n+\t\t    int literally, const struct git_hash_algo *algo)\n {\n \tstruct stat st;\n \tstruct object_id oid;\n \n+\tif (!literally && algo != the_hash_algo)\n+\t\tdie(_(\"Can't use hash algo %s except literally yet\"), algo->name);\n+\n \tif (fstat(fd, &st) < 0 ||\n \t    (literally\n-\t     ? hash_literally(&oid, fd, type, flags)\n+\t     ? hash_literally(&oid, fd, type, flags, algo)\n \t     : index_fd(the_repository->index, &oid, fd, &st,\n \t\t\ttype_from_string(type), path, flags)))\n \t\tdie((flags & HASH_WRITE_OBJECT)\n \t\t    ? \"Unable to add %s to database\"\n \t\t    : \"Unable to hash %s\", path);\n-\tprintf(\"%s\\n\", oid_to_hex(&oid));\n+\tprintf(\"%s\\n\", hash_to_hex_algop(oid.hash, algo));\n \tmaybe_flush_or_die(stdout, \"hash to stdout\");\n }\n \n static void hash_object(const char *path, const char *type, const char *vpath,\n-\t\t\tunsigned flags, int literally)\n+\t\t\tunsigned flags, int literally,\n+\t\t\tconst struct git_hash_algo *algo)\n {\n \tint fd;\n \tfd = open(path, O_RDONLY);\n \tif (fd < 0)\n \t\tdie_errno(\"Cannot open '%s'\", path);\n-\thash_fd(fd, type, vpath, flags, literally);\n+\thash_fd(fd, type, vpath, flags, literally, algo);\n }\n \n static void hash_stdin_paths(const char *type, int no_filters, unsigned flags,\n-\t\t\t     int literally)\n+\t\t\t     int literally, const struct git_hash_algo *algo)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct strbuf unquoted = STRBUF_INIT;\n@@ -73,7 +78,7 @@ static void hash_stdin_paths(const char *type, int no_filters, unsigned flags,\n \t\t\tstrbuf_swap(&buf, &unquoted);\n \t\t}\n \t\thash_object(buf.buf, type, no_filters ? NULL : buf.buf, flags,\n-\t\t\t    literally);\n+\t\t\t    literally, algo);\n \t}\n \tstrbuf_release(&buf);\n \tstrbuf_release(&unquoted);\n@@ -94,6 +99,8 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)\n \tint nongit = 0;\n \tunsigned flags = HASH_FORMAT_CHECK;\n \tconst char *vpath = NULL;\n+\tconst char *object_format = NULL;\n+\tconst struct git_hash_algo *algo;\n \tconst struct option hash_object_options[] = {\n \t\tOPT_STRING('t', NULL, &type, N_(\"type\"), N_(\"object type\")),\n \t\tOPT_BIT('w', NULL, &flags, N_(\"write the object into the object database\"),\n@@ -103,6 +110,7 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL( 0 , \"no-filters\", &no_filters, N_(\"store file as is without filters\")),\n \t\tOPT_BOOL( 0, \"literally\", &literally, N_(\"just hash any random garbage to create corrupt objects for debugging Git\")),\n \t\tOPT_STRING( 0 , \"path\", &vpath, N_(\"file\"), N_(\"process file as it were from this path\")),\n+\t\tOPT_STRING( 0 , \"object-format\", &object_format, N_(\"object-format\"), N_(\"Use this hash algorithm\")),\n \t\tOPT_END()\n \t};\n \tint i;\n@@ -121,6 +129,19 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)\n \n \tgit_config(git_default_config, NULL);\n \n+\talgo = the_hash_algo;\n+\tif (object_format) {\n+\t\tif (flags & HASH_WRITE_OBJECT)\n+\t\t\terrstr = \"Can't use -w with --object-format\";\n+\t\telse {\n+\t\t\tint id = hash_algo_by_name(object_format);\n+\t\t\tif (id == GIT_HASH_UNKNOWN)\n+\t\t\t\terrstr = \"Unknown object format\";\n+\t\t\telse\n+\t\t\t\talgo = &hash_algos[id];\n+\t\t}\n+\t}\n+\n \tif (stdin_paths) {\n \t\tif (hashstdin)\n \t\t\terrstr = \"Can't use --stdin-paths with --stdin\";\n@@ -142,7 +163,7 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)\n \t}\n \n \tif (hashstdin)\n-\t\thash_fd(0, type, vpath, flags, literally);\n+\t\thash_fd(0, type, vpath, flags, literally, algo);\n \n \tfor (i = 0 ; i < argc; i++) {\n \t\tconst char *arg = argv[i];\n@@ -151,12 +172,12 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)\n \t\tif (prefix)\n \t\t\targ = to_free = prefix_filename(prefix, arg);\n \t\thash_object(arg, type, no_filters ? NULL : vpath ? vpath : arg,\n-\t\t\t    flags, literally);\n+\t\t\t    flags, literally, algo);\n \t\tfree(to_free);\n \t}\n \n \tif (stdin_paths)\n-\t\thash_stdin_paths(type, no_filters, flags, literally);\n+\t\thash_stdin_paths(type, no_filters, flags, literally, algo);\n \n \treturn 0;\n }\ndiff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh\nindex 64b340f227..ea4b3d2bda 100755\n--- a/t/t1007-hash-object.sh\n+++ b/t/t1007-hash-object.sh\n@@ -83,6 +83,11 @@ test_expect_success 'hash a file' '\n \ttest \"$(test_oid hello)\" = $(git hash-object hello)\n '\n \n+test_expect_failure 'hash a file with a given algorithm' '\n+\ttest \"$(test_oid --hash=sha1 hello)\" = $(git hash-object --object-format=sha1 hello) &&\n+\ttest \"$(test_oid --hash=sha256 hello)\" = $(git hash-object --object-format=sha256 hello)\n+'\n+\n test_blob_does_not_exist \"$(test_oid hello)\"\n \n test_expect_success 'hash from stdin' '\n@@ -248,4 +253,9 @@ test_expect_success '--literally with extra-long type' '\n \techo example | git hash-object -t $t --literally --stdin\n '\n \n+test_expect_success '--literally with --object-format' '\n+\ttest $(test_oid --hash=sha1 hello) = $(git hash-object -t blob --literally --object-format=sha1 hello) &&\n+\ttest $(test_oid --hash=sha256 hello) = $(git hash-object -t blob --literally --object-format=sha256 hello)\n+'\n+\n test_done\n"},{"id":"421547","messageId":"20210410152140.3525040-4-sandals@crustytoothpaste.net","threadId":"55472","inReplyTo":"20210410152140.3525040-1-sandals@crustytoothpaste.net","subject":"[PATCH 03/15] cache: add an algo member to struct object_id","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-10T15:21:28Z","receivedAt":"2021-04-10T15:23:06Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"Now that we're working with multiple hash algorithms in the same repo,\nit's best if we label each object ID with its algorithm so we can\ndetermine how to format a given object ID. Add a member called algo to\nstruct object_id.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n hash.h | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/hash.h b/hash.h\nindex 3fb0c3d400..dafdcb3335 100644\n--- a/hash.h\n+++ b/hash.h\n@@ -181,6 +181,7 @@ static inline int hash_algo_by_ptr(const struct git_hash_algo *p)\n \n struct object_id {\n \tunsigned char hash[GIT_MAX_RAWSZ];\n+\tint algo;\n };\n \n #define the_hash_algo the_repository->hash_algo\n"},{"id":"421548","messageId":"20210410152140.3525040-7-sandals@crustytoothpaste.net","threadId":"55472","inReplyTo":"20210410152140.3525040-1-sandals@crustytoothpaste.net","subject":"[PATCH 06/15] Use the final_oid_fn to finalize hashing of object IDs","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-10T15:21:31Z","receivedAt":"2021-04-10T15:23:06Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"When we're hashing a value which is going to be an object ID, we want to\nzero-pad that value if necessary.  To do so, use the final_oid_fn\ninstead of the final_fn anytime we're going to create an object ID to\nensure we perform this operation.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n builtin/fast-import.c    | 4 ++--\n builtin/index-pack.c     | 2 +-\n builtin/unpack-objects.c | 2 +-\n bulk-checkin.c           | 2 +-\n diff.c                   | 2 +-\n http.c                   | 2 +-\n object-file.c            | 8 ++++----\n 7 files changed, 11 insertions(+), 11 deletions(-)\n\ndiff --git a/builtin/fast-import.c b/builtin/fast-import.c\nindex 9d2a058a66..20406f6775 100644\n--- a/builtin/fast-import.c\n+++ b/builtin/fast-import.c\n@@ -940,7 +940,7 @@ static int store_object(\n \tthe_hash_algo->init_fn(&c);\n \tthe_hash_algo->update_fn(&c, hdr, hdrlen);\n \tthe_hash_algo->update_fn(&c, dat->buf, dat->len);\n-\tthe_hash_algo->final_fn(oid.hash, &c);\n+\tthe_hash_algo->final_oid_fn(&oid, &c);\n \tif (oidout)\n \t\toidcpy(oidout, &oid);\n \n@@ -1136,7 +1136,7 @@ static void stream_blob(uintmax_t len, struct object_id *oidout, uintmax_t mark)\n \t\t}\n \t}\n \tgit_deflate_end(&s);\n-\tthe_hash_algo->final_fn(oid.hash, &c);\n+\tthe_hash_algo->final_oid_fn(&oid, &c);\n \n \tif (oidout)\n \t\toidcpy(oidout, &oid);\ndiff --git a/builtin/index-pack.c b/builtin/index-pack.c\nindex 41e2c240b8..3fbc5d7077 100644\n--- a/builtin/index-pack.c\n+++ b/builtin/index-pack.c\n@@ -489,7 +489,7 @@ static void *unpack_entry_data(off_t offset, unsigned long size,\n \t\tbad_object(offset, _(\"inflate returned %d\"), status);\n \tgit_inflate_end(&stream);\n \tif (oid)\n-\t\tthe_hash_algo->final_fn(oid->hash, &c);\n+\t\tthe_hash_algo->final_oid_fn(oid, &c);\n \treturn buf == fixed_buf ? NULL : buf;\n }\n \ndiff --git a/builtin/unpack-objects.c b/builtin/unpack-objects.c\nindex a8b73ecf43..6ac90dc5f7 100644\n--- a/builtin/unpack-objects.c\n+++ b/builtin/unpack-objects.c\n@@ -576,7 +576,7 @@ int cmd_unpack_objects(int argc, const char **argv, const char *prefix)\n \tthe_hash_algo->init_fn(&ctx);\n \tunpack_all();\n \tthe_hash_algo->update_fn(&ctx, buffer, offset);\n-\tthe_hash_algo->final_fn(oid.hash, &ctx);\n+\tthe_hash_algo->final_oid_fn(&oid, &ctx);\n \tif (strict) {\n \t\twrite_rest();\n \t\tif (fsck_finish(&fsck_options))\ndiff --git a/bulk-checkin.c b/bulk-checkin.c\nindex 6f3c97cd34..127312acd1 100644\n--- a/bulk-checkin.c\n+++ b/bulk-checkin.c\n@@ -238,7 +238,7 @@ static int deflate_to_pack(struct bulk_checkin_state *state,\n \t\tif (lseek(fd, seekback, SEEK_SET) == (off_t) -1)\n \t\t\treturn error(\"cannot seek back\");\n \t}\n-\tthe_hash_algo->final_fn(result_oid->hash, &ctx);\n+\tthe_hash_algo->final_oid_fn(result_oid, &ctx);\n \tif (!idx)\n \t\treturn 0;\n \ndiff --git a/diff.c b/diff.c\nindex 4acccd9d7e..97c62f47df 100644\n--- a/diff.c\n+++ b/diff.c\n@@ -6234,7 +6234,7 @@ static int diff_get_patch_id(struct diff_options *options, struct object_id *oid\n \t}\n \n \tif (!stable)\n-\t\tthe_hash_algo->final_fn(oid->hash, &ctx);\n+\t\tthe_hash_algo->final_oid_fn(oid, &ctx);\n \n \treturn 0;\n }\ndiff --git a/http.c b/http.c\nindex 406410f884..c83bc33a5f 100644\n--- a/http.c\n+++ b/http.c\n@@ -2576,7 +2576,7 @@ int finish_http_object_request(struct http_object_request *freq)\n \t}\n \n \tgit_inflate_end(&freq->stream);\n-\tthe_hash_algo->final_fn(freq->real_oid.hash, &freq->c);\n+\tthe_hash_algo->final_oid_fn(&freq->real_oid, &freq->c);\n \tif (freq->zret != Z_STREAM_END) {\n \t\tunlink_or_warn(freq->tmpfile.buf);\n \t\treturn -1;\ndiff --git a/object-file.c b/object-file.c\nindex 58d31452d8..3f43c376e7 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -1054,7 +1054,7 @@ int check_object_signature(struct repository *r, const struct object_id *oid,\n \t\t\tbreak;\n \t\tr->hash_algo->update_fn(&c, buf, readlen);\n \t}\n-\tr->hash_algo->final_fn(real_oid.hash, &c);\n+\tr->hash_algo->final_oid_fn(&real_oid, &c);\n \tclose_istream(st);\n \treturn !oideq(oid, &real_oid) ? -1 : 0;\n }\n@@ -1755,7 +1755,7 @@ static void write_object_file_prepare(const struct git_hash_algo *algo,\n \talgo->init_fn(&c);\n \talgo->update_fn(&c, hdr, *hdrlen);\n \talgo->update_fn(&c, buf, len);\n-\talgo->final_fn(oid->hash, &c);\n+\talgo->final_oid_fn(oid, &c);\n }\n \n /*\n@@ -1927,7 +1927,7 @@ static int write_loose_object(const struct object_id *oid, char *hdr,\n \tif (ret != Z_OK)\n \t\tdie(_(\"deflateEnd on object %s failed (%d)\"), oid_to_hex(oid),\n \t\t    ret);\n-\tthe_hash_algo->final_fn(parano_oid.hash, &c);\n+\tthe_hash_algo->final_oid_fn(&parano_oid, &c);\n \tif (!oideq(oid, &parano_oid))\n \t\tdie(_(\"confused by unstable object source data for %s\"),\n \t\t    oid_to_hex(oid));\n@@ -2520,7 +2520,7 @@ static int check_stream_oid(git_zstream *stream,\n \t\treturn -1;\n \t}\n \n-\tthe_hash_algo->final_fn(real_oid.hash, &c);\n+\tthe_hash_algo->final_oid_fn(&real_oid, &c);\n \tif (!oideq(expected_oid, &real_oid)) {\n \t\terror(_(\"hash mismatch for %s (expected %s)\"), path,\n \t\t      oid_to_hex(expected_oid));\n"},{"id":"421549","messageId":"20210410152140.3525040-14-sandals@crustytoothpaste.net","threadId":"55472","inReplyTo":"20210410152140.3525040-1-sandals@crustytoothpaste.net","subject":"[PATCH 13/15] builtin/pack-objects: avoid using struct object_id for pack hash","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-10T15:21:38Z","receivedAt":"2021-04-10T15:23:09Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"We use struct object_id for the names of objects.  It isn't intended to\nbe used for other hash values that don't name objects such as the pack\nhash.\n\nBecause struct object_id will soon need to have its algorithm member\nset, using it in this code path would mean that we didn't set that\nmember, only the hash member, which would result in a crash.  For both\nof these reasons, switch to using an unsigned char array of size\nGIT_MAX_RAWSZ.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n builtin/pack-objects.c | 20 ++++++++++----------\n 1 file changed, 10 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 525c2d8552..5b25382204 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -1030,7 +1030,7 @@ static void write_pack_file(void)\n \twrite_order = compute_write_order();\n \n \tdo {\n-\t\tstruct object_id oid;\n+\t\tunsigned char hash[GIT_MAX_RAWSZ];\n \t\tchar *pack_tmp_name = NULL;\n \n \t\tif (pack_to_stdout)\n@@ -1059,13 +1059,13 @@ static void write_pack_file(void)\n \t\t * If so, rewrite it like in fast-import\n \t\t */\n \t\tif (pack_to_stdout) {\n-\t\t\tfinalize_hashfile(f, oid.hash, CSUM_HASH_IN_STREAM | CSUM_CLOSE);\n+\t\t\tfinalize_hashfile(f, hash, CSUM_HASH_IN_STREAM | CSUM_CLOSE);\n \t\t} else if (nr_written == nr_remaining) {\n-\t\t\tfinalize_hashfile(f, oid.hash, CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);\n+\t\t\tfinalize_hashfile(f, hash, CSUM_HASH_IN_STREAM | CSUM_FSYNC | CSUM_CLOSE);\n \t\t} else {\n-\t\t\tint fd = finalize_hashfile(f, oid.hash, 0);\n-\t\t\tfixup_pack_header_footer(fd, oid.hash, pack_tmp_name,\n-\t\t\t\t\t\t nr_written, oid.hash, offset);\n+\t\t\tint fd = finalize_hashfile(f, hash, 0);\n+\t\t\tfixup_pack_header_footer(fd, hash, pack_tmp_name,\n+\t\t\t\t\t\t nr_written, hash, offset);\n \t\t\tclose(fd);\n \t\t\tif (write_bitmap_index) {\n \t\t\t\tif (write_bitmap_index != WRITE_BITMAP_QUIET)\n@@ -1100,17 +1100,17 @@ static void write_pack_file(void)\n \t\t\tstrbuf_addf(&tmpname, \"%s-\", base_name);\n \n \t\t\tif (write_bitmap_index) {\n-\t\t\t\tbitmap_writer_set_checksum(oid.hash);\n+\t\t\t\tbitmap_writer_set_checksum(hash);\n \t\t\t\tbitmap_writer_build_type_index(\n \t\t\t\t\t&to_pack, written_list, nr_written);\n \t\t\t}\n \n \t\t\tfinish_tmp_packfile(&tmpname, pack_tmp_name,\n \t\t\t\t\t    written_list, nr_written,\n-\t\t\t\t\t    &pack_idx_opts, oid.hash);\n+\t\t\t\t\t    &pack_idx_opts, hash);\n \n \t\t\tif (write_bitmap_index) {\n-\t\t\t\tstrbuf_addf(&tmpname, \"%s.bitmap\", oid_to_hex(&oid));\n+\t\t\t\tstrbuf_addf(&tmpname, \"%s.bitmap\", hash_to_hex(hash));\n \n \t\t\t\tstop_progress(&progress_state);\n \n@@ -1124,7 +1124,7 @@ static void write_pack_file(void)\n \n \t\t\tstrbuf_release(&tmpname);\n \t\t\tfree(pack_tmp_name);\n-\t\t\tputs(oid_to_hex(&oid));\n+\t\t\tputs(hash_to_hex(hash));\n \t\t}\n \n \t\t/* mark written objects as written to previous pack */\n"},{"id":"421550","messageId":"20210410152140.3525040-15-sandals@crustytoothpaste.net","threadId":"55472","inReplyTo":"20210410152140.3525040-1-sandals@crustytoothpaste.net","subject":"[PATCH 14/15] hex: default to the_hash_algo on zero algorithm value","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-10T15:21:39Z","receivedAt":"2021-04-10T15:23:10Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"There are numerous places in the codebase where we assume we can\ninitialize data by zeroing all its bytes.  However, when we do that with\na struct object_id, it leaves the structure with a zero value for the\nalgorithm, which is invalid.\n\nWe could forbid this pattern and require that all struct object_id\ninstances be initialized using oidclr, but this seems burdensome and\nit's unnatural to most C programmers.  Instead, if the algorithm is\nzero, assume we wanted to use the default hash algorithm instead.\n\nSigned-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n---\n hex.c | 7 +++++++\n 1 file changed, 7 insertions(+)\n\ndiff --git a/hex.c b/hex.c\nindex 5fa3e71cb9..43597b2dbb 100644\n--- a/hex.c\n+++ b/hex.c\n@@ -124,6 +124,13 @@ char *hash_to_hex_algop_r(char *buffer, const unsigned char *hash,\n \tchar *buf = buffer;\n \tint i;\n \n+\t/*\n+\t * Our struct object_id has been memset to 0, so default to printing\n+\t * using the default hash.\n+\t */\n+\tif (algop == &hash_algos[0])\n+\t\talgop = the_hash_algo;\n+\n \tfor (i = 0; i < algop->rawsz; i++) {\n \t\tunsigned int val = *hash++;\n \t\t*buf++ = hex[val >> 4];\n"},{"id":"421571","messageId":"CAPx1GvfiG9AzYJi2PqhEW2d1n1ghn4Jnna4QR0cJSgNXvaEXQg@mail.gmail.com","threadId":"55472","inReplyTo":"20210410152140.3525040-9-sandals@crustytoothpaste.net","subject":"Re: [PATCH 08/15] cache: compare the entire buffer for struct object_id","fromName":"Chris Torek","fromEmail":"chris.torek@gmail.com","sentAt":"2021-04-11T08:17:10Z","receivedAt":"2021-04-11T08:17:25Z","isPatch":true,"sender":{"key":"chris.torek@gmail.com","avatar":"https://avatars.githubusercontent.com/u/16826774?v=4"},"body":"Just an observation here: comparing 256 bytes every time\nwould seem to have one nice bonus side effect and one\npotentially bad, but vanishingly unlikely, side effect: a 160\nbyte null hash will now compare equal to a 256 byte null\nhash (good), but a 160 byte hash extended to 256 bytes\nwill compare equal to a 256 byte hash that just happens to\nend in 96 bytes of zero (bad, but I would guess, will never\nactually happen).\n\nChris\n\nOn Sat, Apr 10, 2021 at 8:23 AM brian m. carlson\n<sandals@crustytoothpaste.net> wrote:\n>\n> Currently, when we compare two object IDs, we have to take a branch to\n> determine what the hash size is supposed to be.  The compiler can\n> optimize well for a single length, but has trouble when there are two\n> possible lengths.\n>\n> There is, however, an alternative: we can ensure that we always compare\n> the full length of the hash buffer, but in turn we must zero the\n> remainder of the buffer when using SHA-1; otherwise, we'll end up with\n> incompatible junk at the end of otherwise equivalent object IDs that\n> will prevent them from matching.  This is an acceptable tradeoff,\n> because we generally read an object ID in once, but then compare it\n> against others multiple times.\n>\n> This latter approach also has some benefits as well: since we will have\n> annotated every location in which we load an object ID into an instance\n> of struct object_id, if we want to set the hash algorithm for the object\n> ID, we can do so at the same time.\n>\n> Adopt this latter approach, since it provides us greater flexibility and\n> lets us read and store object IDs for multiple algorithms at once.\n>\n> Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n> ---\n>  hash.h        | 13 ++++++++++---\n>  hex.c         |  9 ++++++---\n>  notes.c       |  3 +++\n>  object-file.c |  1 +\n>  4 files changed, 20 insertions(+), 6 deletions(-)\n>\n> diff --git a/hash.h b/hash.h\n> index c8f03d8aee..04eba5c56b 100644\n> --- a/hash.h\n> +++ b/hash.h\n> @@ -205,7 +205,7 @@ static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)\n>\n>  static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)\n>  {\n> -       return hashcmp(oid1->hash, oid2->hash);\n> +       return memcmp(oid1->hash, oid2->hash, GIT_MAX_RAWSZ);\n>  }\n>\n>  static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2)\n> @@ -221,7 +221,7 @@ static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2)\n>\n>  static inline int oideq(const struct object_id *oid1, const struct object_id *oid2)\n>  {\n> -       return hasheq(oid1->hash, oid2->hash);\n> +       return !memcmp(oid1->hash, oid2->hash, GIT_MAX_RAWSZ);\n>  }\n>\n>  static inline int is_null_oid(const struct object_id *oid)\n> @@ -258,7 +258,9 @@ static inline void oidclr(struct object_id *oid)\n>\n>  static inline void oidread(struct object_id *oid, const unsigned char *hash)\n>  {\n> -       memcpy(oid->hash, hash, the_hash_algo->rawsz);\n> +       size_t rawsz = the_hash_algo->rawsz;\n> +       memcpy(oid->hash, hash, rawsz);\n> +       memset(oid->hash + rawsz, 0, GIT_MAX_RAWSZ - rawsz);\n>  }\n>\n>  static inline int is_empty_blob_sha1(const unsigned char *sha1)\n> @@ -281,6 +283,11 @@ static inline int is_empty_tree_oid(const struct object_id *oid)\n>         return oideq(oid, the_hash_algo->empty_tree);\n>  }\n>\n> +static inline void oid_pad_buffer(struct object_id *oid, const struct git_hash_algo *algop)\n> +{\n> +       memset(oid->hash + algop->rawsz, 0, GIT_MAX_RAWSZ - algop->rawsz);\n> +}\n> +\n>  const char *empty_tree_oid_hex(void);\n>  const char *empty_blob_oid_hex(void);\n>\n> diff --git a/hex.c b/hex.c\n> index da51e64929..5fa3e71cb9 100644\n> --- a/hex.c\n> +++ b/hex.c\n> @@ -69,7 +69,10 @@ int get_sha1_hex(const char *hex, unsigned char *sha1)\n>  int get_oid_hex_algop(const char *hex, struct object_id *oid,\n>                       const struct git_hash_algo *algop)\n>  {\n> -       return get_hash_hex_algop(hex, oid->hash, algop);\n> +       int ret = get_hash_hex_algop(hex, oid->hash, algop);\n> +       if (!ret)\n> +               oid_pad_buffer(oid, algop);\n> +       return ret;\n>  }\n>\n>  /*\n> @@ -80,7 +83,7 @@ int get_oid_hex_any(const char *hex, struct object_id *oid)\n>  {\n>         int i;\n>         for (i = GIT_HASH_NALGOS - 1; i > 0; i--) {\n> -               if (!get_hash_hex_algop(hex, oid->hash, &hash_algos[i]))\n> +               if (!get_oid_hex_algop(hex, oid, &hash_algos[i]))\n>                         return i;\n>         }\n>         return GIT_HASH_UNKNOWN;\n> @@ -95,7 +98,7 @@ int parse_oid_hex_algop(const char *hex, struct object_id *oid,\n>                         const char **end,\n>                         const struct git_hash_algo *algop)\n>  {\n> -       int ret = get_hash_hex_algop(hex, oid->hash, algop);\n> +       int ret = get_oid_hex_algop(hex, oid, algop);\n>         if (!ret)\n>                 *end = hex + algop->hexsz;\n>         return ret;\n> diff --git a/notes.c b/notes.c\n> index a44b25858f..1dfe9e2b9f 100644\n> --- a/notes.c\n> +++ b/notes.c\n> @@ -455,6 +455,8 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n>                 CALLOC_ARRAY(l, 1);\n>                 oidcpy(&l->key_oid, &object_oid);\n>                 oidcpy(&l->val_oid, &entry.oid);\n> +               oid_pad_buffer(&l->key_oid, the_hash_algo);\n> +               oid_pad_buffer(&l->val_oid, the_hash_algo);\n>                 if (note_tree_insert(t, node, n, l, type,\n>                                      combine_notes_concatenate))\n>                         die(\"Failed to load %s %s into notes tree \"\n> @@ -484,6 +486,7 @@ static void load_subtree(struct notes_tree *t, struct leaf_node *subtree,\n>                                 strbuf_addch(&non_note_path, '/');\n>                         }\n>                         strbuf_addstr(&non_note_path, entry.path);\n> +                       oid_pad_buffer(&entry.oid, the_hash_algo);\n>                         add_non_note(t, strbuf_detach(&non_note_path, NULL),\n>                                      entry.mode, entry.oid.hash);\n>                 }\n> diff --git a/object-file.c b/object-file.c\n> index 3f43c376e7..8e338247cc 100644\n> --- a/object-file.c\n> +++ b/object-file.c\n> @@ -2352,6 +2352,7 @@ int for_each_file_in_obj_subdir(unsigned int subdir_nr,\n>                 if (namelen == the_hash_algo->hexsz - 2 &&\n>                     !hex_to_bytes(oid.hash + 1, de->d_name,\n>                                   the_hash_algo->rawsz - 1)) {\n> +                       oid_pad_buffer(&oid, the_hash_algo);\n>                         if (obj_cb) {\n>                                 r = obj_cb(&oid, path->buf, data);\n>                                 if (r)\n"},{"id":"421572","messageId":"87sg3xfbkd.fsf@evledraar.gmail.com","threadId":"55472","inReplyTo":"20210410152140.3525040-3-sandals@crustytoothpaste.net","subject":"Re: [PATCH 02/15] builtin/hash-object: allow literally hashing with a given algorithm","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-11T08:52:02Z","receivedAt":"2021-04-11T08:52:09Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sat, Apr 10 2021, brian m. carlson wrote:\n\n> +\talgo = the_hash_algo;\n> +\tif (object_format) {\n> +\t\tif (flags & HASH_WRITE_OBJECT)\n> +\t\t\terrstr = \"Can't use -w with --object-format\";\n> +\t\telse {\n> +\t\t\tint id = hash_algo_by_name(object_format);\n> +\t\t\tif (id == GIT_HASH_UNKNOWN)\n> +\t\t\t\terrstr = \"Unknown object format\";\n\nAn established pattern, but shouldn't these be N_()'d while we're at it?\nAt least for new strings.\n\n> +\t\t\telse\n> +\t\t\t\talgo = &hash_algos[id];\n> +\t\t}\n> +\t}\n\nStyle nit: if .. {} else {} not if .. else {}.\n\n> +test_expect_success '--literally with --object-format' '\n> +\ttest $(test_oid --hash=sha1 hello) = $(git hash-object -t blob --literally --object-format=sha1 hello) &&\n> +\ttest $(test_oid --hash=sha256 hello) = $(git hash-object -t blob --literally --object-format=sha256 hello)\n> +'\n\nThis would be more readable and easier to debug with 4x tempfiles and 2x\ntest_cmp.\n"},{"id":"421586","messageId":"87pmz1f3y6.fsf@evledraar.gmail.com","threadId":"55472","inReplyTo":"20210410152140.3525040-9-sandals@crustytoothpaste.net","subject":"Re: [PATCH 08/15] cache: compare the entire buffer for struct object_id","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-11T11:36:33Z","receivedAt":"2021-04-11T11:36:37Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sat, Apr 10 2021, brian m. carlson wrote:\n\n> Currently, when we compare two object IDs, we have to take a branch to\n> determine what the hash size is supposed to be.  The compiler can\n> optimize well for a single length, but has trouble when there are two\n> possible lengths.\n\nThis would benefit from some performance/perf numbers. When this code\nwas first changed like this in 183a638b7da (hashcmp: assert constant\nhash size, 2018-08-23) we had:\n\n      Test     v2.18.0             v2.19.0-rc0               HEAD\n      ------------------------------------------------------------------------------\n      0001.2:  34.24(33.81+0.43)   34.83(34.42+0.40) +1.7%   33.90(33.47+0.42) -1.0%\n\nThen it was later modified in 0dab7129ab1 (cache: make hashcmp and\nhasheq work with larger hashes, 2018-11-14).\n\n> @@ -205,7 +205,7 @@ static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)\n>  \n>  static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)\n>  {\n> -\treturn hashcmp(oid1->hash, oid2->hash);\n> +\treturn memcmp(oid1->hash, oid2->hash, GIT_MAX_RAWSZ);\n>  }\n\nhashcmp is now:\n\n        if (the_hash_algo->rawsz == GIT_MAX_RAWSZ)\n                return memcmp(sha1, sha2, GIT_MAX_RAWSZ);\n        return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);\n\nWouldn't it make more sense to amend it to just be a memcmp\nwrapper/macro if we're going to not make this conditional on the hash\nalgorithm, or are there other callsites where we still want the old way\nof doing it?\n\n>  \n>  static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2)\n> @@ -221,7 +221,7 @@ static inline int hasheq(const unsigned char *sha1, const unsigned char *sha2)\n>  \n>  static inline int oideq(const struct object_id *oid1, const struct object_id *oid2)\n>  {\n> -\treturn hasheq(oid1->hash, oid2->hash);\n> +\treturn !memcmp(oid1->hash, oid2->hash, GIT_MAX_RAWSZ);\n>  }\n\nDitto hasheq v.s. !memcmp:\n\n        if (the_hash_algo->rawsz == GIT_MAX_RAWSZ)\n                return !memcmp(sha1, sha2, GIT_MAX_RAWSZ);\n        return !memcmp(sha1, sha2, GIT_SHA1_RAWSZ);\n"},{"id":"421588","messageId":"87mtu5f31u.fsf@evledraar.gmail.com","threadId":"55472","inReplyTo":"20210410152140.3525040-4-sandals@crustytoothpaste.net","subject":"Re: [PATCH 03/15] cache: add an algo member to struct object_id","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-11T11:55:57Z","receivedAt":"2021-04-11T11:56:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sat, Apr 10 2021, brian m. carlson wrote:\n\n> Now that we're working with multiple hash algorithms in the same repo,\n> it's best if we label each object ID with its algorithm so we can\n> determine how to format a given object ID. Add a member called algo to\n> struct object_id.\n>\n> Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n> ---\n>  hash.h | 1 +\n>  1 file changed, 1 insertion(+)\n>\n> diff --git a/hash.h b/hash.h\n> index 3fb0c3d400..dafdcb3335 100644\n> --- a/hash.h\n> +++ b/hash.h\n> @@ -181,6 +181,7 @@ static inline int hash_algo_by_ptr(const struct git_hash_algo *p)\n>  \n>  struct object_id {\n>  \tunsigned char hash[GIT_MAX_RAWSZ];\n> +\tint algo;\n\nCuriosity since I'm not as familiar as you with the multi-hash support\nby far:\n\nSo struct object_id is GIT_MAX_RAWSZ, not two types of structs for\nGIT_SHA1_RAWSZ and GIT_SHA256_RAWSZ. That pre-dates this series because\nwe'd like to not deal with two types of objects everywhere for SHA-1 and\nSHA-256. Makes sense.\n\nBefore this series we'd memcmp them up to their actual length, but the\nlast GIT_MAX_RAWSZ-GIT_SHA1_RAWSZ would be uninitialized\n\nNow we pad them out, so the last 96 bits of every SHA1 are 0000...;\nCouldn't we also tell which hash an object is by memcmp-ing those last N\nbits and see if they're all zero'd?\n\nFeels a bit hackish, and we'd need to reconsider that method if we'd\never support other same-length hashes.\n\nBut OTOH having these objects all padded out in memory to the same\nlength, but having to carry around a \"what hash algo\" is it yields the\narguably weird hack of having a per-hash NULL_OID, which has never been\nan actual object of any hash type, but just a pseudo-object.\n\nAs another aside I had some local patches (just for playing around) to\nimplement SHA-256/160, i.e. a SHA-256-to-SHA-1-length that doesn't\nofficially exist. We'd store things as full-length SHA-256 internally,\nbut on anything that would format them (including plumbing output) we'd\nemit the truncated version(s).\n\nThe idea was to support Git/SHA-256 when combined with legacy systems\nwho'd all need DB column changes to have different length hashes.\n\nI abandoned it as insany sillyness after playing with it for about a\nday, but it did reveal that much of the hash code now can assume\ninternal length == formatting length, which is why I'm 3 paragraphs into\nthis digression, i.e. maybe some of the code structure also makes having\na NULL_OID always be 256-bits when we want to format it as 160/256\npainful...\n"},{"id":"421589","messageId":"87k0p9f2z9.fsf@evledraar.gmail.com","threadId":"55472","inReplyTo":"20210410152140.3525040-10-sandals@crustytoothpaste.net","subject":"Re: [PATCH 09/15] hash: set and copy algo field in struct object_id","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-11T11:57:30Z","receivedAt":"2021-04-11T11:57:35Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sat, Apr 10 2021, brian m. carlson wrote:\n\n>  const struct object_id null_oid;\n>  static const struct object_id empty_tree_oid = {\n> -\tEMPTY_TREE_SHA1_BIN_LITERAL\n> +\tEMPTY_TREE_SHA1_BIN_LITERAL,\n> +\tGIT_HASH_SHA1,\n>  };\n>  static const struct object_id empty_blob_oid = {\n> -\tEMPTY_BLOB_SHA1_BIN_LITERAL\n> +\tEMPTY_BLOB_SHA1_BIN_LITERAL,\n> +\tGIT_HASH_SHA1,\n>  };\n>  static const struct object_id empty_tree_oid_sha256 = {\n> -\tEMPTY_TREE_SHA256_BIN_LITERAL\n> +\tEMPTY_TREE_SHA256_BIN_LITERAL,\n> +\tGIT_HASH_SHA256,\n>  };\n>  static const struct object_id empty_blob_oid_sha256 = {\n> -\tEMPTY_BLOB_SHA256_BIN_LITERAL\n> +\tEMPTY_BLOB_SHA256_BIN_LITERAL,\n> +\tGIT_HASH_SHA256,\n>  };\n\nIn this and some other patches we're continuing to add new fields to\nstructs without using designated initializers.\n\nNot a new problem at all, just a note that if you re-roll I for one\nwould very much appreciate starting by migrating over to that. It makes\nfor much easier reading in subsequent patches in this series, and in\nfuture ones.\n"},{"id":"421594","messageId":"xmqqy2dohqau.fsf@gitster.g","threadId":"55472","inReplyTo":"20210410152140.3525040-11-sandals@crustytoothpaste.net","subject":"Re: [PATCH 10/15] hash: provide per-algorithm null OIDs","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-04-11T14:03:05Z","receivedAt":"2021-04-11T14:03:12Z","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> diff --git a/object-file.c b/object-file.c\n> index 5f1fa05c4e..50bb5b6ca4 100644\n> --- a/object-file.c\n> +++ b/object-file.c\n> @@ -55,7 +55,6 @@\n>  \t\"\\x6f\\xe1\\x41\\xf7\\x74\\x91\\x20\\xa3\\x03\\x72\" \\\n>  \t\"\\x18\\x13\"\n>  \n> -const struct object_id null_oid;\n>  static const struct object_id empty_tree_oid = {\n>  \tEMPTY_TREE_SHA1_BIN_LITERAL,\n>  \tGIT_HASH_SHA1,\n> @@ -64,6 +63,9 @@ static const struct object_id empty_blob_oid = {\n>  \tEMPTY_BLOB_SHA1_BIN_LITERAL,\n>  \tGIT_HASH_SHA1,\n>  };\n> +const struct object_id null_oid_sha1 = {\n> +\t{0}, GIT_HASH_SHA1,\n> +};\n\nsparse wants this to be a file-scope static.\n"},{"id":"421623","messageId":"YHNkrnid7sJh5RSh@camp.crustytoothpaste.net","threadId":"55472","inReplyTo":"87pmz1f3y6.fsf@evledraar.gmail.com","subject":"Re: [PATCH 08/15] cache: compare the entire buffer for struct object_id","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-11T21:05:50Z","receivedAt":"2021-04-11T21:05:57Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2021-04-11 at 11:36:33, Ævar Arnfjörð Bjarmason wrote:\n> \n> On Sat, Apr 10 2021, brian m. carlson wrote:\n> \n> > Currently, when we compare two object IDs, we have to take a branch to\n> > determine what the hash size is supposed to be.  The compiler can\n> > optimize well for a single length, but has trouble when there are two\n> > possible lengths.\n> \n> This would benefit from some performance/perf numbers. When this code\n> was first changed like this in 183a638b7da (hashcmp: assert constant\n> hash size, 2018-08-23) we had:\n> \n>       Test     v2.18.0             v2.19.0-rc0               HEAD\n>       ------------------------------------------------------------------------------\n>       0001.2:  34.24(33.81+0.43)   34.83(34.42+0.40) +1.7%   33.90(33.47+0.42) -1.0%\n> \n> Then it was later modified in 0dab7129ab1 (cache: make hashcmp and\n> hasheq work with larger hashes, 2018-11-14).\n\nI can do some perf numbers.\n\n> > @@ -205,7 +205,7 @@ static inline int hashcmp(const unsigned char *sha1, const unsigned char *sha2)\n> >  \n> >  static inline int oidcmp(const struct object_id *oid1, const struct object_id *oid2)\n> >  {\n> > -\treturn hashcmp(oid1->hash, oid2->hash);\n> > +\treturn memcmp(oid1->hash, oid2->hash, GIT_MAX_RAWSZ);\n> >  }\n> \n> hashcmp is now:\n> \n>         if (the_hash_algo->rawsz == GIT_MAX_RAWSZ)\n>                 return memcmp(sha1, sha2, GIT_MAX_RAWSZ);\n>         return memcmp(sha1, sha2, GIT_SHA1_RAWSZ);\n> \n> Wouldn't it make more sense to amend it to just be a memcmp\n> wrapper/macro if we're going to not make this conditional on the hash\n> algorithm, or are there other callsites where we still want the old way\n> of doing it?\n\nNo, we can't do that.  With oidcmp, we know the buffer is large enough.\nHowever, in some cases, the buffer in hashcmp is not large enough.  For\nexample, we may be at the end of a SHA-1 tree object and we'd segfault.\nI did try that and I quickly found that it was totally broken.\n-- \nbrian m. carlson (he/him or they/them)\nHouston, Texas, US\n"},{"id":"421624","messageId":"YHNlFmpQa3Qlgh9g@camp.crustytoothpaste.net","threadId":"55472","inReplyTo":"87sg3xfbkd.fsf@evledraar.gmail.com","subject":"Re: [PATCH 02/15] builtin/hash-object: allow literally hashing with a given algorithm","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-11T21:07:34Z","receivedAt":"2021-04-11T21:07:42Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2021-04-11 at 08:52:02, Ævar Arnfjörð Bjarmason wrote:\n> \n> On Sat, Apr 10 2021, brian m. carlson wrote:\n> \n> > +\talgo = the_hash_algo;\n> > +\tif (object_format) {\n> > +\t\tif (flags & HASH_WRITE_OBJECT)\n> > +\t\t\terrstr = \"Can't use -w with --object-format\";\n> > +\t\telse {\n> > +\t\t\tint id = hash_algo_by_name(object_format);\n> > +\t\t\tif (id == GIT_HASH_UNKNOWN)\n> > +\t\t\t\terrstr = \"Unknown object format\";\n> \n> An established pattern, but shouldn't these be N_()'d while we're at it?\n> At least for new strings.\n\nSure, I can do that.\n\n> > +\t\t\telse\n> > +\t\t\t\talgo = &hash_algos[id];\n> > +\t\t}\n> > +\t}\n> \n> Style nit: if .. {} else {} not if .. else {}.\n\nWill fix.\n\n> > +test_expect_success '--literally with --object-format' '\n> > +\ttest $(test_oid --hash=sha1 hello) = $(git hash-object -t blob --literally --object-format=sha1 hello) &&\n> > +\ttest $(test_oid --hash=sha256 hello) = $(git hash-object -t blob --literally --object-format=sha256 hello)\n> > +'\n> \n> This would be more readable and easier to debug with 4x tempfiles and 2x\n> test_cmp.\n\nOkay, I can go for that.\n-- \nbrian m. carlson (he/him or they/them)\nHouston, Texas, US\n"},{"id":"421626","messageId":"YHNsEVMs2lRmycOx@camp.crustytoothpaste.net","threadId":"55472","inReplyTo":"87mtu5f31u.fsf@evledraar.gmail.com","subject":"Re: [PATCH 03/15] cache: add an algo member to struct object_id","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-11T21:37:21Z","receivedAt":"2021-04-11T21:38:00Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2021-04-11 at 11:55:57, Ævar Arnfjörð Bjarmason wrote:\n> \n> On Sat, Apr 10 2021, brian m. carlson wrote:\n> \n> > Now that we're working with multiple hash algorithms in the same repo,\n> > it's best if we label each object ID with its algorithm so we can\n> > determine how to format a given object ID. Add a member called algo to\n> > struct object_id.\n> >\n> > Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n> > ---\n> >  hash.h | 1 +\n> >  1 file changed, 1 insertion(+)\n> >\n> > diff --git a/hash.h b/hash.h\n> > index 3fb0c3d400..dafdcb3335 100644\n> > --- a/hash.h\n> > +++ b/hash.h\n> > @@ -181,6 +181,7 @@ static inline int hash_algo_by_ptr(const struct git_hash_algo *p)\n> >  \n> >  struct object_id {\n> >  \tunsigned char hash[GIT_MAX_RAWSZ];\n> > +\tint algo;\n> \n> Curiosity since I'm not as familiar as you with the multi-hash support\n> by far:\n> \n> So struct object_id is GIT_MAX_RAWSZ, not two types of structs for\n> GIT_SHA1_RAWSZ and GIT_SHA256_RAWSZ. That pre-dates this series because\n> we'd like to not deal with two types of objects everywhere for SHA-1 and\n> SHA-256. Makes sense.\n> \n> Before this series we'd memcmp them up to their actual length, but the\n> last GIT_MAX_RAWSZ-GIT_SHA1_RAWSZ would be uninitialized\n> \n> Now we pad them out, so the last 96 bits of every SHA1 are 0000...;\n> Couldn't we also tell which hash an object is by memcmp-ing those last N\n> bits and see if they're all zero'd?\n\nThat makes a lot of assumptions about the security of the hash\nalgorithm that I don't want to make here.  If anyone can ever find a\nSHA-256 hash with trailing 96 bits zero, then they can confuse it with a\nSHA-1 hash.  That means that our security level goes from 128 bits to 96\nbits.  It's also a nonstandard construction.\n\nMore importantly, it results in the null OID being treated as a SHA-1\nOID.  Because we do print the null OID in some cases, we're going to\nbreak a lot of output formats if we print all the rest of the OIDs with\n64 characters and then the null OID with 40.  That's to say nothing of\nthe problems in binary formats.\n\nThe reason we pad these objects is because our hashmaps are broken if we\ndon't.  I don't remember all the gory details, but it was obvious to me\nthat if they weren't consistently equal, things were going to be broken.\nThat's the only reason, not theoretical purity.\n\n> Feels a bit hackish, and we'd need to reconsider that method if we'd\n> ever support other same-length hashes.\n\nMy hope is that we don't need to do this, but we do have SHA-3 to serve\nas a backup for SHA-2.  If quantum computers don't progress\nsubstantially, SHA-3-256 is definitely a viable candidate for\nreplacement if anything ever happens to SHA-256.\n\n> But OTOH having these objects all padded out in memory to the same\n> length, but having to carry around a \"what hash algo\" is it yields the\n> arguably weird hack of having a per-hash NULL_OID, which has never been\n> an actual object of any hash type, but just a pseudo-object.\n\nUnfortunately, as I mentioned above, we need to have two null OIDs to\nhandle printing things out.  It's inconvenient, I agree.\n\n> I abandoned it as insany sillyness after playing with it for about a\n> day, but it did reveal that much of the hash code now can assume\n> internal length == formatting length, which is why I'm 3 paragraphs into\n> this digression, i.e. maybe some of the code structure also makes having\n> a NULL_OID always be 256-bits when we want to format it as 160/256\n> painful...\n\nWe'll always format based on the algorithm in the OID.  That's the\nsimplest way to make things work because unfortunately we may end up\nwith both types of OIDs in the same code paths (as we're converting one\nto the other) and otherwise our printing functions need a lot of special\nhandling and even more variants than they have today.\n-- \nbrian m. carlson (he/him or they/them)\nHouston, Texas, US\n"},{"id":"421627","messageId":"YHNusqcmwv75P6Ck@camp.crustytoothpaste.net","threadId":"55472","inReplyTo":"87k0p9f2z9.fsf@evledraar.gmail.com","subject":"Re: [PATCH 09/15] hash: set and copy algo field in struct object_id","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-11T21:48:34Z","receivedAt":"2021-04-11T21:48:44Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2021-04-11 at 11:57:30, Ævar Arnfjörð Bjarmason wrote:\n> \n> On Sat, Apr 10 2021, brian m. carlson wrote:\n> \n> >  const struct object_id null_oid;\n> >  static const struct object_id empty_tree_oid = {\n> > -\tEMPTY_TREE_SHA1_BIN_LITERAL\n> > +\tEMPTY_TREE_SHA1_BIN_LITERAL,\n> > +\tGIT_HASH_SHA1,\n> >  };\n> >  static const struct object_id empty_blob_oid = {\n> > -\tEMPTY_BLOB_SHA1_BIN_LITERAL\n> > +\tEMPTY_BLOB_SHA1_BIN_LITERAL,\n> > +\tGIT_HASH_SHA1,\n> >  };\n> >  static const struct object_id empty_tree_oid_sha256 = {\n> > -\tEMPTY_TREE_SHA256_BIN_LITERAL\n> > +\tEMPTY_TREE_SHA256_BIN_LITERAL,\n> > +\tGIT_HASH_SHA256,\n> >  };\n> >  static const struct object_id empty_blob_oid_sha256 = {\n> > -\tEMPTY_BLOB_SHA256_BIN_LITERAL\n> > +\tEMPTY_BLOB_SHA256_BIN_LITERAL,\n> > +\tGIT_HASH_SHA256,\n> >  };\n> \n> In this and some other patches we're continuing to add new fields to\n> structs without using designated initializers.\n> \n> Not a new problem at all, just a note that if you re-roll I for one\n> would very much appreciate starting by migrating over to that. It makes\n> for much easier reading in subsequent patches in this series, and in\n> future ones.\n\nI'm happy to do that.  I thought we were not allowed to use C99 features\nbecause only recent versions of MSVC support modern C.  I was previously\nunder the impression that MSVC didn't support anything but C89, but they\nnow support C11 and C17 in their latest release[0], much to my surprise.\n\nIf we're willing to require C99 features, then I'm happy to add those.\nI'll also send a follow-up series to require C99 support, which I think\nis overdue considering the standard is 22 years old.\n\n[0] https://devblogs.microsoft.com/cppblog/c11-and-c17-standard-support-arriving-in-msvc/\n-- \nbrian m. carlson (he/him or they/them)\nHouston, Texas, US\n"},{"id":"421628","messageId":"YHNvVWyRK3bfS/ZL@camp.crustytoothpaste.net","threadId":"55472","inReplyTo":"xmqqy2dohqau.fsf@gitster.g","subject":"Re: [PATCH 10/15] hash: provide per-algorithm null OIDs","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-11T21:51:17Z","receivedAt":"2021-04-11T21:51:53Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2021-04-11 at 14:03:05, Junio C Hamano wrote:\n> \"brian m. carlson\" <sandals@crustytoothpaste.net> writes:\n> \n> > diff --git a/object-file.c b/object-file.c\n> > index 5f1fa05c4e..50bb5b6ca4 100644\n> > --- a/object-file.c\n> > +++ b/object-file.c\n> > @@ -55,7 +55,6 @@\n> >  \t\"\\x6f\\xe1\\x41\\xf7\\x74\\x91\\x20\\xa3\\x03\\x72\" \\\n> >  \t\"\\x18\\x13\"\n> >  \n> > -const struct object_id null_oid;\n> >  static const struct object_id empty_tree_oid = {\n> >  \tEMPTY_TREE_SHA1_BIN_LITERAL,\n> >  \tGIT_HASH_SHA1,\n> > @@ -64,6 +63,9 @@ static const struct object_id empty_blob_oid = {\n> >  \tEMPTY_BLOB_SHA1_BIN_LITERAL,\n> >  \tGIT_HASH_SHA1,\n> >  };\n> > +const struct object_id null_oid_sha1 = {\n> > +\t{0}, GIT_HASH_SHA1,\n> > +};\n> \n> sparse wants this to be a file-scope static.\n\nCan do.\n-- \nbrian m. carlson (he/him or they/them)\nHouston, Texas, US\n"},{"id":"421630","messageId":"87wnt8eai1.fsf@evledraar.gmail.com","threadId":"55472","inReplyTo":"YHNusqcmwv75P6Ck@camp.crustytoothpaste.net","subject":"Re: [PATCH 09/15] hash: set and copy algo field in struct object_id","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-11T22:12:38Z","receivedAt":"2021-04-11T22:12:46Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, Apr 11 2021, brian m. carlson wrote:\n\n> On 2021-04-11 at 11:57:30, Ævar Arnfjörð Bjarmason wrote:\n>> \n>> On Sat, Apr 10 2021, brian m. carlson wrote:\n>> \n>> >  const struct object_id null_oid;\n>> >  static const struct object_id empty_tree_oid = {\n>> > -\tEMPTY_TREE_SHA1_BIN_LITERAL\n>> > +\tEMPTY_TREE_SHA1_BIN_LITERAL,\n>> > +\tGIT_HASH_SHA1,\n>> >  };\n>> >  static const struct object_id empty_blob_oid = {\n>> > -\tEMPTY_BLOB_SHA1_BIN_LITERAL\n>> > +\tEMPTY_BLOB_SHA1_BIN_LITERAL,\n>> > +\tGIT_HASH_SHA1,\n>> >  };\n>> >  static const struct object_id empty_tree_oid_sha256 = {\n>> > -\tEMPTY_TREE_SHA256_BIN_LITERAL\n>> > +\tEMPTY_TREE_SHA256_BIN_LITERAL,\n>> > +\tGIT_HASH_SHA256,\n>> >  };\n>> >  static const struct object_id empty_blob_oid_sha256 = {\n>> > -\tEMPTY_BLOB_SHA256_BIN_LITERAL\n>> > +\tEMPTY_BLOB_SHA256_BIN_LITERAL,\n>> > +\tGIT_HASH_SHA256,\n>> >  };\n>> \n>> In this and some other patches we're continuing to add new fields to\n>> structs without using designated initializers.\n>> \n>> Not a new problem at all, just a note that if you re-roll I for one\n>> would very much appreciate starting by migrating over to that. It makes\n>> for much easier reading in subsequent patches in this series, and in\n>> future ones.\n>\n> I'm happy to do that.  I thought we were not allowed to use C99 features\n> because only recent versions of MSVC support modern C.  I was previously\n> under the impression that MSVC didn't support anything but C89, but they\n> now support C11 and C17 in their latest release[0], much to my surprise.\n>\n> If we're willing to require C99 features, then I'm happy to add those.\n> I'll also send a follow-up series to require C99 support, which I think\n> is overdue considering the standard is 22 years old.\n>\n> [0] https://devblogs.microsoft.com/cppblog/c11-and-c17-standard-support-arriving-in-msvc/\n\nI don't think we can in general require C99, e.g. I found just the other\nday that our CI's MSVC will fail on %zu (to print size_t without %lu & a\ncast).\n\nBut we can use some subset of C99 features, and happily designated\ninitializers is one of those, see cbc0f81d96f (strbuf: use designated\ninitializers in STRBUF_INIT, 2017-07-10). It's been used all over the\nplace since then.\n\nSee e.g.: git grep -P '^\\s+\\.\\S+ = ' -- '*.[ch]'\n"},{"id":"421634","messageId":"YHOLo36MfuTj6YeD@camp.crustytoothpaste.net","threadId":"55472","inReplyTo":"87wnt8eai1.fsf@evledraar.gmail.com","subject":"Re: [PATCH 09/15] hash: set and copy algo field in struct object_id","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-11T23:52:03Z","receivedAt":"2021-04-11T23:52:42Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2021-04-11 at 22:12:38, Ævar Arnfjörð Bjarmason wrote:\n> \n> On Sun, Apr 11 2021, brian m. carlson wrote:\n> \n> > On 2021-04-11 at 11:57:30, Ævar Arnfjörð Bjarmason wrote:\n> >> \n> >> In this and some other patches we're continuing to add new fields to\n> >> structs without using designated initializers.\n> >> \n> >> Not a new problem at all, just a note that if you re-roll I for one\n> >> would very much appreciate starting by migrating over to that. It makes\n> >> for much easier reading in subsequent patches in this series, and in\n> >> future ones.\n> >\n> > I'm happy to do that.  I thought we were not allowed to use C99 features\n> > because only recent versions of MSVC support modern C.  I was previously\n> > under the impression that MSVC didn't support anything but C89, but they\n> > now support C11 and C17 in their latest release[0], much to my surprise.\n> >\n> > If we're willing to require C99 features, then I'm happy to add those.\n> > I'll also send a follow-up series to require C99 support, which I think\n> > is overdue considering the standard is 22 years old.\n> >\n> > [0] https://devblogs.microsoft.com/cppblog/c11-and-c17-standard-support-arriving-in-msvc/\n> \n> I don't think we can in general require C99, e.g. I found just the other\n> day that our CI's MSVC will fail on %zu (to print size_t without %lu & a\n> cast).\n\nThat's a shame.  I think I'd like to try, though, and ask people to\nupgrade MSVC to a suitable version if we're going to continue to support\nit.  It's not like there aren't alternatives.  So I'm going to send out\nthat series anyway, I think.  That's independent of this series, though,\nso I'll add the designated initializers in v2.\n-- \nbrian m. carlson (he/him or they/them)\nHouston, Texas, US\n"},{"id":"421643","messageId":"xmqqeeffg4fi.fsf@gitster.g","threadId":"55472","inReplyTo":"87wnt8eai1.fsf@evledraar.gmail.com","subject":"Re: [PATCH 09/15] hash: set and copy algo field in struct object_id","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-04-12T10:53:05Z","receivedAt":"2021-04-12T10:53:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> But we can use some subset of C99 features, and happily designated\n> initializers is one of those, see cbc0f81d96f (strbuf: use designated\n> initializers in STRBUF_INIT, 2017-07-10). It's been used all over the\n> place since then.\n\nGood advice to cite a commit that on purpose used a feature and\ndocumented that it is allowed.\n\nAlso see Documentation/CodingGuidelines ;-)  The document should\ngive the authoritative blessing for features allowed to be used (add\nany missing with a proposed patch).\n\nThanks.\n\n"},{"id":"421644","messageId":"patch-2.2-f12e3cad57d-20210412T105422Z-avarab@gmail.com","threadId":"55472","inReplyTo":"cover-0.2-00000000000-20210412T105422Z-avarab@gmail.com","subject":"[PATCH 2/2] C99 support: remove non-HAVE_VARIADIC_MACROS code","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-12T11:02:18Z","receivedAt":"2021-04-12T11:02:32Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove code that depend on HAVE_VARIADIC_MACROS not being set. Since\n765dc168882 (git-compat-util: always enable variadic macros,\n2021-01-28) we've unconditionally defined it to be true, and that\nchange went out with v2.31.0. This should have given packagers enough\ntime to discover whether variadic macros were an issue.\n\nIt seems that they weren't, so let's update the coding guidelines and\nremove all the fallback code for the non-HAVE_VARIADIC_MACROS case.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/CodingGuidelines |  3 ++\n banned.h                       |  5 ---\n git-compat-util.h              | 12 ------\n trace.c                        | 73 ----------------------------------\n trace.h                        | 62 -----------------------------\n trace2.c                       | 39 ------------------\n trace2.h                       | 25 ------------\n usage.c                        | 10 -----\n 8 files changed, 3 insertions(+), 226 deletions(-)\n\ndiff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines\nindex 45465bc0c98..7eafb1758e6 100644\n--- a/Documentation/CodingGuidelines\n+++ b/Documentation/CodingGuidelines\n@@ -205,6 +205,9 @@ For C programs:\n    . since mid 2017 with 512f41cf, we have been using designated\n      initializers for array (e.g. \"int array[10] = { [5] = 2 }\").\n \n+   . since early 2021 with 765dc168882, we have been using variadic\n+     macros, mostly for printf-like trace and debug macros.\n+\n    These used to be forbidden, but we have not heard any breakage\n    report, and they are assumed to be safe.\n \ndiff --git a/banned.h b/banned.h\nindex 7ab4f2e4921..6ccf46bc197 100644\n--- a/banned.h\n+++ b/banned.h\n@@ -21,13 +21,8 @@\n \n #undef sprintf\n #undef vsprintf\n-#ifdef HAVE_VARIADIC_MACROS\n #define sprintf(...) BANNED(sprintf)\n #define vsprintf(...) BANNED(vsprintf)\n-#else\n-#define sprintf(buf,fmt,arg) BANNED(sprintf)\n-#define vsprintf(buf,fmt,arg) BANNED(vsprintf)\n-#endif\n \n #undef gmtime\n #define gmtime(t) BANNED(gmtime)\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 540aba22a4d..da7ab91335f 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -1192,24 +1192,12 @@ static inline int regexec_buf(const regex_t *preg, const char *buf, size_t size,\n #endif\n #endif\n \n-/*\n- * This is always defined as a first step towards making the use of variadic\n- * macros unconditional. If it causes compilation problems on your platform,\n- * please report it to the Git mailing list at git@vger.kernel.org.\n- */\n-#define HAVE_VARIADIC_MACROS 1\n-\n /* usage.c: only to be used for testing BUG() implementation (see test-tool) */\n extern int BUG_exit_code;\n \n-#ifdef HAVE_VARIADIC_MACROS\n __attribute__((format (printf, 3, 4))) NORETURN\n void BUG_fl(const char *file, int line, const char *fmt, ...);\n #define BUG(...) BUG_fl(__FILE__, __LINE__, __VA_ARGS__)\n-#else\n-__attribute__((format (printf, 1, 2))) NORETURN\n-void BUG(const char *fmt, ...);\n-#endif\n \n /*\n  * Preserves errno, prints a message, but gives no warning for ENOENT.\ndiff --git a/trace.c b/trace.c\nindex f726686fd92..43173301f59 100644\n--- a/trace.c\n+++ b/trace.c\n@@ -111,13 +111,11 @@ static int prepare_trace_line(const char *file, int line,\n \tstrbuf_addf(buf, \"%02d:%02d:%02d.%06ld \", tm.tm_hour, tm.tm_min,\n \t\t    tm.tm_sec, (long) tv.tv_usec);\n \n-#ifdef HAVE_VARIADIC_MACROS\n \t/* print file:line */\n \tstrbuf_addf(buf, \"%s:%d \", file, line);\n \t/* align trace output (column 40 catches most files names in git) */\n \twhile (buf->len < 40)\n \t\tstrbuf_addch(buf, ' ');\n-#endif\n \n \treturn 1;\n }\n@@ -229,74 +227,6 @@ static void trace_performance_vprintf_fl(const char *file, int line,\n \tstrbuf_release(&buf);\n }\n \n-#ifndef HAVE_VARIADIC_MACROS\n-\n-void trace_printf(const char *format, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, format);\n-\ttrace_vprintf_fl(NULL, 0, &trace_default_key, format, ap);\n-\tva_end(ap);\n-}\n-\n-void trace_printf_key(struct trace_key *key, const char *format, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, format);\n-\ttrace_vprintf_fl(NULL, 0, key, format, ap);\n-\tva_end(ap);\n-}\n-\n-void trace_argv_printf(const char **argv, const char *format, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, format);\n-\ttrace_argv_vprintf_fl(NULL, 0, argv, format, ap);\n-\tva_end(ap);\n-}\n-\n-void trace_strbuf(struct trace_key *key, const struct strbuf *data)\n-{\n-\ttrace_strbuf_fl(NULL, 0, key, data);\n-}\n-\n-void trace_performance(uint64_t nanos, const char *format, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, format);\n-\ttrace_performance_vprintf_fl(NULL, 0, nanos, format, ap);\n-\tva_end(ap);\n-}\n-\n-void trace_performance_since(uint64_t start, const char *format, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, format);\n-\ttrace_performance_vprintf_fl(NULL, 0, getnanotime() - start,\n-\t\t\t\t     format, ap);\n-\tva_end(ap);\n-}\n-\n-void trace_performance_leave(const char *format, ...)\n-{\n-\tva_list ap;\n-\tuint64_t since;\n-\n-\tif (perf_indent)\n-\t\tperf_indent--;\n-\n-\tif (!format) /* Allow callers to leave without tracing anything */\n-\t\treturn;\n-\n-\tsince = perf_start_times[perf_indent];\n-\tva_start(ap, format);\n-\ttrace_performance_vprintf_fl(NULL, 0, getnanotime() - since,\n-\t\t\t\t     format, ap);\n-\tva_end(ap);\n-}\n-\n-#else\n-\n void trace_printf_key_fl(const char *file, int line, struct trace_key *key,\n \t\t\t const char *format, ...)\n {\n@@ -342,9 +272,6 @@ void trace_performance_leave_fl(const char *file, int line,\n \tva_end(ap);\n }\n \n-#endif /* HAVE_VARIADIC_MACROS */\n-\n-\n static const char *quote_crnl(const char *path)\n {\n \tstatic struct strbuf new_path = STRBUF_INIT;\ndiff --git a/trace.h b/trace.h\nindex 0dbbad0e41c..c6b3f6ce889 100644\n--- a/trace.h\n+++ b/trace.h\n@@ -126,66 +126,6 @@ void trace_command_performance(const char **argv);\n void trace_verbatim(struct trace_key *key, const void *buf, unsigned len);\n uint64_t trace_performance_enter(void);\n \n-#ifndef HAVE_VARIADIC_MACROS\n-\n-/**\n- * Prints a formatted message, similar to printf.\n- */\n-__attribute__((format (printf, 1, 2)))\n-void trace_printf(const char *format, ...);\n-\n-__attribute__((format (printf, 2, 3)))\n-void trace_printf_key(struct trace_key *key, const char *format, ...);\n-\n-/**\n- * Prints a formatted message, followed by a quoted list of arguments.\n- */\n-__attribute__((format (printf, 2, 3)))\n-void trace_argv_printf(const char **argv, const char *format, ...);\n-\n-/**\n- * Prints the strbuf, without additional formatting (i.e. doesn't\n- * choke on `%` or even `\\0`).\n- */\n-void trace_strbuf(struct trace_key *key, const struct strbuf *data);\n-\n-/**\n- * Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled.\n- *\n- * Example:\n- * ------------\n- * uint64_t t = 0;\n- * for (;;) {\n- * \t// ignore\n- * t -= getnanotime();\n- * // code section to measure\n- * t += getnanotime();\n- * // ignore\n- * }\n- * trace_performance(t, \"frotz\");\n- * ------------\n- */\n-__attribute__((format (printf, 2, 3)))\n-void trace_performance(uint64_t nanos, const char *format, ...);\n-\n-/**\n- * Prints elapsed time since 'start' if GIT_TRACE_PERFORMANCE is enabled.\n- *\n- * Example:\n- * ------------\n- * uint64_t start = getnanotime();\n- * // code section to measure\n- * trace_performance_since(start, \"foobar\");\n- * ------------\n- */\n-__attribute__((format (printf, 2, 3)))\n-void trace_performance_since(uint64_t start, const char *format, ...);\n-\n-__attribute__((format (printf, 1, 2)))\n-void trace_performance_leave(const char *format, ...);\n-\n-#else\n-\n /*\n  * Macros to add file:line - see above for C-style declarations of how these\n  * should be used.\n@@ -285,6 +225,4 @@ static inline int trace_pass_fl(struct trace_key *key)\n \treturn key->fd || !key->initialized;\n }\n \n-#endif /* HAVE_VARIADIC_MACROS */\n-\n #endif /* TRACE_H */\ndiff --git a/trace2.c b/trace2.c\nindex 256120c7fd5..51d0e6cbd5e 100644\n--- a/trace2.c\n+++ b/trace2.c\n@@ -597,20 +597,6 @@ void trace2_region_enter_printf_fl(const char *file, int line,\n \tva_end(ap);\n }\n \n-#ifndef HAVE_VARIADIC_MACROS\n-void trace2_region_enter_printf(const char *category, const char *label,\n-\t\t\t\tconst struct repository *repo, const char *fmt,\n-\t\t\t\t...)\n-{\n-\tva_list ap;\n-\n-\tva_start(ap, fmt);\n-\ttrace2_region_enter_printf_va_fl(NULL, 0, category, label, repo, fmt,\n-\t\t\t\t\t ap);\n-\tva_end(ap);\n-}\n-#endif\n-\n void trace2_region_leave_printf_va_fl(const char *file, int line,\n \t\t\t\t      const char *category, const char *label,\n \t\t\t\t      const struct repository *repo,\n@@ -673,20 +659,6 @@ void trace2_region_leave_printf_fl(const char *file, int line,\n \tva_end(ap);\n }\n \n-#ifndef HAVE_VARIADIC_MACROS\n-void trace2_region_leave_printf(const char *category, const char *label,\n-\t\t\t\tconst struct repository *repo, const char *fmt,\n-\t\t\t\t...)\n-{\n-\tva_list ap;\n-\n-\tva_start(ap, fmt);\n-\ttrace2_region_leave_printf_va_fl(NULL, 0, category, label, repo, fmt,\n-\t\t\t\t\t ap);\n-\tva_end(ap);\n-}\n-#endif\n-\n void trace2_data_string_fl(const char *file, int line, const char *category,\n \t\t\t   const struct repository *repo, const char *key,\n \t\t\t   const char *value)\n@@ -782,17 +754,6 @@ void trace2_printf_fl(const char *file, int line, const char *fmt, ...)\n \tva_end(ap);\n }\n \n-#ifndef HAVE_VARIADIC_MACROS\n-void trace2_printf(const char *fmt, ...)\n-{\n-\tva_list ap;\n-\n-\tva_start(ap, fmt);\n-\ttrace2_printf_va_fl(NULL, 0, fmt, ap);\n-\tva_end(ap);\n-}\n-#endif\n-\n const char *trace2_session_id(void)\n {\n \treturn tr2_sid_get();\ndiff --git a/trace2.h b/trace2.h\nindex ede18c2e063..5d85826f23d 100644\n--- a/trace2.h\n+++ b/trace2.h\n@@ -362,18 +362,9 @@ void trace2_region_enter_printf_fl(const char *file, int line,\n \t\t\t\t   const struct repository *repo,\n \t\t\t\t   const char *fmt, ...);\n \n-#ifdef HAVE_VARIADIC_MACROS\n #define trace2_region_enter_printf(category, label, repo, ...)                 \\\n \ttrace2_region_enter_printf_fl(__FILE__, __LINE__, (category), (label), \\\n \t\t\t\t      (repo), __VA_ARGS__)\n-#else\n-/* clang-format off */\n-__attribute__((format (region_enter_printf, 4, 5)))\n-void trace2_region_enter_printf(const char *category, const char *label,\n-\t\t\t\tconst struct repository *repo, const char *fmt,\n-\t\t\t\t...);\n-/* clang-format on */\n-#endif\n \n /**\n  * Emit a 'region_leave' event for <category>.<label> with optional\n@@ -407,18 +398,9 @@ void trace2_region_leave_printf_fl(const char *file, int line,\n \t\t\t\t   const struct repository *repo,\n \t\t\t\t   const char *fmt, ...);\n \n-#ifdef HAVE_VARIADIC_MACROS\n #define trace2_region_leave_printf(category, label, repo, ...)                 \\\n \ttrace2_region_leave_printf_fl(__FILE__, __LINE__, (category), (label), \\\n \t\t\t\t      (repo), __VA_ARGS__)\n-#else\n-/* clang-format off */\n-__attribute__((format (region_leave_printf, 4, 5)))\n-void trace2_region_leave_printf(const char *category, const char *label,\n-\t\t\t\tconst struct repository *repo, const char *fmt,\n-\t\t\t\t...);\n-/* clang-format on */\n-#endif\n \n /**\n  * Emit a key-value pair 'data' event of the form <category>.<key> = <value>.\n@@ -471,14 +453,7 @@ void trace2_printf_va_fl(const char *file, int line, const char *fmt,\n \n void trace2_printf_fl(const char *file, int line, const char *fmt, ...);\n \n-#ifdef HAVE_VARIADIC_MACROS\n #define trace2_printf(...) trace2_printf_fl(__FILE__, __LINE__, __VA_ARGS__)\n-#else\n-/* clang-format off */\n-__attribute__((format (printf, 1, 2)))\n-void trace2_printf(const char *fmt, ...);\n-/* clang-format on */\n-#endif\n \n /*\n  * Optional platform-specific code to dump information about the\ndiff --git a/usage.c b/usage.c\nindex 1b206de36d6..c8022c517e2 100644\n--- a/usage.c\n+++ b/usage.c\n@@ -290,7 +290,6 @@ static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_lis\n \tabort();\n }\n \n-#ifdef HAVE_VARIADIC_MACROS\n NORETURN void BUG_fl(const char *file, int line, const char *fmt, ...)\n {\n \tva_list ap;\n@@ -298,15 +297,6 @@ NORETURN void BUG_fl(const char *file, int line, const char *fmt, ...)\n \tBUG_vfl(file, line, fmt, ap);\n \tva_end(ap);\n }\n-#else\n-NORETURN void BUG(const char *fmt, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, fmt);\n-\tBUG_vfl(NULL, 0, fmt, ap);\n-\tva_end(ap);\n-}\n-#endif\n \n #ifdef SUPPRESS_ANNOTATED_LEAKS\n void unleak_memory(const void *ptr, size_t len)\n-- \n2.31.1.631.gb80e078001e\n\n"},{"id":"421645","messageId":"cover-0.2-00000000000-20210412T105422Z-avarab@gmail.com","threadId":"55472","inReplyTo":"YHOLo36MfuTj6YeD@camp.crustytoothpaste.net","subject":"[PATCH 0/2] C99: harder dependency on variadic macros","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-12T11:02:16Z","receivedAt":"2021-04-12T11:02:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Since [1] which has been out since v2.31.0 we've had a hard dependency\non variadic macros.\n\nRemoving the relevant always-off-unless-you-monkeypatch-the-source\ncode may be too aggressive for Junio's \"Let's give it enough time\"[2].\n\nBut I'm submitting this because of brian m. carlson's note[3] about\nwanting to submit more general patches for declaring a hard dependency\non all of C99.\n\nWhatever anyone thinks of that this harder dependency on C99 variadic\nmacros would be a subset of such a change, so it makes sense to\nconsider it first. Let's see if anyone has an issue with this landing\nbefore brian's suggested larger change.\n\n1. https://lore.kernel.org/git/YBJLgY+CWtS9TeVb@coredump.intra.peff.net/\n2. https://lore.kernel.org/git/xmqq5z3hy4fq.fsf@gitster.c.googlers.com/\n3. https://lore.kernel.org/git/YHOLo36MfuTj6YeD@camp.crustytoothpaste.net/\n\nÆvar Arnfjörð Bjarmason (2):\n  git-compat-util.h: clarify comment on GCC-specific code\n  C99 support: remove non-HAVE_VARIADIC_MACROS code\n\n Documentation/CodingGuidelines |  3 ++\n banned.h                       |  5 ---\n git-compat-util.h              | 25 +++++-------\n trace.c                        | 73 ----------------------------------\n trace.h                        | 62 -----------------------------\n trace2.c                       | 39 ------------------\n trace2.h                       | 25 ------------\n usage.c                        | 10 -----\n 8 files changed, 12 insertions(+), 230 deletions(-)\n\n-- \n2.31.1.631.gb80e078001e\n\n"},{"id":"421646","messageId":"patch-1.2-a8cc05cf56f-20210412T105422Z-avarab@gmail.com","threadId":"55472","inReplyTo":"cover-0.2-00000000000-20210412T105422Z-avarab@gmail.com","subject":"[PATCH 1/2] git-compat-util.h: clarify comment on GCC-specific code","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-12T11:02:17Z","receivedAt":"2021-04-12T11:02:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change a comment added in e208f9cc757 (make error()'s constant return\nvalue more visible, 2012-12-15) to note that the code doesn't only\ndepend on variadic macros, which have been a hard dependency since\n765dc168882 (git-compat-util: always enable variadic macros,\n2021-01-28), but also on GCC's handling of __VA_ARGS__. The commit\nmessage for e208f9cc757 made this clear, but the comment it added did\nnot.\n\nSee also e05bed960d3 (trace: add 'file:line' to all trace output,\n2014-07-12) for another comment about GNUC's handling of __VA_ARGS__.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n git-compat-util.h | 13 +++++++++----\n 1 file changed, 9 insertions(+), 4 deletions(-)\n\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 9ddf9d7044b..540aba22a4d 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -480,10 +480,15 @@ void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));\n \n /*\n  * Let callers be aware of the constant return value; this can help\n- * gcc with -Wuninitialized analysis. We restrict this trick to gcc, though,\n- * because some compilers may not support variadic macros. Since we're only\n- * trying to help gcc, anyway, it's OK; other compilers will fall back to\n- * using the function as usual.\n+ * gcc with -Wuninitialized analysis.\n+ *\n+ * We restrict this trick to gcc, though, because while we rely on the\n+ * presence of C99 variadic macros, this code also relies on the\n+ * non-standard behavior of GCC's __VA_ARGS__, allowing error() to\n+ * work even if no format specifiers are passed to error().\n+ *\n+ * Since we're only trying to help gcc, anyway, it's OK; other\n+ * compilers will fall back to using the function as usual.\n  */\n #if defined(__GNUC__)\n static inline int const_error(void)\n-- \n2.31.1.631.gb80e078001e\n\n"},{"id":"421665","messageId":"87tuobeoxt.fsf@evledraar.gmail.com","threadId":"55472","inReplyTo":"xmqqeeffg4fi.fsf@gitster.g","subject":"Re: [PATCH 09/15] hash: set and copy algo field in struct object_id","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-12T11:13:02Z","receivedAt":"2021-04-12T11:13:06Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Apr 12 2021, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>> But we can use some subset of C99 features, and happily designated\n>> initializers is one of those, see cbc0f81d96f (strbuf: use designated\n>> initializers in STRBUF_INIT, 2017-07-10). It's been used all over the\n>> place since then.\n>\n> Good advice to cite a commit that on purpose used a feature and\n> documented that it is allowed.\n>\n> Also see Documentation/CodingGuidelines ;-)  The document should\n> give the authoritative blessing for features allowed to be used (add\n> any missing with a proposed patch).\n\nOur E-Mails probably crossed, my initial motivation for just-submitted\nhttp://lore.kernel.org/git/cover-0.2-00000000000-20210412T105422Z-avarab@gmail.com\nwas going to CodingGuidelines, and vaguely remembering that there was\nsome other C99 thing that wasn't listed there, and then (re-)discovering\nthe recent variadic macro commit from Jeff King.\n\nAs noted there maybe 2/2 of that is too aggressive, but in that case it\nwould make sense to have a V2 of that which just carved off the\nCodingGuidelines change.\n"},{"id":"421675","messageId":"67645f4b-b320-3ed6-bf5d-552bae0c2688@gmail.com","threadId":"55472","inReplyTo":"cover-0.2-00000000000-20210412T105422Z-avarab@gmail.com","subject":"Re: [PATCH 0/2] C99: harder dependency on variadic macros","fromName":"Bagas Sanjaya","fromEmail":"bagasdotme@gmail.com","sentAt":"2021-04-12T12:14:52Z","receivedAt":"2021-04-12T12:14:58Z","isPatch":true,"sender":{"key":"bagasdotme@gmail.com","avatar":"https://avatars.githubusercontent.com/u/40219486?v=4"},"body":"On 12/04/21 18.02, Ævar Arnfjörð Bjarmason wrote:\n> But I'm submitting this because of brian m. carlson's note[3] about\n> wanting to submit more general patches for declaring a hard dependency\n> on all of C99.\n\nI think we should bump standard requirement to C99, right?\n\n-- \nAn old man doll... just what I always wanted! - Clara\n"},{"id":"421677","messageId":"87r1jfeku8.fsf@evledraar.gmail.com","threadId":"55472","inReplyTo":"67645f4b-b320-3ed6-bf5d-552bae0c2688@gmail.com","subject":"Re: [PATCH 0/2] C99: harder dependency on variadic macros","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-12T12:41:35Z","receivedAt":"2021-04-12T12:41:40Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Apr 12 2021, Bagas Sanjaya wrote:\n\n> On 12/04/21 18.02, Ævar Arnfjörð Bjarmason wrote:\n>> But I'm submitting this because of brian m. carlson's note[3] about\n>> wanting to submit more general patches for declaring a hard dependency\n>> on all of C99.\n>\n> I think we should bump standard requirement to C99, right?\n\nI think that's worth discussing, but isn't the topic of this more narrow\nchange.\n\nAs noted in\nhttp://lore.kernel.org/git/87wnt8eai1.fsf@evledraar.gmail.com if we\nsimply do that some of our MSVC CI will start failing.\n\nI don't know what other compilers we need to support that may support\nour current subset of C99 features, but not the full set, or if e.g. the\nCI can simply have its MSVC compiler version bumped.\n"},{"id":"421743","messageId":"xmqqeeffe669.fsf@gitster.g","threadId":"55472","inReplyTo":"patch-2.2-f12e3cad57d-20210412T105422Z-avarab@gmail.com","subject":"Re: [PATCH 2/2] C99 support: remove non-HAVE_VARIADIC_MACROS code","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-04-12T17:58:22Z","receivedAt":"2021-04-12T17:58:31Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Remove code that depend on HAVE_VARIADIC_MACROS not being set. Since\n> 765dc168882 (git-compat-util: always enable variadic macros,\n> 2021-01-28) we've unconditionally defined it to be true, and that\n> change went out with v2.31.0. This should have given packagers enough\n> time to discover whether variadic macros were an issue.\n\nIt hasn't even been a month since we did v2.31.0.  Since it was not\neven a maintenance release for security update, I have no reason to\nexpect packagers to be all that prompt to react.  And because we\ngave them an escape hatch, they may have used it to update their\ndistro packages and haven't had a chance to tell us about it yet.\n\nSo, the above does not sound like a credible excuse to make our\nfuture work necessary to react to \"our toolchain is not ready yet\"\ncomplaints bigger.  At least not yet.\n\nPlease do not add patches that you know are unnecessary right now to\nthe pile of patches that needs to consume reviewer bandwidth.\n\nThanks.\n"},{"id":"421819","messageId":"YHTQZK5ja8Ypja0l@camp.crustytoothpaste.net","threadId":"55472","inReplyTo":"87r1jfeku8.fsf@evledraar.gmail.com","subject":"Re: [PATCH 0/2] C99: harder dependency on variadic macros","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-12T22:57:40Z","receivedAt":"2021-04-12T22:57:51Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2021-04-12 at 12:41:35, Ævar Arnfjörð Bjarmason wrote:\n> \n> On Mon, Apr 12 2021, Bagas Sanjaya wrote:\n> \n> > On 12/04/21 18.02, Ævar Arnfjörð Bjarmason wrote:\n> >> But I'm submitting this because of brian m. carlson's note[3] about\n> >> wanting to submit more general patches for declaring a hard dependency\n> >> on all of C99.\n> >\n> > I think we should bump standard requirement to C99, right?\n> \n> I think that's worth discussing, but isn't the topic of this more narrow\n> change.\n\nI'm in favor of this more narrow change as well.\n\nJunio's statement that packages may not have had time to update is true,\nbut I also just looked at a variety of packages that run on Linux,\nFreeBSD, and NetBSD (and, since it's pkgsrc, Solaris), and they're all\nupdated.  Usually most open source OS vendors are reasonably prompt\nabout updating their Git versions, at least in the bleeding edge\nrepositories.\n\nIf this works on Windows, it will also work on Unix, because POSIX has\nrequired C99 support since the 2001 revision, and __VA_ARGS__ is C99.\nUnix systems are not the thing preventing us from enabling C99 support\n(or any subset of it) in any meaningful sense.\n\n> As noted in\n> http://lore.kernel.org/git/87wnt8eai1.fsf@evledraar.gmail.com if we\n> simply do that some of our MSVC CI will start failing.\n> \n> I don't know what other compilers we need to support that may support\n> our current subset of C99 features, but not the full set, or if e.g. the\n> CI can simply have its MSVC compiler version bumped.\n\nI'm looking at fixing our CI before I send in my series.  My series,\nwhen it comes in, will have a green CI status because I do want to be\nsure that we're providing a supported environment for MSVC wherever\nthat's possible.\n-- \nbrian m. carlson (he/him or they/them)\nHouston, Texas, US\n"},{"id":"421823","messageId":"xmqqlf9nm6pv.fsf@gitster.g","threadId":"55472","inReplyTo":"YHTQZK5ja8Ypja0l@camp.crustytoothpaste.net","subject":"Re: [PATCH 0/2] C99: harder dependency on variadic macros","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-04-12T23:19:24Z","receivedAt":"2021-04-12T23:19:29Z","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> If this works on Windows, it will also work on Unix, because POSIX has\n> required C99 support since the 2001 revision, and __VA_ARGS__ is C99.\n> Unix systems are not the thing preventing us from enabling C99 support\n> (or any subset of it) in any meaningful sense.\n\nYeah, among the list (semi-)regulars, the only ones that may likely\nto be broken is the NonStop folks.  Hopefully they are aware of this\ndiscussion?\n\nhttps://lore.kernel.org/git/xmqqeeffe669.fsf@gitster.g/\n\n"},{"id":"421847","messageId":"YHVPAGVDjS1EH6L3@coredump.intra.peff.net","threadId":"55472","inReplyTo":"patch-1.2-a8cc05cf56f-20210412T105422Z-avarab@gmail.com","subject":"Re: [PATCH 1/2] git-compat-util.h: clarify comment on GCC-specific code","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-04-13T07:57:52Z","receivedAt":"2021-04-13T07:57:55Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Apr 12, 2021 at 01:02:17PM +0200, Ævar Arnfjörð Bjarmason wrote:\n\n> diff --git a/git-compat-util.h b/git-compat-util.h\n> index 9ddf9d7044b..540aba22a4d 100644\n> --- a/git-compat-util.h\n> +++ b/git-compat-util.h\n> @@ -480,10 +480,15 @@ void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));\n>  \n>  /*\n>   * Let callers be aware of the constant return value; this can help\n> - * gcc with -Wuninitialized analysis. We restrict this trick to gcc, though,\n> - * because some compilers may not support variadic macros. Since we're only\n> - * trying to help gcc, anyway, it's OK; other compilers will fall back to\n> - * using the function as usual.\n> + * gcc with -Wuninitialized analysis.\n> + *\n> + * We restrict this trick to gcc, though, because while we rely on the\n> + * presence of C99 variadic macros, this code also relies on the\n> + * non-standard behavior of GCC's __VA_ARGS__, allowing error() to\n> + * work even if no format specifiers are passed to error().\n> + *\n> + * Since we're only trying to help gcc, anyway, it's OK; other\n> + * compilers will fall back to using the function as usual.\n>   */\n>  #if defined(__GNUC__)\n\nI don't mind leaving this gcc-only, since as you note that's the point\nof what the code is trying to do. But wouldn't this always work because\nwe know there is at least one arg (the format itself)?\n\nI.e., if we had written:\n\n  #define error(fmt, ...) (error(fmt, __VA_ARGS__), const_error())\n\nthat would be a problem for:\n\n  error(\"foo\");\n\nBut because we wrote:\n\n  #define error(...) (error(__VA_ARGS__), const_error())\n\nthen it's OK.\n\n-Peff\n"},{"id":"421848","messageId":"YHVPurTYDUE9XHN7@coredump.intra.peff.net","threadId":"55472","inReplyTo":"xmqqeeffe669.fsf@gitster.g","subject":"Re: [PATCH 2/2] C99 support: remove non-HAVE_VARIADIC_MACROS code","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-04-13T08:00:58Z","receivedAt":"2021-04-13T08:01:02Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Apr 12, 2021 at 10:58:22AM -0700, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n> \n> > Remove code that depend on HAVE_VARIADIC_MACROS not being set. Since\n> > 765dc168882 (git-compat-util: always enable variadic macros,\n> > 2021-01-28) we've unconditionally defined it to be true, and that\n> > change went out with v2.31.0. This should have given packagers enough\n> > time to discover whether variadic macros were an issue.\n> \n> It hasn't even been a month since we did v2.31.0.  Since it was not\n> even a maintenance release for security update, I have no reason to\n> expect packagers to be all that prompt to react.  And because we\n> gave them an escape hatch, they may have used it to update their\n> distro packages and haven't had a chance to tell us about it yet.\n> \n> So, the above does not sound like a credible excuse to make our\n> future work necessary to react to \"our toolchain is not ready yet\"\n> complaints bigger.  At least not yet.\n\nYeah, the whole idea of the change in v2.31.0 was to change as little as\npossible for the weather-balloon. I agree that waiting longer to see the\nresults of our test makes sense, unless there is a pressing need.\n\nIf we had some new use case that was going to have to _add_ workarounds\nfor platforms without variadic macros, I'd be more inclined to think\nabout that timetable versus how much work it would be to add those\nworkarounds. But since there isn't one, it seems there is little cost to\nwaiting longer.\n\n-Peff\n"},{"id":"421882","messageId":"38f48634-077b-6351-5285-f8ecc6f552d0@gmail.com","threadId":"55472","inReplyTo":"20210410152140.3525040-4-sandals@crustytoothpaste.net","subject":"Re: [PATCH 03/15] cache: add an algo member to struct object_id","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2021-04-13T12:12:21Z","receivedAt":"2021-04-13T12:12:25Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 4/10/2021 11:21 AM, brian m. carlson wrote:\n> Now that we're working with multiple hash algorithms in the same repo,\n> it's best if we label each object ID with its algorithm so we can\n> determine how to format a given object ID. Add a member called algo to\n> struct object_id.\n> \n> Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n> ---\n>  hash.h | 1 +\n>  1 file changed, 1 insertion(+)\n> \n> diff --git a/hash.h b/hash.h\n> index 3fb0c3d400..dafdcb3335 100644\n> --- a/hash.h\n> +++ b/hash.h\n> @@ -181,6 +181,7 @@ static inline int hash_algo_by_ptr(const struct git_hash_algo *p)\n>  \n>  struct object_id {\n>  \tunsigned char hash[GIT_MAX_RAWSZ];\n> +\tint algo;\n>  };\n\nWhat are the performance implications of adding this single bit\n(that actually costs us 4 to 8 bytes, based on alignment)? Later\nin the series you add longer hash comparisons, too. These seem\nlike they will affect performance for existing SHA-1 repos, and\nit would be nice to know how much we are paying for this support.\n\nI assume that we already checked what happened when GIT_MAX_RAWSZ\nincreased, but that seemed worth the cost so we could have SHA-256\nat all. I find the justification for this interoperability mode to\nbe less significant, and potentially adding too much of a tax onto\nboth SHA-1 repos that will never upgrade, and SHA-256 repos that\nupgrade all at once (or start as SHA-256).\n\nOf course, if there truly is no serious performance implication to\nthis change, then I support following the transition plan and\nallowing us to be flexible on timelines for interoperability. It\njust seems like we need to investigate what this will cost.\n\nThanks,\n-Stolee\n"},{"id":"421932","messageId":"xmqqblahlwqm.fsf@gitster.g","threadId":"55472","inReplyTo":"YHVPAGVDjS1EH6L3@coredump.intra.peff.net","subject":"Re: [PATCH 1/2] git-compat-util.h: clarify comment on GCC-specific code","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-04-13T21:07:13Z","receivedAt":"2021-04-13T21:07:15Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jeff King <peff@peff.net> writes:\n\n>> + * We restrict this trick to gcc, though, because while we rely on the\n>> + * presence of C99 variadic macros, this code also relies on the\n>> + * non-standard behavior of GCC's __VA_ARGS__, allowing error() to\n>> + * work even if no format specifiers are passed to error().\n\nThe last part of this comment is puzzlling.  Do we ever call error()\nwithout any format specifier?  There may be GCC-ism behaviour around\nthe __VA_ARGS__ stuff, but are we relying on that GCC-ism?\n\n>> + * Since we're only trying to help gcc, anyway, it's OK; other\n>> + * compilers will fall back to using the function as usual.\n>>   */\n>>  #if defined(__GNUC__)\n>\n> I don't mind leaving this gcc-only, since as you note that's the point\n> of what the code is trying to do. But wouldn't this always work because\n> we know there is at least one arg (the format itself)?\n>\n> I.e., if we had written:\n>\n>   #define error(fmt, ...) (error(fmt, __VA_ARGS__), const_error())\n>\n> that would be a problem for:\n>\n>   error(\"foo\");\n>\n> But because we wrote:\n>\n>   #define error(...) (error(__VA_ARGS__), const_error())\n>\n> then it's OK.\n\nI think so.  At least I find the new comment confusing, and I'd\nprefer to see it cleaned up.\n\nThanks.\n\n"},{"id":"421942","messageId":"YHZApIpPBhpmwscP@camp.crustytoothpaste.net","threadId":"55472","inReplyTo":"38f48634-077b-6351-5285-f8ecc6f552d0@gmail.com","subject":"Re: [PATCH 03/15] cache: add an algo member to struct object_id","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-14T01:08:52Z","receivedAt":"2021-04-14T01:09:35Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2021-04-13 at 12:12:21, Derrick Stolee wrote:\n> On 4/10/2021 11:21 AM, brian m. carlson wrote:\n> > Now that we're working with multiple hash algorithms in the same repo,\n> > it's best if we label each object ID with its algorithm so we can\n> > determine how to format a given object ID. Add a member called algo to\n> > struct object_id.\n> > \n> > Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n> > ---\n> >  hash.h | 1 +\n> >  1 file changed, 1 insertion(+)\n> > \n> > diff --git a/hash.h b/hash.h\n> > index 3fb0c3d400..dafdcb3335 100644\n> > --- a/hash.h\n> > +++ b/hash.h\n> > @@ -181,6 +181,7 @@ static inline int hash_algo_by_ptr(const struct git_hash_algo *p)\n> >  \n> >  struct object_id {\n> >  \tunsigned char hash[GIT_MAX_RAWSZ];\n> > +\tint algo;\n> >  };\n> \n> What are the performance implications of adding this single bit\n> (that actually costs us 4 to 8 bytes, based on alignment)? Later\n> in the series you add longer hash comparisons, too. These seem\n> like they will affect performance for existing SHA-1 repos, and\n> it would be nice to know how much we are paying for this support.\n\nI will do some performance numbers on these patches, but it will likely\nbe the weekend before I can get to it.  I think this will add 4 bytes on\nmost platforms, since int is typically 32 bits, and the alignment\nrequirement would be for the most strictly aligned member, which is the\nint, so a 4-byte alignment.  I don't think the alignment requirements\nare especially onerous here.\n\n> I assume that we already checked what happened when GIT_MAX_RAWSZ\n> increased, but that seemed worth the cost so we could have SHA-256\n> at all. I find the justification for this interoperability mode to\n> be less significant, and potentially adding too much of a tax onto\n> both SHA-1 repos that will never upgrade, and SHA-256 repos that\n> upgrade all at once (or start as SHA-256).\n\nThe entire goal of the interoperability is to let people seamlessly and\ntransparently move from SHA-1 to SHA-256.  Currently, the only way\npeople can move a SHA-1 repository to a SHA-256 repository is with\nfast-import and fast-export, which loses all digital signatures and tags\nto blobs.  This also requires a flag day.\n\nSHA-1 can now be attacked for USD 45,000.  That means it is within the\nbudget of a dedicated professional and virtually all medium or large\ncorporations, including even most municipal governments, to create a\nSHA-1 collision.  Unfortunately, the way we deal with this is to die, so\nas soon as this happens, the repository fails closed.  While an attacker\ncannot make use of the collisions to spread malicious objects, because\nof the way Git works, they can effectively DoS a repository, which is in\nitself a security issue.  Fixing this requires major surgery.\n\nWe need the interoperability code to let people transition their\nrepositories away from SHA-1, even if it has some performance impact,\nbecause without that most SHA-1 repositories will never transition.\nThat's what's outlined in the transition plan, and why that approach was\nproposed, even though it would be nicer to avoid having to implement it\nat all.\n\nI will endeavor to make the performance impact as small as possible, of\ncourse, and ideally there will be none.  I am sensitive to the fact that\npeople do run absurdly large workloads on Git, as we both know, and I do\nwant to support that.\n-- \nbrian m. carlson (he/him or they/them)\nHouston, Texas, US\n"},{"id":"421946","messageId":"YHZ72NVfEUXZmBoz@coredump.intra.peff.net","threadId":"55472","inReplyTo":"xmqqblahlwqm.fsf@gitster.g","subject":"Re: [PATCH 1/2] git-compat-util.h: clarify comment on GCC-specific code","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-04-14T05:21:28Z","receivedAt":"2021-04-14T05:21:36Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Tue, Apr 13, 2021 at 02:07:13PM -0700, Junio C Hamano wrote:\n\n> Jeff King <peff@peff.net> writes:\n> \n> >> + * We restrict this trick to gcc, though, because while we rely on the\n> >> + * presence of C99 variadic macros, this code also relies on the\n> >> + * non-standard behavior of GCC's __VA_ARGS__, allowing error() to\n> >> + * work even if no format specifiers are passed to error().\n> \n> The last part of this comment is puzzlling.  Do we ever call error()\n> without any format specifier?  There may be GCC-ism behaviour around\n> the __VA_ARGS__ stuff, but are we relying on that GCC-ism?\n\nI took \"format specifier\" to mean the \"%\" code within the format. E.g.:\n\n  error(\"foo\");\n\nhas no format specifier, and thus no arguments after the format. But\nevery call will have at least the format string itself.\n\nAFAIK, portably using variadic macros means you need there to always be\nat least one argument. Hence \"error(fmt, ...)\" is wrong (the \"...\" may\nhave no arguments) but \"error(...)\" is OK (you always have a format\nstring). I'm not sure if Ævar knows about some other portability gotcha,\nor if he just didn't realize that this was written in the portable way.\n\n-Peff\n"},{"id":"421949","messageId":"877dl52y3x.fsf@evledraar.gmail.com","threadId":"55472","inReplyTo":"YHZ72NVfEUXZmBoz@coredump.intra.peff.net","subject":"Re: [PATCH 1/2] git-compat-util.h: clarify comment on GCC-specific code","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-14T06:12:34Z","receivedAt":"2021-04-14T06:12:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Apr 14 2021, Jeff King wrote:\n\n> On Tue, Apr 13, 2021 at 02:07:13PM -0700, Junio C Hamano wrote:\n>\n>> Jeff King <peff@peff.net> writes:\n>> \n>> >> + * We restrict this trick to gcc, though, because while we rely on the\n>> >> + * presence of C99 variadic macros, this code also relies on the\n>> >> + * non-standard behavior of GCC's __VA_ARGS__, allowing error() to\n>> >> + * work even if no format specifiers are passed to error().\n>> \n>> The last part of this comment is puzzlling.  Do we ever call error()\n>> without any format specifier?  There may be GCC-ism behaviour around\n>> the __VA_ARGS__ stuff, but are we relying on that GCC-ism?\n>\n> I took \"format specifier\" to mean the \"%\" code within the format. E.g.:\n>\n>   error(\"foo\");\n>\n> has no format specifier, and thus no arguments after the format. But\n> every call will have at least the format string itself.\n>\n> AFAIK, portably using variadic macros means you need there to always be\n> at least one argument. Hence \"error(fmt, ...)\" is wrong (the \"...\" may\n> have no arguments) but \"error(...)\" is OK (you always have a format\n> string). I'm not sure if Ævar knows about some other portability gotcha,\n> or if he just didn't realize that this was written in the portable way.\n\nNo, I just read elsewhere that GCC had non-standard behavior, and didn't\nlook carefully at your implementation, but since it explicitly depended\non GNUC etc. understood it to mean it was GCC-specific, not just\nC99-specific.\n\nSo it can simply be changed to depend on HAVE_VARIADIC_MACROS instead?\n"},{"id":"421954","messageId":"YHaaOoE8/BiVKhKt@coredump.intra.peff.net","threadId":"55472","inReplyTo":"877dl52y3x.fsf@evledraar.gmail.com","subject":"Re: [PATCH 1/2] git-compat-util.h: clarify comment on GCC-specific code","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2021-04-14T07:31:06Z","receivedAt":"2021-04-14T07:31:10Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Apr 14, 2021 at 08:12:34AM +0200, Ævar Arnfjörð Bjarmason wrote:\n\n> \n> On Wed, Apr 14 2021, Jeff King wrote:\n> \n> > On Tue, Apr 13, 2021 at 02:07:13PM -0700, Junio C Hamano wrote:\n> >\n> >> Jeff King <peff@peff.net> writes:\n> >> \n> >> >> + * We restrict this trick to gcc, though, because while we rely on the\n> >> >> + * presence of C99 variadic macros, this code also relies on the\n> >> >> + * non-standard behavior of GCC's __VA_ARGS__, allowing error() to\n> >> >> + * work even if no format specifiers are passed to error().\n> >> \n> >> The last part of this comment is puzzlling.  Do we ever call error()\n> >> without any format specifier?  There may be GCC-ism behaviour around\n> >> the __VA_ARGS__ stuff, but are we relying on that GCC-ism?\n> >\n> > I took \"format specifier\" to mean the \"%\" code within the format. E.g.:\n> >\n> >   error(\"foo\");\n> >\n> > has no format specifier, and thus no arguments after the format. But\n> > every call will have at least the format string itself.\n> >\n> > AFAIK, portably using variadic macros means you need there to always be\n> > at least one argument. Hence \"error(fmt, ...)\" is wrong (the \"...\" may\n> > have no arguments) but \"error(...)\" is OK (you always have a format\n> > string). I'm not sure if Ævar knows about some other portability gotcha,\n> > or if he just didn't realize that this was written in the portable way.\n> \n> No, I just read elsewhere that GCC had non-standard behavior, and didn't\n> look carefully at your implementation, but since it explicitly depended\n> on GNUC etc. understood it to mean it was GCC-specific, not just\n> C99-specific.\n> \n> So it can simply be changed to depend on HAVE_VARIADIC_MACROS instead?\n\nI think it probably could be, yes.\n\nThe original predates HAVE_VARIADIC_MACROS (which we got in 2014); the\noriginal error() macro is from e208f9cc75 (make error()'s constant\nreturn value more visible, 2012-12-15).\n\nThe original also used the gcc-ism with the paste operator (see the\ncommit message for mention of it), but that was actually dropped later\nby 9798f7e5f9 (Use __VA_ARGS__ for all of error's arguments,\n2013-02-08), giving us the C99-portable version we have now.\n\nAll that said, I don't see much value in converting it to use\nHAVE_VARIADIC_MACROS. It is mostly there to benefit gcc's warning code.\n\nIn theory making the return value visible could help other compilers\ngenerate better code, too. If we care about doing that, we could switch\nto making it unconditional. But aside from the variadic macros, it's\nkind of a weird construct and I'd be worried about generating warnings\non other compilers or static analysis systems. E.g., see the hack from\n87fe5df365 (inline constant return from error() function, 2014-05-06).\n\n-Peff\n"},{"id":"422006","messageId":"87pmyw0waj.fsf@evledraar.gmail.com","threadId":"55472","inReplyTo":"YHZApIpPBhpmwscP@camp.crustytoothpaste.net","subject":"Re: [PATCH 03/15] cache: add an algo member to struct object_id","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-15T08:47:00Z","receivedAt":"2021-04-15T08:47:05Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Apr 14 2021, brian m. carlson wrote:\n\n> On 2021-04-13 at 12:12:21, Derrick Stolee wrote:\n>> On 4/10/2021 11:21 AM, brian m. carlson wrote:\n>> > Now that we're working with multiple hash algorithms in the same repo,\n>> > it's best if we label each object ID with its algorithm so we can\n>> > determine how to format a given object ID. Add a member called algo to\n>> > struct object_id.\n>> > \n>> > Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n>> > ---\n>> >  hash.h | 1 +\n>> >  1 file changed, 1 insertion(+)\n>> > \n>> > diff --git a/hash.h b/hash.h\n>> > index 3fb0c3d400..dafdcb3335 100644\n>> > --- a/hash.h\n>> > +++ b/hash.h\n>> > @@ -181,6 +181,7 @@ static inline int hash_algo_by_ptr(const struct git_hash_algo *p)\n>> >  \n>> >  struct object_id {\n>> >  \tunsigned char hash[GIT_MAX_RAWSZ];\n>> > +\tint algo;\n>> >  };\n>> \n>> What are the performance implications of adding this single bit\n>> (that actually costs us 4 to 8 bytes, based on alignment)? Later\n>> in the series you add longer hash comparisons, too. These seem\n>> like they will affect performance for existing SHA-1 repos, and\n>> it would be nice to know how much we are paying for this support.\n>\n> I will do some performance numbers on these patches, but it will likely\n> be the weekend before I can get to it.  I think this will add 4 bytes on\n> most platforms, since int is typically 32 bits, and the alignment\n> requirement would be for the most strictly aligned member, which is the\n> int, so a 4-byte alignment.  I don't think the alignment requirements\n> are especially onerous here.\n\nI think if you're doing such a perf test one where we have SHA-1 mode\nwith SHA-1 length OID v.s. SHA-256 (the current behavior) would be\ninteresting as well.\n\nIt seems like good SHA1s to test that are 5a0cc8aca79 and\n13eeedb5d17. Running:\n\n    GIT_PERF_REPEAT_COUNT=10 \\\n    GIT_SKIP_TESTS=\"p0001.[3-9] p1450.2\" \\\n    GIT_TEST_OPTS= GIT_PERF_MAKE_OPTS='-j8 CFLAGS=-O3' \\\n    ./run 5a0cc8aca79 13eeedb5d17 -- p0001-rev-list.sh p1450-fsck.sh\n\n(I added a fsck --connectivity-only test)\n\nGives us:\n\n    Test                               5a0cc8aca79         13eeedb5d17            \n    ------------------------------------------------------------------------------\n    0001.1: rev-list --all             2.46(2.22+0.14)     2.48(2.25+0.14) +0.8%  \n    0001.2: rev-list --all --objects   10.79(10.22+0.14)   10.92(10.24+0.20) +1.2%\n    1450.1: fsck --connectivity-only   16.61(15.42+0.34)   16.94(15.60+0.32) +2.0%\n\nSo at least on my box none of those are outside of the confidence\nintervals. This was against my copy of git.git. Perhaps it matters more\nunder memory pressure.\n\n>> I assume that we already checked what happened when GIT_MAX_RAWSZ\n>> increased, but that seemed worth the cost so we could have SHA-256\n>> at all. I find the justification for this interoperability mode to\n>> be less significant, and potentially adding too much of a tax onto\n>> both SHA-1 repos that will never upgrade, and SHA-256 repos that\n>> upgrade all at once (or start as SHA-256).\n>\n> The entire goal of the interoperability is to let people seamlessly and\n> transparently move from SHA-1 to SHA-256.  Currently, the only way\n> people can move a SHA-1 repository to a SHA-256 repository is with\n> fast-import and fast-export, which loses all digital signatures and tags\n> to blobs.  This also requires a flag day.\n>\n> SHA-1 can now be attacked for USD 45,000.  That means it is within the\n> budget of a dedicated professional and virtually all medium or large\n> corporations, including even most municipal governments, to create a\n> SHA-1 collision.\n\nIs that for vanilla SHA-1, or SHA-1DC?\n\n> Unfortunately, the way we deal with this is to die, so\n> as soon as this happens, the repository fails closed.  While an attacker\n> cannot make use of the collisions to spread malicious objects, because\n> of the way Git works, they can effectively DoS a repository, which is in\n> itself a security issue.  Fixing this requires major surgery.\n\nCan you elaborate on this? I believe that we die on any known collision\nvia the SHA1-DC code, and even if we didn't have that we'd detect the\ncollision (see [1] for the code) and die while the object is in the\ntemporary quarantine.\n\nI believe such a request is cheaper to serve than one that doesn't\nupload colliding objects, we die earlier (less CPU etc.), and don't add\nobjects to the store.\n\nSo what's the DoS vector?\n\n> We need the interoperability code to let people transition their\n> repositories away from SHA-1, even if it has some performance impact,\n> because without that most SHA-1 repositories will never transition.\n> That's what's outlined in the transition plan, and why that approach was\n> proposed, even though it would be nicer to avoid having to implement it\n> at all.\n\nThere's no question that we need working interop.\n\nThe question at least in my mind is why that interop is happening by\nannotating every object held in memory with whether they're SHA-1 or\nSHA-256, as opposed to having some translation layer earlier in the\nchain.\n\nNot all our file or in-memory structures are are like that, e.g. the\ncommit graph has a header saying \"this is a bunch of SHA-1/256\", and the\nobjects that follow are padded to that actual hash size, not the max\nsize we know about.\n\nMy understanding of the transition plan was that we'd e.g. have a\nSHA-1<->SHA-256 mapping of objects, which we'd say use to push/pull.\n\nBut that by the time I ran say a \"git commit\" none of that machinery\nneeded to care that I was interoping with a SHA-1 repo on the other end.\nIt would just happily have all SHA-256 objects, create new ones, and\nonly by the time I needed to push them would something kick in to\nre-hash them.\n\nI *think* the anwer is just \"it's easier on the C-level\" and \"the\nwastage doesn't seem to matter much\", which is fair enough.\n\n*Goes and digs in the ML archive*:\n\n    https://lore.kernel.org/git/1399147942-165308-1-git-send-email-sandals@crustytoothpaste.net/#t\n    https://lore.kernel.org/git/55016A3A.6010100@alum.mit.edu/\n\nTo answer (some) of that myself:\n\nDigging up some of the initial discussion that seems to be the case, at\nthat point there was a suggestion of:\n\n    struct object_id {\n        unsigned char hash_type;\n        union {\n            unsigned char sha1[GIT_SHA1_RAWSZ];\n            unsigned char sha256[GIT_SHA256_RAWSZ];\n        } hash;\n    };\n\nTo which you replied:\n    \n    What I think might be more beneficial is to make the hash function\n    specific to a particular repository, and therefore you could maintain\n    something like this:\n    \n      struct object_id {\n          unsigned char hash[GIT_MAX_RAWSZ];\n      };\n\nIt wouldn't matter for the memory use to make it a union, but my reading\nof the above is that the reason for the current object_id not-a-union\nstructure might not be valid now that there's a \"hash_type\" member, no?\n\n> I will endeavor to make the performance impact as small as possible, of\n> course, and ideally there will be none.  I am sensitive to the fact that\n> people do run absurdly large workloads on Git, as we both know, and I do\n> want to support that.\n\nAll of the above being said I do wonder if for those who worry about\nhash size inflating their object store whether a more sensible end-goal\nif that's an issue wouldn't be to store abbreviated hashes.\n\nAs long as you'd re-hash/inflate the size in the case of collisions\n(which would be a more expensive check at the \"fetch\" boundary) you\ncould do so safely, and the result would be less memory consumption.\n\nBut maybe it's just a non-issue :)\n\n1. https://lore.kernel.org/git/20181113201910.11518-1-avarab@gmail.com/\n"},{"id":"422009","messageId":"YHf/mPIyHr+S2Fpy@generichostname","threadId":"55472","inReplyTo":"20210410152140.3525040-2-sandals@crustytoothpaste.net","subject":"Re: [PATCH 01/15] sha1-file: allow hashing objects literally with any algorithm","fromName":"Denton Liu","fromEmail":"liu.denton@gmail.com","sentAt":"2021-04-15T08:55:52Z","receivedAt":"2021-04-15T08:55:57Z","isPatch":true,"sender":{"key":"liu.denton@gmail.com","avatar":"https://avatars.githubusercontent.com/u/9620836?v=4"},"body":"Hi brian,\n\n> Subject: [PATCH 01/15] sha1-file: allow hashing objects literally with any algorithm\n\ns/sha1-file/object-file/\n\nI can see that you've waited a while to send this series ;)\n\nOn Sat, Apr 10, 2021 at 03:21:26PM +0000, brian m. carlson wrote:\n> In order to perform suitable testing with multiple algorithms and\n> interoperability, we'll need the ability to hash an object with a given\n> algorithm. Introduce this capability for now only for objects which are\n> hashed literally by adding a function which does this and changing a\n> static function to accept an algorithm pointer.\n> \n> Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n> ---\n>  object-file.c  | 16 ++++++++++++++--\n>  object-store.h |  3 +++\n>  2 files changed, 17 insertions(+), 2 deletions(-)\n"},{"id":"422072","messageId":"YHjGWifub3o5Ynj6@camp.crustytoothpaste.net","threadId":"55472","inReplyTo":"YHf/mPIyHr+S2Fpy@generichostname","subject":"Re: [PATCH 01/15] sha1-file: allow hashing objects literally with any algorithm","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-15T23:03:54Z","receivedAt":"2021-04-15T23:04:32Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2021-04-15 at 08:55:52, Denton Liu wrote:\n> Hi brian,\n> \n> > Subject: [PATCH 01/15] sha1-file: allow hashing objects literally with any algorithm\n> \n> s/sha1-file/object-file/\n\nGood point.  Will fix.\n\n> I can see that you've waited a while to send this series ;)\n\nYes, I started writing this series after I finished the origin SHA-256\nseries.  That had been completed for some time, but it took time to send\nout all the patches, so it's been sitting in my repository for a while.\n\nI had hoped that things would be a little simpler than they had been and\nI could have been finished by now, but I still have some 150 tests to\nfix, so I decided to send out some initial patches to keep things\nmoving.\n-- \nbrian m. carlson (he/him or they/them)\nHouston, Texas, US\n"},{"id":"422074","messageId":"YHjReOSYlTF8XZL9@camp.crustytoothpaste.net","threadId":"55472","inReplyTo":"87pmyw0waj.fsf@evledraar.gmail.com","subject":"Re: [PATCH 03/15] cache: add an algo member to struct object_id","fromName":"brian m. carlson","fromEmail":"sandals@crustytoothpaste.net","sentAt":"2021-04-15T23:51:20Z","receivedAt":"2021-04-15T23:51:58Z","isPatch":true,"sender":{"key":"sandals@crustytoothpaste.net","avatar":"https://avatars.githubusercontent.com/u/497054?v=4"},"body":"On 2021-04-15 at 08:47:00, Ævar Arnfjörð Bjarmason wrote:\n> \n> On Wed, Apr 14 2021, brian m. carlson wrote:\n> > I will do some performance numbers on these patches, but it will likely\n> > be the weekend before I can get to it.  I think this will add 4 bytes on\n> > most platforms, since int is typically 32 bits, and the alignment\n> > requirement would be for the most strictly aligned member, which is the\n> > int, so a 4-byte alignment.  I don't think the alignment requirements\n> > are especially onerous here.\n> \n> I think if you're doing such a perf test one where we have SHA-1 mode\n> with SHA-1 length OID v.s. SHA-256 (the current behavior) would be\n> interesting as well.\n> \n> It seems like good SHA1s to test that are 5a0cc8aca79 and\n> 13eeedb5d17. Running:\n> \n>     GIT_PERF_REPEAT_COUNT=10 \\\n>     GIT_SKIP_TESTS=\"p0001.[3-9] p1450.2\" \\\n>     GIT_TEST_OPTS= GIT_PERF_MAKE_OPTS='-j8 CFLAGS=-O3' \\\n>     ./run 5a0cc8aca79 13eeedb5d17 -- p0001-rev-list.sh p1450-fsck.sh\n> \n> (I added a fsck --connectivity-only test)\n> \n> Gives us:\n> \n>     Test                               5a0cc8aca79         13eeedb5d17\n>     ------------------------------------------------------------------------------\n>     0001.1: rev-list --all             2.46(2.22+0.14)     2.48(2.25+0.14) +0.8%\n>     0001.2: rev-list --all --objects   10.79(10.22+0.14)   10.92(10.24+0.20) +1.2%\n>     1450.1: fsck --connectivity-only   16.61(15.42+0.34)   16.94(15.60+0.32) +2.0%\n> \n> So at least on my box none of those are outside of the confidence\n> intervals. This was against my copy of git.git. Perhaps it matters more\n> under memory pressure.\n\nI do plan to take a deeper look at this this weekend and do some\nperformance numbers, and I think these are great examples to use.  I\njust have a limited amount of time most weeknights because, among other\nthings, I am taking French a couple nights a week.\n\nI talked with Stolee today about this approach and the desire for\nperformance, so I think we're on the same page about trying to make this\nas fast as possible.  I plan to try a couple alternative solutions which\nmay work as well (or at least, I will make notes this time about why\nthey didn't work) and be less impactful.\n\n> >> I assume that we already checked what happened when GIT_MAX_RAWSZ\n> >> increased, but that seemed worth the cost so we could have SHA-256\n> >> at all. I find the justification for this interoperability mode to\n> >> be less significant, and potentially adding too much of a tax onto\n> >> both SHA-1 repos that will never upgrade, and SHA-256 repos that\n> >> upgrade all at once (or start as SHA-256).\n> >\n> > The entire goal of the interoperability is to let people seamlessly and\n> > transparently move from SHA-1 to SHA-256.  Currently, the only way\n> > people can move a SHA-1 repository to a SHA-256 repository is with\n> > fast-import and fast-export, which loses all digital signatures and tags\n> > to blobs.  This also requires a flag day.\n> >\n> > SHA-1 can now be attacked for USD 45,000.  That means it is within the\n> > budget of a dedicated professional and virtually all medium or large\n> > corporations, including even most municipal governments, to create a\n> > SHA-1 collision.\n> \n> Is that for vanilla SHA-1, or SHA-1DC?\n\nWell, for SHA-1 in general.  SHA-1DC doesn't do anything except detect\ncollisions.  People can still create collisions, but we detect them.\n\n> > Unfortunately, the way we deal with this is to die, so\n> > as soon as this happens, the repository fails closed.  While an attacker\n> > cannot make use of the collisions to spread malicious objects, because\n> > of the way Git works, they can effectively DoS a repository, which is in\n> > itself a security issue.  Fixing this requires major surgery.\n> \n> Can you elaborate on this? I believe that we die on any known collision\n> via the SHA1-DC code, and even if we didn't have that we'd detect the\n> collision (see [1] for the code) and die while the object is in the\n> temporary quarantine.\n> \n> I believe such a request is cheaper to serve than one that doesn't\n> upload colliding objects, we die earlier (less CPU etc.), and don't add\n> objects to the store.\n> \n> So what's the DoS vector?\n\nThat assumes that the server is using SHA-1DC and that the object can't\nbe uploaded any way except a push where its hash is checked.  Those are\ntrue for many, but not all, hosting providers.  For example, anyone\nusing Git in a FIPS-validated environment can only use FIPS-validated\ncrypto, and I'm not aware of any SHA-1DC implementations that are.\nAlso, some implementations like Dulwich don't use SHA-1DC.\n\nOnce someone can get that object to a standard Git which does use\nSHA-1DC, then the repository is pretty much hosed.  I probably can make\nthis better by just dropping the non SHA-1DC mode here and in libgit2,\nto at least disincentivize poor choices in the most common\nimplementations.\n\n> > We need the interoperability code to let people transition their\n> > repositories away from SHA-1, even if it has some performance impact,\n> > because without that most SHA-1 repositories will never transition.\n> > That's what's outlined in the transition plan, and why that approach was\n> > proposed, even though it would be nicer to avoid having to implement it\n> > at all.\n> \n> There's no question that we need working interop.\n> \n> The question at least in my mind is why that interop is happening by\n> annotating every object held in memory with whether they're SHA-1 or\n> SHA-256, as opposed to having some translation layer earlier in the\n> chain.\n\nThis is a good question.  Let me provide an example.\n\nWhen we speak the remote protocol with a remote system, we'll parse out\nseveral object ID of the appropriate algorithm.  We will then pass those\naround to various places in our transport code.  It makes it a lot\neasier if we can just run every object ID through an inline mapping\nfunction which immediately does nothing if the object ID is of the\nappropriate type rather than adding additional code to keep a check of\nthe current algorithm that's being used in the transport code.\n\nIt also makes it a lot easier when we let people store data in SHA-256\nand then print things in SHA-1 for compatibility if we can add an\noid_to_hex_output and just map every object automatically on output,\nregardless of where it came from, without needing to keep track of what\nalgorithm it's in.  For example, think about situations where the user\nmay have passed in a SHA-1 object ID and we reuse that value instead of\nreading a SHA-256 object from the store.\n\nSo it's not completely impossible to avoid a hash algorithm member, but\nit does significantly complicate our code not to do it.\n\n> Not all our file or in-memory structures are are like that, e.g. the\n> commit graph has a header saying \"this is a bunch of SHA-1/256\", and the\n> objects that follow are padded to that actual hash size, not the max\n> size we know about.\n> \n> My understanding of the transition plan was that we'd e.g. have a\n> SHA-1<->SHA-256 mapping of objects, which we'd say use to push/pull.\n\nCorrect.  That code exists and mostly works.  There are still a lot of\nfailing tests, but I have a pack index v3 that stores that data and a\nloose object store.\n\n> But that by the time I ran say a \"git commit\" none of that machinery\n> needed to care that I was interoping with a SHA-1 repo on the other end.\n> It would just happily have all SHA-256 objects, create new ones, and\n> only by the time I needed to push them would something kick in to\n> re-hash them.\n> \n> I *think* the anwer is just \"it's easier on the C-level\" and \"the\n> wastage doesn't seem to matter much\", which is fair enough.\n\nI think that's accurate.\n\n> *Goes and digs in the ML archive*:\n> \n>     https://lore.kernel.org/git/1399147942-165308-1-git-send-email-sandals@crustytoothpaste.net/#t\n>     https://lore.kernel.org/git/55016A3A.6010100@alum.mit.edu/\n> \n> To answer (some) of that myself:\n> \n> Digging up some of the initial discussion that seems to be the case, at\n> that point there was a suggestion of:\n> \n>     struct object_id {\n>         unsigned char hash_type;\n>         union {\n>             unsigned char sha1[GIT_SHA1_RAWSZ];\n>             unsigned char sha256[GIT_SHA256_RAWSZ];\n>         } hash;\n>     };\n> \n> To which you replied:\n> \n>     What I think might be more beneficial is to make the hash function\n>     specific to a particular repository, and therefore you could maintain\n>     something like this:\n> \n>       struct object_id {\n>           unsigned char hash[GIT_MAX_RAWSZ];\n>       };\n> \n> It wouldn't matter for the memory use to make it a union, but my reading\n> of the above is that the reason for the current object_id not-a-union\n> structure might not be valid now that there's a \"hash_type\" member, no?\n\nProbably at that time we didn't have the formal transition plan and\ndidn't anticipate interoperability as a concern.  I do think that using\na single hash member instead of a union makes things easier because we\ngenerally don't want to look up two different members in cases like\nprinting a hex OID.  We ultimately just want to print the right number\nof bytes from that data, and the union just makes things trickier with\nthe compiler when we do that.\n-- \nbrian m. carlson (he/him or they/them)\nHouston, Texas, US\n"},{"id":"422100","messageId":"87tuo6tgmj.fsf@evledraar.gmail.com","threadId":"55472","inReplyTo":"20210410152140.3525040-2-sandals@crustytoothpaste.net","subject":"Re: [PATCH 01/15] sha1-file: allow hashing objects literally with any algorithm","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-16T15:04:52Z","receivedAt":"2021-04-16T15:05:00Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sat, Apr 10 2021, brian m. carlson wrote:\n\n> In order to perform suitable testing with multiple algorithms and\n> interoperability, we'll need the ability to hash an object with a given\n> algorithm. Introduce this capability for now only for objects which are\n> hashed literally by adding a function which does this and changing a\n> static function to accept an algorithm pointer.\n>\n> Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n> ---\n>  object-file.c  | 16 ++++++++++++++--\n>  object-store.h |  3 +++\n>  2 files changed, 17 insertions(+), 2 deletions(-)\n>\n> diff --git a/object-file.c b/object-file.c\n> index 624af408cd..f5847ee20f 100644\n> --- a/object-file.c\n> +++ b/object-file.c\n> @@ -1957,6 +1957,15 @@ int write_object_file(const void *buf, unsigned long len, const char *type,\n>  int hash_object_file_literally(const void *buf, unsigned long len,\n>  \t\t\t       const char *type, struct object_id *oid,\n>  \t\t\t       unsigned flags)\n> +{\n> +\treturn hash_object_file_literally_algop(buf, len, type, oid, flags,\n> +\t\t\t\t\t\tthe_hash_algo);\n> +}\n> +\n> +int hash_object_file_literally_algop(const void *buf, unsigned long len,\n> +\t\t\t\t     const char *type, struct object_id *oid,\n> +\t\t\t\t     unsigned flags,\n> +\t\t\t\t     const struct git_hash_algo *algo)\n>  {\n>  \tchar *header;\n>  \tint hdrlen, status = 0;\n> @@ -1964,11 +1973,14 @@ int hash_object_file_literally(const void *buf, unsigned long len,\n>  \t/* type string, SP, %lu of the length plus NUL must fit this */\n>  \thdrlen = strlen(type) + MAX_HEADER_LEN;\n>  \theader = xmalloc(hdrlen);\n> -\twrite_object_file_prepare(the_hash_algo, buf, len, type, oid, header,\n> -\t\t\t\t  &hdrlen);\n> +\twrite_object_file_prepare(algo, buf, len, type, oid, header, &hdrlen);\n>  \n>  \tif (!(flags & HASH_WRITE_OBJECT))\n>  \t\tgoto cleanup;\n> +\tif (algo->format_id != the_hash_algo->format_id) {\n> +\t\tstatus = -1;\n> +\t\tgoto cleanup;\n> +\t}\n>  \tif (freshen_packed_object(oid) || freshen_loose_object(oid))\n>  \t\tgoto cleanup;\n>  \tstatus = write_loose_object(oid, header, hdrlen, buf, len, 0);\n> diff --git a/object-store.h b/object-store.h\n> index ec32c23dcb..f95d03a7f5 100644\n> --- a/object-store.h\n> +++ b/object-store.h\n> @@ -221,6 +221,9 @@ int hash_object_file_literally(const void *buf, unsigned long len,\n>  \t\t\t       const char *type, struct object_id *oid,\n>  \t\t\t       unsigned flags);\n>  \n> +int hash_object_file_literally_algop(const void *buf, unsigned long len,\n> +\t\t\t\t     const char *type, struct object_id *oid,\n> +\t\t\t\t     unsigned flags, const struct git_hash_algo *algo);\n>  /*\n>   * Add an object file to the in-memory object store, without writing it\n>   * to disk.\n\nWe only have one user of hash_object_file_literally(),\nbuiltin/hash-object.c, let's just change the signature of\nhash_object_file_literally() instead of adding a new function. This\nleaves the tree with no direct user of hash_object_file_literally().\n"},{"id":"422101","messageId":"87r1jatfv5.fsf@evledraar.gmail.com","threadId":"55472","inReplyTo":"20210410152140.3525040-3-sandals@crustytoothpaste.net","subject":"Re: [PATCH 02/15] builtin/hash-object: allow literally hashing with a given algorithm","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-16T15:21:18Z","receivedAt":"2021-04-16T15:21:37Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sat, Apr 10 2021, brian m. carlson wrote:\n\n> @@ -103,6 +110,7 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)\n>  \t\tOPT_BOOL( 0 , \"no-filters\", &no_filters, N_(\"store file as is without filters\")),\n>  \t\tOPT_BOOL( 0, \"literally\", &literally, N_(\"just hash any random garbage to create corrupt objects for debugging Git\")),\n>  \t\tOPT_STRING( 0 , \"path\", &vpath, N_(\"file\"), N_(\"process file as it were from this path\")),\n> +\t\tOPT_STRING( 0 , \"object-format\", &object_format, N_(\"object-format\"), N_(\"Use this hash algorithm\")),\n>  \t\tOPT_END()\n\nNit: Carrrying the \"0 , \" v.s. \"0, \" formatting bug in the \"path\"\noption, due to copy/pasting.\n"},{"id":"422104","messageId":"8735vq2l8a.fsf@evledraar.gmail.com","threadId":"55472","inReplyTo":"20210410152140.3525040-3-sandals@crustytoothpaste.net","subject":"Re: [PATCH 02/15] builtin/hash-object: allow literally hashing with a given algorithm","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2021-04-16T17:27:33Z","receivedAt":"2021-04-16T17:27:40Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sat, Apr 10 2021, brian m. carlson wrote:\n\n> Add an --object-format argument to git hash-object that allows hashing\n> an object with a given algorithm. Currently this options is limited to\n> use with --literally, since the index_* functions do not yet handle\n> multiple hash algorithms.\n>\n> Signed-off-by: brian m. carlson <sandals@crustytoothpaste.net>\n> ---\n>  builtin/hash-object.c  | 47 ++++++++++++++++++++++++++++++------------\n>  t/t1007-hash-object.sh | 10 +++++++++\n>  2 files changed, 44 insertions(+), 13 deletions(-)\n>\n> diff --git a/builtin/hash-object.c b/builtin/hash-object.c\n> index 640ef4ded5..0203cfbe9a 100644\n> --- a/builtin/hash-object.c\n> +++ b/builtin/hash-object.c\n> @@ -17,7 +17,8 @@\n>   * needs to bypass the data conversion performed by, and the type\n>   * limitation imposed by, index_fd() and its callees.\n>   */\n> -static int hash_literally(struct object_id *oid, int fd, const char *type, unsigned flags)\n> +static int hash_literally(struct object_id *oid, int fd, const char *type,\n> +\t\t\t  unsigned flags, const struct git_hash_algo *algo)\n>  {\n>  \tstruct strbuf buf = STRBUF_INIT;\n>  \tint ret;\n> @@ -25,42 +26,46 @@ static int hash_literally(struct object_id *oid, int fd, const char *type, unsig\n>  \tif (strbuf_read(&buf, fd, 4096) < 0)\n>  \t\tret = -1;\n>  \telse\n> -\t\tret = hash_object_file_literally(buf.buf, buf.len, type, oid,\n> -\t\t\t\t\t\t flags);\n> +\t\tret = hash_object_file_literally_algop(buf.buf, buf.len, type, oid,\n> +\t\t\t\t\t\t       flags, algo);\n>  \tstrbuf_release(&buf);\n>  \treturn ret;\n>  }\n>  \n>  static void hash_fd(int fd, const char *type, const char *path, unsigned flags,\n> -\t\t    int literally)\n> +\t\t    int literally, const struct git_hash_algo *algo)\n>  {\n>  \tstruct stat st;\n>  \tstruct object_id oid;\n>  \n> +\tif (!literally && algo != the_hash_algo)\n> +\t\tdie(_(\"Can't use hash algo %s except literally yet\"), algo->name);\n> +\n>  \tif (fstat(fd, &st) < 0 ||\n>  \t    (literally\n> -\t     ? hash_literally(&oid, fd, type, flags)\n> +\t     ? hash_literally(&oid, fd, type, flags, algo)\n>  \t     : index_fd(the_repository->index, &oid, fd, &st,\n>  \t\t\ttype_from_string(type), path, flags)))\n>  \t\tdie((flags & HASH_WRITE_OBJECT)\n>  \t\t    ? \"Unable to add %s to database\"\n>  \t\t    : \"Unable to hash %s\", path);\n> -\tprintf(\"%s\\n\", oid_to_hex(&oid));\n> +\tprintf(\"%s\\n\", hash_to_hex_algop(oid.hash, algo));\n>  \tmaybe_flush_or_die(stdout, \"hash to stdout\");\n>  }\n>  \n>  static void hash_object(const char *path, const char *type, const char *vpath,\n> -\t\t\tunsigned flags, int literally)\n> +\t\t\tunsigned flags, int literally,\n> +\t\t\tconst struct git_hash_algo *algo)\n>  {\n>  \tint fd;\n>  \tfd = open(path, O_RDONLY);\n>  \tif (fd < 0)\n>  \t\tdie_errno(\"Cannot open '%s'\", path);\n> -\thash_fd(fd, type, vpath, flags, literally);\n> +\thash_fd(fd, type, vpath, flags, literally, algo);\n>  }\n>  \n>  static void hash_stdin_paths(const char *type, int no_filters, unsigned flags,\n> -\t\t\t     int literally)\n> +\t\t\t     int literally, const struct git_hash_algo *algo)\n>  {\n>  \tstruct strbuf buf = STRBUF_INIT;\n>  \tstruct strbuf unquoted = STRBUF_INIT;\n> @@ -73,7 +78,7 @@ static void hash_stdin_paths(const char *type, int no_filters, unsigned flags,\n>  \t\t\tstrbuf_swap(&buf, &unquoted);\n>  \t\t}\n>  \t\thash_object(buf.buf, type, no_filters ? NULL : buf.buf, flags,\n> -\t\t\t    literally);\n> +\t\t\t    literally, algo);\n>  \t}\n>  \tstrbuf_release(&buf);\n>  \tstrbuf_release(&unquoted);\n> @@ -94,6 +99,8 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)\n>  \tint nongit = 0;\n>  \tunsigned flags = HASH_FORMAT_CHECK;\n>  \tconst char *vpath = NULL;\n> +\tconst char *object_format = NULL;\n> +\tconst struct git_hash_algo *algo;\n>  \tconst struct option hash_object_options[] = {\n>  \t\tOPT_STRING('t', NULL, &type, N_(\"type\"), N_(\"object type\")),\n>  \t\tOPT_BIT('w', NULL, &flags, N_(\"write the object into the object database\"),\n> @@ -103,6 +110,7 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)\n>  \t\tOPT_BOOL( 0 , \"no-filters\", &no_filters, N_(\"store file as is without filters\")),\n>  \t\tOPT_BOOL( 0, \"literally\", &literally, N_(\"just hash any random garbage to create corrupt objects for debugging Git\")),\n>  \t\tOPT_STRING( 0 , \"path\", &vpath, N_(\"file\"), N_(\"process file as it were from this path\")),\n> +\t\tOPT_STRING( 0 , \"object-format\", &object_format, N_(\"object-format\"), N_(\"Use this hash algorithm\")),\n>  \t\tOPT_END()\n>  \t};\n>  \tint i;\n> @@ -121,6 +129,19 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)\n>  \n>  \tgit_config(git_default_config, NULL);\n>  \n> +\talgo = the_hash_algo;\n> +\tif (object_format) {\n> +\t\tif (flags & HASH_WRITE_OBJECT)\n> +\t\t\terrstr = \"Can't use -w with --object-format\";\n> +\t\telse {\n> +\t\t\tint id = hash_algo_by_name(object_format);\n> +\t\t\tif (id == GIT_HASH_UNKNOWN)\n> +\t\t\t\terrstr = \"Unknown object format\";\n> +\t\t\telse\n> +\t\t\t\talgo = &hash_algos[id];\n> +\t\t}\n> +\t}\n> +\n>  \tif (stdin_paths) {\n>  \t\tif (hashstdin)\n>  \t\t\terrstr = \"Can't use --stdin-paths with --stdin\";\n> @@ -142,7 +163,7 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)\n>  \t}\n>  \n>  \tif (hashstdin)\n> -\t\thash_fd(0, type, vpath, flags, literally);\n> +\t\thash_fd(0, type, vpath, flags, literally, algo);\n>  \n>  \tfor (i = 0 ; i < argc; i++) {\n>  \t\tconst char *arg = argv[i];\n> @@ -151,12 +172,12 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)\n>  \t\tif (prefix)\n>  \t\t\targ = to_free = prefix_filename(prefix, arg);\n>  \t\thash_object(arg, type, no_filters ? NULL : vpath ? vpath : arg,\n> -\t\t\t    flags, literally);\n> +\t\t\t    flags, literally, algo);\n>  \t\tfree(to_free);\n>  \t}\n>  \n>  \tif (stdin_paths)\n> -\t\thash_stdin_paths(type, no_filters, flags, literally);\n> +\t\thash_stdin_paths(type, no_filters, flags, literally, algo);\n>  \n>  \treturn 0;\n>  }\n> diff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh\n> index 64b340f227..ea4b3d2bda 100755\n> --- a/t/t1007-hash-object.sh\n> +++ b/t/t1007-hash-object.sh\n> @@ -83,6 +83,11 @@ test_expect_success 'hash a file' '\n>  \ttest \"$(test_oid hello)\" = $(git hash-object hello)\n>  '\n>  \n> +test_expect_failure 'hash a file with a given algorithm' '\n> +\ttest \"$(test_oid --hash=sha1 hello)\" = $(git hash-object --object-format=sha1 hello) &&\n> +\ttest \"$(test_oid --hash=sha256 hello)\" = $(git hash-object --object-format=sha256 hello)\n> +'\n> +\n>  test_blob_does_not_exist \"$(test_oid hello)\"\n>  \n>  test_expect_success 'hash from stdin' '\n> @@ -248,4 +253,9 @@ test_expect_success '--literally with extra-long type' '\n>  \techo example | git hash-object -t $t --literally --stdin\n>  '\n>  \n> +test_expect_success '--literally with --object-format' '\n> +\ttest $(test_oid --hash=sha1 hello) = $(git hash-object -t blob --literally --object-format=sha1 hello) &&\n> +\ttest $(test_oid --hash=sha256 hello) = $(git hash-object -t blob --literally --object-format=sha256 hello)\n> +'\n> +\n>  test_done\n\nFirst, the \"errstr\" handling here is arguably buggy, or at least hard to\nread. Before this change we never clobber it if we find an issue, but\nafter we do. In the end it doesn't really matter, we error on one usage\nerror or the other, but it does make fore more confusing reading.\n\nThe patch-on-top mentioned below turns that into a \"goto error\".\n\nSecondly, sorry about the semantic merge conflict, but while trying to\nfix that issue today I started reviwing this in more depth, and wanted\nto ask some questions first:\n\nI don't get why we need to pass the algop around here at all. Why not\nhandle this like your previous change to e.g. show-index.c where we set\nit globally?\n\nJust doing it like that would make your TODO test pass, and we're a\none-shot command here, I see no reason not do do that.\n\nI have a hacky WIP series on top of this which implements that mid-way,\nthe diff is also at the end of this E-Mail.\n\nFor my own re-rolling of my object.c changes I settled on passing a\n\"type_len\" down to hash_literally() et al for reasons I won't go into\nhere. That would conflict with your in-flight changes (in a way that's\nnot hard to resolve), but not conflicting at all because we just used a\nglobal repo_set_hash_algo() would be even better.\n\nSo here's the messy patches on top:\n\nhttps://github.com/avar/git/compare/3e8b16d2e70...avar-bc/hash-transition-interop-part-1\n\nThe other part of that is to just support \"git --object-format=sha256\"\ninstead of needing every command to slowly aquire an --object-format\nargument.\n\nThat's currently broken in that series for a reason I wanted to ask you\nabout. I know *why*, but I wonder what the best solution is.\n\nPart how that's dealt with, i.e. if you think this direction of making\n\"git\" take that argument directly, has to do with data in your wetware,\nnot something in-tree :) I.e. what the future direction is.\n\nI.e. it's broken because we end up calling repo_set_hash_algo() in\nseveral places, first via common-main.c, then with my patch below in\ngit.c's argument handling, and then via parse_options() and in setup.c,\ne.g. hash-object.c's explicit setup_git_directory() call.\n\nAs you'll see from the setup_git_directory_gently() call below I had an\nattempt to monkeypatch around that by detecting zero'd out structs in\nsetup_git_directory_gently().\n\nBut e.g. ./t5318-commit-graph.sh is still broken because we no longer\npay attention to \"git init --object-format=*\" since\ncheck_repository_format() and validate_hash_algorithm() end up not only\nvalidating config, but also (re-)setting it.\n\nIt seems to me that we need a separation of concerns there, to have the\ndefault in one place, and validation in another. But maybe there's some\nmore subtle reason for why we're (re-)setting it in that manner, and I\nran out of hacking time today and figured I'd send this E-Mail off at\nthe end of the day :)\n\n\ndiff --git a/builtin/hash-object.c b/builtin/hash-object.c\nindex 0203cfbe9a4..0ecf24b4793 100644\n--- a/builtin/hash-object.c\n+++ b/builtin/hash-object.c\n@@ -17,8 +17,7 @@\n  * needs to bypass the data conversion performed by, and the type\n  * limitation imposed by, index_fd() and its callees.\n  */\n-static int hash_literally(struct object_id *oid, int fd, const char *type,\n-\t\t\t  unsigned flags, const struct git_hash_algo *algo)\n+static int hash_literally(struct object_id *oid, int fd, const char *type, unsigned flags)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n \tint ret;\n@@ -26,46 +25,42 @@ static int hash_literally(struct object_id *oid, int fd, const char *type,\n \tif (strbuf_read(&buf, fd, 4096) < 0)\n \t\tret = -1;\n \telse\n-\t\tret = hash_object_file_literally_algop(buf.buf, buf.len, type, oid,\n-\t\t\t\t\t\t       flags, algo);\n+\t\tret = hash_object_file_literally(buf.buf, buf.len, type, oid,\n+\t\t\t\t\t\t flags);\n \tstrbuf_release(&buf);\n \treturn ret;\n }\n \n static void hash_fd(int fd, const char *type, const char *path, unsigned flags,\n-\t\t    int literally, const struct git_hash_algo *algo)\n+\t\t    int literally)\n {\n \tstruct stat st;\n \tstruct object_id oid;\n \n-\tif (!literally && algo != the_hash_algo)\n-\t\tdie(_(\"Can't use hash algo %s except literally yet\"), algo->name);\n-\n \tif (fstat(fd, &st) < 0 ||\n \t    (literally\n-\t     ? hash_literally(&oid, fd, type, flags, algo)\n+\t     ? hash_literally(&oid, fd, type, flags)\n \t     : index_fd(the_repository->index, &oid, fd, &st,\n \t\t\ttype_from_string(type), path, flags)))\n \t\tdie((flags & HASH_WRITE_OBJECT)\n \t\t    ? \"Unable to add %s to database\"\n \t\t    : \"Unable to hash %s\", path);\n-\tprintf(\"%s\\n\", hash_to_hex_algop(oid.hash, algo));\n+\tprintf(\"%s\\n\", oid_to_hex(&oid));\n \tmaybe_flush_or_die(stdout, \"hash to stdout\");\n }\n \n static void hash_object(const char *path, const char *type, const char *vpath,\n-\t\t\tunsigned flags, int literally,\n-\t\t\tconst struct git_hash_algo *algo)\n+\t\t\tunsigned flags, int literally)\n {\n \tint fd;\n \tfd = open(path, O_RDONLY);\n \tif (fd < 0)\n \t\tdie_errno(\"Cannot open '%s'\", path);\n-\thash_fd(fd, type, vpath, flags, literally, algo);\n+\thash_fd(fd, type, vpath, flags, literally);\n }\n \n static void hash_stdin_paths(const char *type, int no_filters, unsigned flags,\n-\t\t\t     int literally, const struct git_hash_algo *algo)\n+\t\t\t     int literally)\n {\n \tstruct strbuf buf = STRBUF_INIT;\n \tstruct strbuf unquoted = STRBUF_INIT;\n@@ -78,7 +73,7 @@ static void hash_stdin_paths(const char *type, int no_filters, unsigned flags,\n \t\t\tstrbuf_swap(&buf, &unquoted);\n \t\t}\n \t\thash_object(buf.buf, type, no_filters ? NULL : buf.buf, flags,\n-\t\t\t    literally, algo);\n+\t\t\t    literally);\n \t}\n \tstrbuf_release(&buf);\n \tstrbuf_release(&unquoted);\n@@ -100,7 +95,6 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)\n \tunsigned flags = HASH_FORMAT_CHECK;\n \tconst char *vpath = NULL;\n \tconst char *object_format = NULL;\n-\tconst struct git_hash_algo *algo;\n \tconst struct option hash_object_options[] = {\n \t\tOPT_STRING('t', NULL, &type, N_(\"type\"), N_(\"object type\")),\n \t\tOPT_BIT('w', NULL, &flags, N_(\"write the object into the object database\"),\n@@ -110,47 +104,38 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)\n \t\tOPT_BOOL( 0 , \"no-filters\", &no_filters, N_(\"store file as is without filters\")),\n \t\tOPT_BOOL( 0, \"literally\", &literally, N_(\"just hash any random garbage to create corrupt objects for debugging Git\")),\n \t\tOPT_STRING( 0 , \"path\", &vpath, N_(\"file\"), N_(\"process file as it were from this path\")),\n-\t\tOPT_STRING( 0 , \"object-format\", &object_format, N_(\"object-format\"), N_(\"Use this hash algorithm\")),\n+\t\tOPT_OBJECT_FORMAT(0, \"object-format\", &object_format),\n \t\tOPT_END()\n \t};\n \tint i;\n \tconst char *errstr = NULL;\n \n-\targc = parse_options(argc, argv, prefix, hash_object_options,\n-\t\t\t     hash_object_usage, 0);\n-\n+\t/* might setup the hash algorithm */\n \tif (flags & HASH_WRITE_OBJECT)\n \t\tprefix = setup_git_directory();\n \telse\n \t\tprefix = setup_git_directory_gently(&nongit);\n \n+\t/* maybe override the already setup hash algorithm */\n+\targc = parse_options(argc, argv, prefix, hash_object_options,\n+\t\t\t     hash_object_usage, 0);\n+\n \tif (vpath && prefix)\n \t\tvpath = xstrdup(prefix_filename(prefix, vpath));\n \n \tgit_config(git_default_config, NULL);\n \n-\talgo = the_hash_algo;\n-\tif (object_format) {\n-\t\tif (flags & HASH_WRITE_OBJECT)\n-\t\t\terrstr = \"Can't use -w with --object-format\";\n-\t\telse {\n-\t\t\tint id = hash_algo_by_name(object_format);\n-\t\t\tif (id == GIT_HASH_UNKNOWN)\n-\t\t\t\terrstr = \"Unknown object format\";\n-\t\t\telse\n-\t\t\t\talgo = &hash_algos[id];\n-\t\t}\n-\t}\n \n-\tif (stdin_paths) {\n+\tif (object_format && flags & HASH_WRITE_OBJECT) {\n+\t\terrstr = \"Can't use -w with --object-format\";\n+\t} else if (stdin_paths) {\n \t\tif (hashstdin)\n \t\t\terrstr = \"Can't use --stdin-paths with --stdin\";\n \t\telse if (argc)\n \t\t\terrstr = \"Can't specify files with --stdin-paths\";\n \t\telse if (vpath)\n \t\t\terrstr = \"Can't use --stdin-paths with --path\";\n-\t}\n-\telse {\n+\t} else {\n \t\tif (hashstdin > 1)\n \t\t\terrstr = \"Multiple --stdin arguments are not supported\";\n \t\tif (vpath && no_filters)\n@@ -163,7 +148,7 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)\n \t}\n \n \tif (hashstdin)\n-\t\thash_fd(0, type, vpath, flags, literally, algo);\n+\t\thash_fd(0, type, vpath, flags, literally);\n \n \tfor (i = 0 ; i < argc; i++) {\n \t\tconst char *arg = argv[i];\n@@ -172,12 +157,12 @@ int cmd_hash_object(int argc, const char **argv, const char *prefix)\n \t\tif (prefix)\n \t\t\targ = to_free = prefix_filename(prefix, arg);\n \t\thash_object(arg, type, no_filters ? NULL : vpath ? vpath : arg,\n-\t\t\t    flags, literally, algo);\n+\t\t\t    flags, literally);\n \t\tfree(to_free);\n \t}\n \n \tif (stdin_paths)\n-\t\thash_stdin_paths(type, no_filters, flags, literally, algo);\n+\t\thash_stdin_paths(type, no_filters, flags, literally);\n \n \treturn 0;\n }\ndiff --git a/builtin/init-db.c b/builtin/init-db.c\nindex c19b35f1e69..7a7e63cf702 100644\n--- a/builtin/init-db.c\n+++ b/builtin/init-db.c\n@@ -548,8 +548,7 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \t\t\t   N_(\"separate git dir from working tree\")),\n \t\tOPT_STRING('b', \"initial-branch\", &initial_branch, N_(\"name\"),\n \t\t\t   N_(\"override the name of the initial branch\")),\n-\t\tOPT_STRING(0, \"object-format\", &object_format, N_(\"hash\"),\n-\t\t\t   N_(\"specify the hash algorithm to use\")),\n+\t\tOPT_OBJECT_FORMAT(0, \"object-format\", &object_format),\n \t\tOPT_END()\n \t};\n \n@@ -607,11 +606,8 @@ int cmd_init_db(int argc, const char **argv, const char *prefix)\n \t\tfree(cwd);\n \t}\n \n-\tif (object_format) {\n+\tif (object_format)\n \t\thash_algo = hash_algo_by_name(object_format);\n-\t\tif (hash_algo == GIT_HASH_UNKNOWN)\n-\t\t\tdie(_(\"unknown hash algorithm '%s'\"), object_format);\n-\t}\n \n \tif (init_shared_repository != -1)\n \t\tset_shared_repository(init_shared_repository);\ndiff --git a/builtin/show-index.c b/builtin/show-index.c\nindex 0e0b9fb95bc..51352c2eaeb 100644\n--- a/builtin/show-index.c\n+++ b/builtin/show-index.c\n@@ -15,23 +15,12 @@ int cmd_show_index(int argc, const char **argv, const char *prefix)\n \tunsigned int version;\n \tstatic unsigned int top_index[256];\n \tunsigned hashsz;\n-\tconst char *hash_name = NULL;\n-\tint hash_algo;\n \tconst struct option show_index_options[] = {\n-\t\tOPT_STRING(0, \"object-format\", &hash_name, N_(\"hash-algorithm\"),\n-\t\t\t   N_(\"specify the hash algorithm to use\")),\n+\t\tOPT_OBJECT_FORMAT(0, \"object-format\", NULL),\n \t\tOPT_END()\n \t};\n \n \targc = parse_options(argc, argv, prefix, show_index_options, show_index_usage, 0);\n-\n-\tif (hash_name) {\n-\t\thash_algo = hash_algo_by_name(hash_name);\n-\t\tif (hash_algo == GIT_HASH_UNKNOWN)\n-\t\t\tdie(_(\"Unknown hash algorithm\"));\n-\t\trepo_set_hash_algo(the_repository, hash_algo);\n-\t}\n-\n \thashsz = the_hash_algo->rawsz;\n \n \tif (fread(top_index, 2 * 4, 1, stdin) != 1)\ndiff --git a/builtin/verify-pack.c b/builtin/verify-pack.c\nindex 05c52135946..6ce09d068e7 100644\n--- a/builtin/verify-pack.c\n+++ b/builtin/verify-pack.c\n@@ -16,6 +16,8 @@ static int verify_one_pack(const char *path, unsigned int flags, const char *has\n \tint stat_only = flags & VERIFY_PACK_STAT_ONLY;\n \tint err;\n \n+\tif (hash_algo)\n+\t\tstrvec_pushf(argv, \"--object-format=%s\", hash_algo);\n \tstrvec_push(argv, \"index-pack\");\n \n \tif (stat_only)\n@@ -25,9 +27,6 @@ static int verify_one_pack(const char *path, unsigned int flags, const char *has\n \telse\n \t\tstrvec_push(argv, \"--verify\");\n \n-\tif (hash_algo)\n-\t\tstrvec_pushf(argv, \"--object-format=%s\", hash_algo);\n-\n \t/*\n \t * In addition to \"foo.pack\" we accept \"foo.idx\" and \"foo\";\n \t * normalize these forms to \"foo.pack\" for \"index-pack --verify\".\n@@ -71,8 +70,7 @@ int cmd_verify_pack(int argc, const char **argv, const char *prefix)\n \t\t\tVERIFY_PACK_VERBOSE),\n \t\tOPT_BIT('s', \"stat-only\", &flags, N_(\"show statistics only\"),\n \t\t\tVERIFY_PACK_STAT_ONLY),\n-\t\tOPT_STRING(0, \"object-format\", &object_format, N_(\"hash\"),\n-\t\t\t   N_(\"specify the hash algorithm to use\")),\n+\t\tOPT_OBJECT_FORMAT(0, \"object-format\", &object_format),\n \t\tOPT_END()\n \t};\n \ndiff --git a/git.c b/git.c\nindex 9bc077a025c..824a9ef6cd2 100644\n--- a/git.c\n+++ b/git.c\n@@ -160,6 +160,8 @@ static int handle_options(const char ***argv, int *argc, int *envchanged)\n \t\t\t\ttrace2_cmd_name(\"_query_\");\n \t\t\t\texit(0);\n \t\t\t}\n+\t\t} else if (skip_prefix(cmd, \"--object-format=\", &cmd)) {\n+\t\t\trepo_set_hash_algo_arg(the_repository, cmd);\n \t\t} else if (!strcmp(cmd, \"--html-path\")) {\n \t\t\tputs(system_path(GIT_HTML_PATH));\n \t\t\ttrace2_cmd_name(\"_query_\");\ndiff --git a/object-file.c b/object-file.c\nindex 0401d7ca4fb..c53fc14718f 100644\n--- a/object-file.c\n+++ b/object-file.c\n@@ -2001,15 +2001,6 @@ int write_object_file(const void *buf, unsigned long len, const char *type,\n int hash_object_file_literally(const void *buf, unsigned long len,\n \t\t\t       const char *type, struct object_id *oid,\n \t\t\t       unsigned flags)\n-{\n-\treturn hash_object_file_literally_algop(buf, len, type, oid, flags,\n-\t\t\t\t\t\tthe_hash_algo);\n-}\n-\n-int hash_object_file_literally_algop(const void *buf, unsigned long len,\n-\t\t\t\t     const char *type, struct object_id *oid,\n-\t\t\t\t     unsigned flags,\n-\t\t\t\t     const struct git_hash_algo *algo)\n {\n \tchar *header;\n \tint hdrlen, status = 0;\n@@ -2017,14 +2008,11 @@ int hash_object_file_literally_algop(const void *buf, unsigned long len,\n \t/* type string, SP, %lu of the length plus NUL must fit this */\n \thdrlen = strlen(type) + MAX_HEADER_LEN;\n \theader = xmalloc(hdrlen);\n-\twrite_object_file_prepare(algo, buf, len, type, oid, header, &hdrlen);\n+\twrite_object_file_prepare(the_hash_algo, buf, len, type, oid, header,\n+\t\t\t\t  &hdrlen);\n \n \tif (!(flags & HASH_WRITE_OBJECT))\n \t\tgoto cleanup;\n-\tif (algo->format_id != the_hash_algo->format_id) {\n-\t\tstatus = -1;\n-\t\tgoto cleanup;\n-\t}\n \tif (freshen_packed_object(oid) || freshen_loose_object(oid))\n \t\tgoto cleanup;\n \tstatus = write_loose_object(oid, header, hdrlen, buf, len, 0);\ndiff --git a/parse-options-cb.c b/parse-options-cb.c\nindex 3c811e1e4a7..6308bed9675 100644\n--- a/parse-options-cb.c\n+++ b/parse-options-cb.c\n@@ -293,3 +293,17 @@ int parse_opt_passthru_argv(const struct option *opt, const char *arg, int unset\n \n \treturn 0;\n }\n+\n+int parse_opt_object_format_cb(const struct option *opt, const char *arg, int unset)\n+{\n+\tconst char **value = opt->value;\n+\n+\tBUG_ON_OPT_NEG(unset);\n+\n+\tif (arg)\n+\t\trepo_set_hash_algo_arg(the_repository, arg);\n+\tif (value)\n+\t\t*value = arg;\n+\n+\treturn 0;\n+}\ndiff --git a/parse-options.h b/parse-options.h\nindex a845a9d9527..086dab755ca 100644\n--- a/parse-options.h\n+++ b/parse-options.h\n@@ -201,6 +201,10 @@ struct option {\n #define OPT_ALIAS(s, l, source_long_name) \\\n \t{ OPTION_ALIAS, (s), (l), (source_long_name) }\n \n+#define OPT_OBJECT_FORMAT(s, l, v) \\\n+\t{ OPTION_CALLBACK, (s), (l), (v), N_(\"hash\"),N_(\"hash algorithm\"), \\\n+\t  PARSE_OPT_NONEG, parse_opt_object_format_cb }\n+\n /*\n  * parse_options() will filter out the processed options and leave the\n  * non-option arguments in argv[]. argv0 is assumed program name and\n@@ -303,6 +307,7 @@ enum parse_opt_result parse_opt_unknown_cb(struct parse_opt_ctx_t *ctx,\n \t\t\t\t\t   const char *, int);\n int parse_opt_passthru(const struct option *, const char *, int);\n int parse_opt_passthru_argv(const struct option *, const char *, int);\n+int parse_opt_object_format_cb(const struct option *, const char *, int);\n \n #define OPT__VERBOSE(var, h)  OPT_COUNTUP('v', \"verbose\", (var), (h))\n #define OPT__QUIET(var, h)    OPT_COUNTUP('q', \"quiet\",   (var), (h))\ndiff --git a/repository.c b/repository.c\nindex 87b355e7a65..1a7aca657db 100644\n--- a/repository.c\n+++ b/repository.c\n@@ -91,6 +91,15 @@ void repo_set_hash_algo(struct repository *repo, int hash_algo)\n \trepo->hash_algo = &hash_algos[hash_algo];\n }\n \n+void repo_set_hash_algo_arg(struct repository *repo, const char *algo)\n+{\n+\tint algo_id = hash_algo_by_name(algo);\n+\tif (algo_id == GIT_HASH_UNKNOWN)\n+\t\tdie(_(\"unknown hash algorithm '%s'\"), algo);\n+\tfprintf(stderr, \"now setting %s=%d\\n\", algo, algo_id);\n+\trepo_set_hash_algo(the_repository, algo_id);\n+}\n+\n /*\n  * Attempt to resolve and set the provided 'gitdir' for repository 'repo'.\n  * Return 0 upon success and a non-zero value upon failure.\ndiff --git a/repository.h b/repository.h\nindex b385ca3c94b..56482f89f64 100644\n--- a/repository.h\n+++ b/repository.h\n@@ -160,6 +160,7 @@ void repo_set_gitdir(struct repository *repo, const char *root,\n \t\t     const struct set_gitdir_args *extra_args);\n void repo_set_worktree(struct repository *repo, const char *path);\n void repo_set_hash_algo(struct repository *repo, int algo);\n+void repo_set_hash_algo_arg(struct repository *repo, const char *algo);\n void initialize_the_repository(void);\n int repo_init(struct repository *r, const char *gitdir, const char *worktree);\n \ndiff --git a/setup.c b/setup.c\nindex c04cd25a30d..7ad8f685b9b 100644\n--- a/setup.c\n+++ b/setup.c\n@@ -1308,7 +1308,15 @@ const char *setup_git_directory_gently(int *nongit_ok)\n \t\t\t\tgitdir = DEFAULT_GIT_DIR_ENVIRONMENT;\n \t\t\tsetup_git_env(gitdir);\n \t\t}\n-\t\tif (startup_info->have_repository)\n+\t\tif (startup_info->have_repository &&\n+\t\t    /*\n+\t\t     * If we have called initialize_the_repository()\n+\t\t     * via common-main.c let's not set things up from\n+\t\t     * the REPOSITORY_FORMAT_INIT defaults again,\n+\t\t     * otherwise we'll clobber e.g. a invocations of\n+\t\t     * \"git --object-format=<HASH> some-cmd\".\n+\t\t     */\n+\t\t    !the_repository->hash_algo)\n \t\t\trepo_set_hash_algo(the_repository, repo_fmt.hash_algo);\n \t}\n \ndiff --git a/t/t1007-hash-object.sh b/t/t1007-hash-object.sh\nindex ea4b3d2bda4..37bfb40c506 100755\n--- a/t/t1007-hash-object.sh\n+++ b/t/t1007-hash-object.sh\n@@ -83,7 +83,7 @@ test_expect_success 'hash a file' '\n \ttest \"$(test_oid hello)\" = $(git hash-object hello)\n '\n \n-test_expect_failure 'hash a file with a given algorithm' '\n+test_expect_success 'hash a file with a given algorithm' '\n \ttest \"$(test_oid --hash=sha1 hello)\" = $(git hash-object --object-format=sha1 hello) &&\n \ttest \"$(test_oid --hash=sha256 hello)\" = $(git hash-object --object-format=sha256 hello)\n '\n@@ -255,7 +255,9 @@ test_expect_success '--literally with extra-long type' '\n \n test_expect_success '--literally with --object-format' '\n \ttest $(test_oid --hash=sha1 hello) = $(git hash-object -t blob --literally --object-format=sha1 hello) &&\n-\ttest $(test_oid --hash=sha256 hello) = $(git hash-object -t blob --literally --object-format=sha256 hello)\n+\ttest $(test_oid --hash=sha256 hello) = $(git hash-object -t blob --literally --object-format=sha256 hello) &&\n+\ttest $(test_oid --hash=sha256 hello) = $(git --object-format=sha256 hash-object -t blob --literally hello) &&\n+\ttest $(test_oid --hash=sha256 hello) = $(git --object-format=sha1 hash-object -t blob --object-format=sha256 --literally hello)\n '\n \n test_done\n"},{"id":"422112","messageId":"xmqqfszqm546.fsf@gitster.g","threadId":"55472","inReplyTo":"87tuo6tgmj.fsf@evledraar.gmail.com","subject":"Re: [PATCH 01/15] sha1-file: allow hashing objects literally with any algorithm","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2021-04-16T18:55:21Z","receivedAt":"2021-04-16T18:55:47Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> We only have one user of hash_object_file_literally(),\n> builtin/hash-object.c, let's just change the signature of\n> hash_object_file_literally() instead of adding a new function. This\n> leaves the tree with no direct user of hash_object_file_literally().\n\nMakes sense.\n"},{"id":"425177","messageId":"YKcVsfsLZqrkMjXl@google.com","threadId":"55472","inReplyTo":"patch-1.2-a8cc05cf56f-20210412T105422Z-avarab@gmail.com","subject":"Re: [PATCH 1/2] git-compat-util.h: clarify comment on GCC-specific code","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2021-05-21T02:06:41Z","receivedAt":"2021-05-21T02:06:47Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi,\n\nÆvar Arnfjörð Bjarmason wrote:\n\n> Change a comment added in e208f9cc757 (make error()'s constant return\n> value more visible, 2012-12-15) to note that the code doesn't only\n> depend on variadic macros, which have been a hard dependency since\n> 765dc168882 (git-compat-util: always enable variadic macros,\n> 2021-01-28), but also on GCC's handling of __VA_ARGS__. The commit\n> message for e208f9cc757 made this clear, but the comment it added did\n> not.\n>\n> See also e05bed960d3 (trace: add 'file:line' to all trace output,\n> 2014-07-12) for another comment about GNUC's handling of __VA_ARGS__.\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  git-compat-util.h | 13 +++++++++----\n>  1 file changed, 9 insertions(+), 4 deletions(-)\n\nOh, subtle.  In fact, I believe 9798f7e5f9 (Use __VA_ARGS__ for all of\nerror's arguments, 2013-02-08) got rid of the gcc-ism, so we could do\nthe following instead.\n\n(See section 6.10.3.10 for a description of\n\n\t#define f(...) g(__VA_ARGS__)\n\nin the C99 standard.)\n\nThanks,\nJonathan\n\n-- >8 --\nSubject: error: use macro-based static analysis aid on non-gcc, too\n\nIn the rest of Git, we check HAVE_VARIADIC_MACROS (which is\nunconditionally defined to true as a way to discover platforms that do\nnot support it) to guard code that requires variadic macro support.\nThis variadic macro is a bit older, so it uses a __GNUC__ check\ninstead.  Switch to use of HAVE_VARIADIC_MACROS here as well, so more\ncompilers can get the benefit of knowing at compile time that error()\nalways returns -1.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n git-compat-util.h | 22 ++++++++++------------\n 1 file changed, 10 insertions(+), 12 deletions(-)\n\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex a508dbe5a3..ca22b11760 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -483,14 +483,19 @@ void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));\n #include <openssl/x509v3.h>\n #endif /* NO_OPENSSL */\n \n+/*\n+ * This is always defined as a first step towards making the use of variadic\n+ * macros unconditional. If it causes compilation problems on your platform,\n+ * please report it to the Git mailing list at git@vger.kernel.org.\n+ */\n+#define HAVE_VARIADIC_MACROS 1\n+\n /*\n  * Let callers be aware of the constant return value; this can help\n- * gcc with -Wuninitialized analysis. We restrict this trick to gcc, though,\n- * because some compilers may not support variadic macros. Since we're only\n- * trying to help gcc, anyway, it's OK; other compilers will fall back to\n- * using the function as usual.\n+ * gcc with -Wuninitialized analysis. Compilers without support for variadic\n+ * macros will fall back to using the function as usual.\n  */\n-#if defined(__GNUC__)\n+#ifdef HAVE_VARIADIC_MACROS\n static inline int const_error(void)\n {\n \treturn -1;\n@@ -1192,13 +1197,6 @@ static inline int regexec_buf(const regex_t *preg, const char *buf, size_t size,\n #endif\n #endif\n \n-/*\n- * This is always defined as a first step towards making the use of variadic\n- * macros unconditional. If it causes compilation problems on your platform,\n- * please report it to the Git mailing list at git@vger.kernel.org.\n- */\n-#define HAVE_VARIADIC_MACROS 1\n-\n /* usage.c: only to be used for testing BUG() implementation (see test-tool) */\n extern int BUG_exit_code;\n \n-- \n2.31.1.818.g46aad6cb9e\n\n"},{"id":"425179","messageId":"YKcf95uQ1W7COSsf@google.com","threadId":"55472","inReplyTo":"patch-2.2-f12e3cad57d-20210412T105422Z-avarab@gmail.com","subject":"Re: [PATCH 2/2] C99 support: remove non-HAVE_VARIADIC_MACROS code","fromName":"Jonathan Nieder","fromEmail":"jrnieder@gmail.com","sentAt":"2021-05-21T02:50:31Z","receivedAt":"2021-05-21T02:50:37Z","isPatch":true,"sender":{"key":"jrnieder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/281595?v=4"},"body":"Hi,\n\nÆvar Arnfjörð Bjarmason wrote:\n\n> Remove code that depend on HAVE_VARIADIC_MACROS not being set. Since\n> 765dc168882 (git-compat-util: always enable variadic macros,\n> 2021-01-28) we've unconditionally defined it to be true, and that\n> change went out with v2.31.0. This should have given packagers enough\n> time to discover whether variadic macros were an issue.\n>\n> It seems that they weren't, so let's update the coding guidelines and\n> remove all the fallback code for the non-HAVE_VARIADIC_MACROS case.\n\nAs discussed in the rest of this thread, that's a pretty short time,\nso while I would love to be able to use variadic macros\nunconditionally, I think we'd need a different rationale.\n\nThat said, I want us to be ready the moment external conditions allow.\nIdeally we want it to be as simple as\n\n\tgit grep --name-only -e HAVE_VARIADIC_MACROS |\n\txargs unifdef -m -DHAVE_VARIADIC_MACROS=1\n\nplus removing the #define; is there anything we need to do in advance\nto make that work well?  Let's see.\n\n[...]\n> --- a/trace.h\n> +++ b/trace.h\n> @@ -126,66 +126,6 @@ void trace_command_performance(const char **argv);\n>  void trace_verbatim(struct trace_key *key, const void *buf, unsigned len);\n>  uint64_t trace_performance_enter(void);\n>  \n> -#ifndef HAVE_VARIADIC_MACROS\n> -\n> -/**\n> - * Prints a formatted message, similar to printf.\n> - */\n> -__attribute__((format (printf, 1, 2)))\n> -void trace_printf(const char *format, ...);\n\nThis removes the documentation for these functions and doesn't add it\nback on the #else side.  So I think we'd want the following\npreparatory patch.\n\nThanks,\nJonathan\n\n-- >8 --\nSubject: trace: move comments to variadic macro variant of trace functions\n\nNowadays compilers not having variadic macros are the exception.  Move\nAPI documentation to the declarations used in the common case.\n\nSigned-off-by: Jonathan Nieder <jrnieder@gmail.com>\n---\n trace.h | 79 +++++++++++++++++++++++++++------------------------------\n 1 file changed, 38 insertions(+), 41 deletions(-)\n\ndiff --git a/trace.h b/trace.h\nindex 0dbbad0e41..fc7eb0bc72 100644\n--- a/trace.h\n+++ b/trace.h\n@@ -128,56 +128,22 @@ uint64_t trace_performance_enter(void);\n \n #ifndef HAVE_VARIADIC_MACROS\n \n-/**\n- * Prints a formatted message, similar to printf.\n- */\n+/* Fallback implementation that does not add file:line. */\n+\n __attribute__((format (printf, 1, 2)))\n void trace_printf(const char *format, ...);\n \n __attribute__((format (printf, 2, 3)))\n void trace_printf_key(struct trace_key *key, const char *format, ...);\n \n-/**\n- * Prints a formatted message, followed by a quoted list of arguments.\n- */\n __attribute__((format (printf, 2, 3)))\n void trace_argv_printf(const char **argv, const char *format, ...);\n \n-/**\n- * Prints the strbuf, without additional formatting (i.e. doesn't\n- * choke on `%` or even `\\0`).\n- */\n void trace_strbuf(struct trace_key *key, const struct strbuf *data);\n \n-/**\n- * Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled.\n- *\n- * Example:\n- * ------------\n- * uint64_t t = 0;\n- * for (;;) {\n- * \t// ignore\n- * t -= getnanotime();\n- * // code section to measure\n- * t += getnanotime();\n- * // ignore\n- * }\n- * trace_performance(t, \"frotz\");\n- * ------------\n- */\n __attribute__((format (printf, 2, 3)))\n void trace_performance(uint64_t nanos, const char *format, ...);\n \n-/**\n- * Prints elapsed time since 'start' if GIT_TRACE_PERFORMANCE is enabled.\n- *\n- * Example:\n- * ------------\n- * uint64_t start = getnanotime();\n- * // code section to measure\n- * trace_performance_since(start, \"foobar\");\n- * ------------\n- */\n __attribute__((format (printf, 2, 3)))\n void trace_performance_since(uint64_t start, const char *format, ...);\n \n@@ -186,11 +152,6 @@ void trace_performance_leave(const char *format, ...);\n \n #else\n \n-/*\n- * Macros to add file:line - see above for C-style declarations of how these\n- * should be used.\n- */\n-\n /*\n  * TRACE_CONTEXT may be set to __FUNCTION__ if the compiler supports it. The\n  * default is __FILE__, as it is consistent with assert(), and static function\n@@ -227,8 +188,14 @@ void trace_performance_leave(const char *format, ...);\n \t\t\t\t\t    __VA_ARGS__);\t\t    \\\n \t} while (0)\n \n+/**\n+ * Prints a formatted message, similar to printf.\n+ */\n #define trace_printf(...) trace_printf_key(&trace_default_key, __VA_ARGS__)\n \n+/**\n+ * Prints a formatted message, followed by a quoted list of arguments.\n+ */\n #define trace_argv_printf(argv, ...)\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(&trace_default_key))\t\t\t    \\\n@@ -236,12 +203,32 @@ void trace_performance_leave(const char *format, ...);\n \t\t\t\t\t    argv, __VA_ARGS__);\t\t    \\\n \t} while (0)\n \n+/**\n+ * Prints the strbuf, without additional formatting (i.e. doesn't\n+ * choke on `%` or even `\\0`).\n+ */\n #define trace_strbuf(key, data)\t\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(key))\t\t\t\t\t    \\\n \t\t\ttrace_strbuf_fl(TRACE_CONTEXT, __LINE__, key, data);\\\n \t} while (0)\n \n+/**\n+ * Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled.\n+ *\n+ * Example:\n+ * ------------\n+ * uint64_t t = 0;\n+ * for (;;) {\n+ * \t// ignore\n+ * t -= getnanotime();\n+ * // code section to measure\n+ * t += getnanotime();\n+ * // ignore\n+ * }\n+ * trace_performance(t, \"frotz\");\n+ * ------------\n+ */\n #define trace_performance(nanos, ...)\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(&trace_perf_key))\t\t\t    \\\n@@ -249,6 +236,16 @@ void trace_performance_leave(const char *format, ...);\n \t\t\t\t\t     __VA_ARGS__);\t\t    \\\n \t} while (0)\n \n+/**\n+ * Prints elapsed time since 'start' if GIT_TRACE_PERFORMANCE is enabled.\n+ *\n+ * Example:\n+ * ------------\n+ * uint64_t start = getnanotime();\n+ * // code section to measure\n+ * trace_performance_since(start, \"foobar\");\n+ * ------------\n+ */\n #define trace_performance_since(start, ...)\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(&trace_perf_key))\t\t\t    \\\n-- \n2.31.1.818.g46aad6cb9e\n\n"},{"id":"447189","messageId":"cover-v2-0.2-00000000000-20220128T110330Z-avarab@gmail.com","threadId":"55472","inReplyTo":"cover-0.2-00000000000-20210412T105422Z-avarab@gmail.com","subject":"[PATCH v2 0/2] C99: remove hardcoded-out !HAVE_VARIADIC_MACROS code","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-01-28T11:11:07Z","receivedAt":"2022-01-28T11:11:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This v2 of $subject is being submitted on the one-year anniversary of\nthe patch to hardcode away the !HAVE_VARIADIC_MACROS code.\n\nThis is another small step on the way towards changing the usage.[ch]\nAPI so that we can log the actual file/line number of\nusage()/warning()/error()/die() in trace2 events. See a greater RFC\nseries to do that at:\nhttps://lore.kernel.org/git/RFC-cover-00.21-00000000000-20211115T220831Z-avarab@gmail.com/\n\nThis should address all feedback raised on v1, including Junio\nthinking it was a no-go since the submission in April 2021 was was\nrelatively recently after we'd hardcoded away\n!HAVE_VARIADIC_MACROS. We've now had several releases with that code,\nand other recent C99 test balloons since then.\n\nThe since of the range-diff is mainly because in v1 I didn't notice\nthat I needed to move the relevant API docs around, which this v2\ndoes.\n\nÆvar Arnfjörð Bjarmason (2):\n  git-compat-util.h: clarify GCC v.s. C99-specific in comment\n  C99: remove hardcoded-out !HAVE_VARIADIC_MACROS code\n\n Documentation/CodingGuidelines |   3 +\n banned.h                       |   5 --\n git-compat-util.h              |  16 +---\n trace.c                        |  80 +-------------------\n trace.h                        | 133 +++++++++++++--------------------\n trace2.c                       |  39 ----------\n trace2.h                       |  25 -------\n usage.c                        |  15 +---\n 8 files changed, 59 insertions(+), 257 deletions(-)\n\nRange-diff against v1:\n1:  a8cc05cf56f ! 1:  31079a71ecb git-compat-util.h: clarify comment on GCC-specific code\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    git-compat-util.h: clarify comment on GCC-specific code\n    +    git-compat-util.h: clarify GCC v.s. C99-specific in comment\n     \n         Change a comment added in e208f9cc757 (make error()'s constant return\n    -    value more visible, 2012-12-15) to note that the code doesn't only\n    -    depend on variadic macros, which have been a hard dependency since\n    -    765dc168882 (git-compat-util: always enable variadic macros,\n    -    2021-01-28), but also on GCC's handling of __VA_ARGS__. The commit\n    -    message for e208f9cc757 made this clear, but the comment it added did\n    -    not.\n    +    value more visible, 2012-12-15). It's not correct that this is GCC-ism\n    +    anymore, it's code that uses standard C99 features.\n    +\n    +    The comment being changed here pre-dates the HAVE_VARIADIC_MACROS\n    +    define, which we got in e05bed960d3 (trace: add 'file:line' to all\n    +    trace output, 2014-07-12).\n    +\n    +    The original implementation of an error() macro) in e208f9cc757 used a\n    +    GCC-ism with the paste operator (see the commit message for mention of\n    +    it), but that was dropped later by 9798f7e5f9 (Use __VA_ARGS__ for all\n    +    of error's arguments, 2013-02-08), giving us the C99-portable version\n    +    we have now.\n    +\n    +    While we could remove the __GNUC__ define here, it might cause issues\n    +    for other compilers or static analysis systems, so let's not. See\n    +    87fe5df365 (inline constant return from error() function, 2014-05-06)\n    +    for one such issue.\n     \n         See also e05bed960d3 (trace: add 'file:line' to all trace output,\n         2014-07-12) for another comment about GNUC's handling of __VA_ARGS__.\n    @@ Commit message\n     \n      ## git-compat-util.h ##\n     @@ git-compat-util.h: void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));\n    - \n      /*\n       * Let callers be aware of the constant return value; this can help\n    -- * gcc with -Wuninitialized analysis. We restrict this trick to gcc, though,\n    +  * gcc with -Wuninitialized analysis. We restrict this trick to gcc, though,\n     - * because some compilers may not support variadic macros. Since we're only\n     - * trying to help gcc, anyway, it's OK; other compilers will fall back to\n     - * using the function as usual.\n    -+ * gcc with -Wuninitialized analysis.\n    -+ *\n    -+ * We restrict this trick to gcc, though, because while we rely on the\n    -+ * presence of C99 variadic macros, this code also relies on the\n    -+ * non-standard behavior of GCC's __VA_ARGS__, allowing error() to\n    -+ * work even if no format specifiers are passed to error().\n    -+ *\n    -+ * Since we're only trying to help gcc, anyway, it's OK; other\n    -+ * compilers will fall back to using the function as usual.\n    ++ * because other compilers may be confused by this.\n       */\n      #if defined(__GNUC__)\n      static inline int const_error(void)\n2:  f12e3cad57d ! 2:  966d96505cb C99 support: remove non-HAVE_VARIADIC_MACROS code\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    C99 support: remove non-HAVE_VARIADIC_MACROS code\n    +    C99: remove hardcoded-out !HAVE_VARIADIC_MACROS code\n     \n    -    Remove code that depend on HAVE_VARIADIC_MACROS not being set. Since\n    -    765dc168882 (git-compat-util: always enable variadic macros,\n    -    2021-01-28) we've unconditionally defined it to be true, and that\n    -    change went out with v2.31.0. This should have given packagers enough\n    -    time to discover whether variadic macros were an issue.\n    +    Remove the \"else\" branches of the HAVE_VARIADIC_MACROS macro, which\n    +    has been unconditionally omitted since 765dc168882 (git-compat-util:\n    +    always enable variadic macros, 2021-01-28).\n     \n    -    It seems that they weren't, so let's update the coding guidelines and\n    -    remove all the fallback code for the non-HAVE_VARIADIC_MACROS case.\n    +    Since they were hardcoded out anyone trying to compile a version of\n    +    git v2.31.0 or later would have had a compilation error. 10 months\n    +    across a few releases since then should have been enough time for\n    +    anyone who cared to run into that and report the issue.\n    +\n    +    In addition to that, for anyone unsetting HAVE_VARIADIC_MACROS we've\n    +    been emitting extremely verbose warnings since at least\n    +    ee4512ed481 (trace2: create new combined trace facility,\n    +    2019-02-22). That's because there is no such thing as a\n    +    \"region_enter_printf\" or \"region_leave_printf\" format, so at least\n    +    under GCC and Clang everything that includes trace.h (almost every\n    +    file) emits a couple of warnings about that.\n    +\n    +    There's a large benefit to being able to have a hard dependency rely\n    +    on variadic macros, the code surrounding usage.c is hard to maintain\n    +    if we need to write two implementations of everything, and by relying\n    +    on \"__FILE__\" and \"__LINE__\" along with \"__VA_ARGS__\" we can in the\n    +    future make error(), die() etc. log where they were called from. We've\n    +    also recently merged d67fc4bf0ba (Merge branch 'bc/require-c99',\n    +    2021-12-10) which further cements our hard dependency on C99.\n    +\n    +    So let's delete the fallback code, and update our CodingGuidelines to\n    +    note that we depend on this. The added bullet-point starts with\n    +    lower-case for consistency with other bullet-points in that section.\n    +\n    +    The diff in \"trace.h\" is relatively hard to read, since we need to\n    +    retain the existing API docs, which were comments on the code used if\n    +    HAVE_VARIADIC_MACROS was not defined.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    @@ git-compat-util.h: static inline int regexec_buf(const regex_t *preg, const char\n     \n      ## trace.c ##\n     @@ trace.c: static int prepare_trace_line(const char *file, int line,\n    - \tstrbuf_addf(buf, \"%02d:%02d:%02d.%06ld \", tm.tm_hour, tm.tm_min,\n    - \t\t    tm.tm_sec, (long) tv.tv_usec);\n    - \n    + \tgettimeofday(&tv, NULL);\n    + \tsecs = tv.tv_sec;\n    + \tlocaltime_r(&secs, &tm);\n    +-\tstrbuf_addf(buf, \"%02d:%02d:%02d.%06ld \", tm.tm_hour, tm.tm_min,\n    +-\t\t    tm.tm_sec, (long) tv.tv_usec);\n    +-\n     -#ifdef HAVE_VARIADIC_MACROS\n    - \t/* print file:line */\n    - \tstrbuf_addf(buf, \"%s:%d \", file, line);\n    +-\t/* print file:line */\n    +-\tstrbuf_addf(buf, \"%s:%d \", file, line);\n    ++\tstrbuf_addf(buf, \"%02d:%02d:%02d.%06ld %s:%d\", tm.tm_hour, tm.tm_min,\n    ++\t\t    tm.tm_sec, (long) tv.tv_usec, file, line);\n      \t/* align trace output (column 40 catches most files names in git) */\n      \twhile (buf->len < 40)\n      \t\tstrbuf_addch(buf, ' ');\n    @@ trace.h: void trace_command_performance(const char **argv);\n     - */\n     -void trace_strbuf(struct trace_key *key, const struct strbuf *data);\n     -\n    --/**\n    + /**\n     - * Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled.\n    -- *\n    ++ * Macros to add the file:line of the calling code, instead of that of\n    ++ * the trace function itself.\n    +  *\n     - * Example:\n     - * ------------\n     - * uint64_t t = 0;\n    @@ trace.h: void trace_command_performance(const char **argv);\n     -\n     -#else\n     -\n    - /*\n    -  * Macros to add file:line - see above for C-style declarations of how these\n    -  * should be used.\n    +-/*\n    +- * Macros to add file:line - see above for C-style declarations of how these\n    +- * should be used.\n    +- */\n    +-\n    +-/*\n    +- * TRACE_CONTEXT may be set to __FUNCTION__ if the compiler supports it. The\n    +- * default is __FILE__, as it is consistent with assert(), and static function\n    +- * names are not necessarily unique.\n    +- *\n    +- * __FILE__ \":\" __FUNCTION__ doesn't work with GNUC, as __FILE__ is supplied\n    +- * by the preprocessor as a string literal, and __FUNCTION__ is filled in by\n    +- * the compiler as a string constant.\n    +- */\n    +-#ifndef TRACE_CONTEXT\n    +-# define TRACE_CONTEXT __FILE__\n    +-#endif\n    +-\n    +-/*\n    +  * Note: with C99 variadic macros, __VA_ARGS__ must include the last fixed\n    +  * parameter ('format' in this case). Otherwise, a call without variable\n    +  * arguments will have a surplus ','. E.g.:\n    +@@ trace.h: void trace_performance_leave(const char *format, ...);\n    +  *\n    +  * which is invalid (note the ',)'). With GNUC, '##__VA_ARGS__' drops the\n    +  * comma, but this is non-standard.\n    ++ *\n    ++ * TRACE_CONTEXT may be set to __FUNCTION__ if the compiler supports it. The\n    ++ * default is __FILE__, as it is consistent with assert(), and static function\n    ++ * names are not necessarily unique.\n    ++ *\n    ++ * __FILE__ \":\" __FUNCTION__ doesn't work with GNUC, as __FILE__ is supplied\n    ++ * by the preprocessor as a string literal, and __FUNCTION__ is filled in by\n    ++ * the compiler as a string constant.\n    ++ */\n    ++#ifndef TRACE_CONTEXT\n    ++# define TRACE_CONTEXT __FILE__\n    ++#endif\n    ++\n    ++/**\n    ++ * Prints a formatted message, similar to printf.\n    +  */\n    ++#define trace_printf(...) trace_printf_key(&trace_default_key, __VA_ARGS__)\n    + \n    + #define trace_printf_key(key, ...)\t\t\t\t\t    \\\n    + \tdo {\t\t\t\t\t\t\t\t    \\\n    +@@ trace.h: void trace_performance_leave(const char *format, ...);\n    + \t\t\t\t\t    __VA_ARGS__);\t\t    \\\n    + \t} while (0)\n    + \n    +-#define trace_printf(...) trace_printf_key(&trace_default_key, __VA_ARGS__)\n    +-\n    ++/**\n    ++ * Prints a formatted message, followed by a quoted list of arguments.\n    ++ */\n    + #define trace_argv_printf(argv, ...)\t\t\t\t\t    \\\n    + \tdo {\t\t\t\t\t\t\t\t    \\\n    + \t\tif (trace_pass_fl(&trace_default_key))\t\t\t    \\\n    +@@ trace.h: void trace_performance_leave(const char *format, ...);\n    + \t\t\t\t\t    argv, __VA_ARGS__);\t\t    \\\n    + \t} while (0)\n    + \n    ++/**\n    ++ * Prints the strbuf, without additional formatting (i.e. doesn't\n    ++ * choke on `%` or even `\\0`).\n    ++ */\n    + #define trace_strbuf(key, data)\t\t\t\t\t\t    \\\n    + \tdo {\t\t\t\t\t\t\t\t    \\\n    + \t\tif (trace_pass_fl(key))\t\t\t\t\t    \\\n    + \t\t\ttrace_strbuf_fl(TRACE_CONTEXT, __LINE__, key, data);\\\n    + \t} while (0)\n    + \n    ++/**\n    ++ * Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled.\n    ++ *\n    ++ * Example:\n    ++ * ------------\n    ++ * uint64_t t = 0;\n    ++ * for (;;) {\n    ++ * \t// ignore\n    ++ * t -= getnanotime();\n    ++ * // code section to measure\n    ++ * t += getnanotime();\n    ++ * // ignore\n    ++ * }\n    ++ * trace_performance(t, \"frotz\");\n    ++ * ------------\n    ++ */\n    + #define trace_performance(nanos, ...)\t\t\t\t\t    \\\n    + \tdo {\t\t\t\t\t\t\t\t    \\\n    + \t\tif (trace_pass_fl(&trace_perf_key))\t\t\t    \\\n    +@@ trace.h: void trace_performance_leave(const char *format, ...);\n    + \t\t\t\t\t     __VA_ARGS__);\t\t    \\\n    + \t} while (0)\n    + \n    ++/**\n    ++ * Prints elapsed time since 'start' if GIT_TRACE_PERFORMANCE is enabled.\n    ++ *\n    ++ * Example:\n    ++ * ------------\n    ++ * uint64_t start = getnanotime();\n    ++ * // code section to measure\n    ++ * trace_performance_since(start, \"foobar\");\n    ++ * ------------\n    ++ */\n    + #define trace_performance_since(start, ...)\t\t\t\t    \\\n    + \tdo {\t\t\t\t\t\t\t\t    \\\n    + \t\tif (trace_pass_fl(&trace_perf_key))\t\t\t    \\\n    +@@ trace.h: void trace_performance_leave(const char *format, ...);\n    + \t\t\t\t\t\t   __VA_ARGS__);\t    \\\n    + \t} while (0)\n    + \n    ++\n    + /* backend functions, use non-*fl macros instead */\n    + __attribute__((format (printf, 4, 5)))\n    + void trace_printf_key_fl(const char *file, int line, struct trace_key *key,\n     @@ trace.h: static inline int trace_pass_fl(struct trace_key *key)\n      \treturn key->fd || !key->initialized;\n      }\n    @@ trace2.h: void trace2_printf_va_fl(const char *file, int line, const char *fmt,\n       * Optional platform-specific code to dump information about the\n     \n      ## usage.c ##\n    +@@ usage.c: static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_lis\n    + \tva_copy(params_copy, params);\n    + \n    + \t/* truncation via snprintf is OK here */\n    +-\tif (file)\n    +-\t\tsnprintf(prefix, sizeof(prefix), \"BUG: %s:%d: \", file, line);\n    +-\telse\n    +-\t\tsnprintf(prefix, sizeof(prefix), \"BUG: \");\n    ++\tsnprintf(prefix, sizeof(prefix), \"BUG: %s:%d: \", file, line);\n    + \n    + \tvreportf(prefix, fmt, params);\n    + \n     @@ usage.c: static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_lis\n      \tabort();\n      }\n-- \n2.35.0.912.ga4a35ddedc3\n\n"},{"id":"447188","messageId":"patch-v2-1.2-31079a71ecb-20220128T110330Z-avarab@gmail.com","threadId":"55472","inReplyTo":"cover-v2-0.2-00000000000-20220128T110330Z-avarab@gmail.com","subject":"[PATCH v2 1/2] git-compat-util.h: clarify GCC v.s. C99-specific in comment","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-01-28T11:11:08Z","receivedAt":"2022-01-28T11:11:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change a comment added in e208f9cc757 (make error()'s constant return\nvalue more visible, 2012-12-15). It's not correct that this is GCC-ism\nanymore, it's code that uses standard C99 features.\n\nThe comment being changed here pre-dates the HAVE_VARIADIC_MACROS\ndefine, which we got in e05bed960d3 (trace: add 'file:line' to all\ntrace output, 2014-07-12).\n\nThe original implementation of an error() macro) in e208f9cc757 used a\nGCC-ism with the paste operator (see the commit message for mention of\nit), but that was dropped later by 9798f7e5f9 (Use __VA_ARGS__ for all\nof error's arguments, 2013-02-08), giving us the C99-portable version\nwe have now.\n\nWhile we could remove the __GNUC__ define here, it might cause issues\nfor other compilers or static analysis systems, so let's not. See\n87fe5df365 (inline constant return from error() function, 2014-05-06)\nfor one such issue.\n\nSee also e05bed960d3 (trace: add 'file:line' to all trace output,\n2014-07-12) for another comment about GNUC's handling of __VA_ARGS__.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n git-compat-util.h | 4 +---\n 1 file changed, 1 insertion(+), 3 deletions(-)\n\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 1229c8296b9..57f20439ef6 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -522,9 +522,7 @@ void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));\n /*\n  * Let callers be aware of the constant return value; this can help\n  * gcc with -Wuninitialized analysis. We restrict this trick to gcc, though,\n- * because some compilers may not support variadic macros. Since we're only\n- * trying to help gcc, anyway, it's OK; other compilers will fall back to\n- * using the function as usual.\n+ * because other compilers may be confused by this.\n  */\n #if defined(__GNUC__)\n static inline int const_error(void)\n-- \n2.35.0.912.ga4a35ddedc3\n\n"},{"id":"447190","messageId":"patch-v2-2.2-966d96505cb-20220128T110330Z-avarab@gmail.com","threadId":"55472","inReplyTo":"cover-v2-0.2-00000000000-20220128T110330Z-avarab@gmail.com","subject":"[PATCH v2 2/2] C99: remove hardcoded-out !HAVE_VARIADIC_MACROS code","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-01-28T11:11:09Z","receivedAt":"2022-01-28T11:11:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove the \"else\" branches of the HAVE_VARIADIC_MACROS macro, which\nhas been unconditionally omitted since 765dc168882 (git-compat-util:\nalways enable variadic macros, 2021-01-28).\n\nSince they were hardcoded out anyone trying to compile a version of\ngit v2.31.0 or later would have had a compilation error. 10 months\nacross a few releases since then should have been enough time for\nanyone who cared to run into that and report the issue.\n\nIn addition to that, for anyone unsetting HAVE_VARIADIC_MACROS we've\nbeen emitting extremely verbose warnings since at least\nee4512ed481 (trace2: create new combined trace facility,\n2019-02-22). That's because there is no such thing as a\n\"region_enter_printf\" or \"region_leave_printf\" format, so at least\nunder GCC and Clang everything that includes trace.h (almost every\nfile) emits a couple of warnings about that.\n\nThere's a large benefit to being able to have a hard dependency rely\non variadic macros, the code surrounding usage.c is hard to maintain\nif we need to write two implementations of everything, and by relying\non \"__FILE__\" and \"__LINE__\" along with \"__VA_ARGS__\" we can in the\nfuture make error(), die() etc. log where they were called from. We've\nalso recently merged d67fc4bf0ba (Merge branch 'bc/require-c99',\n2021-12-10) which further cements our hard dependency on C99.\n\nSo let's delete the fallback code, and update our CodingGuidelines to\nnote that we depend on this. The added bullet-point starts with\nlower-case for consistency with other bullet-points in that section.\n\nThe diff in \"trace.h\" is relatively hard to read, since we need to\nretain the existing API docs, which were comments on the code used if\nHAVE_VARIADIC_MACROS was not defined.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/CodingGuidelines |   3 +\n banned.h                       |   5 --\n git-compat-util.h              |  12 ---\n trace.c                        |  80 +-------------------\n trace.h                        | 133 +++++++++++++--------------------\n trace2.c                       |  39 ----------\n trace2.h                       |  25 -------\n usage.c                        |  15 +---\n 8 files changed, 58 insertions(+), 254 deletions(-)\n\ndiff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines\nindex 0e27b5395d8..1858075a159 100644\n--- a/Documentation/CodingGuidelines\n+++ b/Documentation/CodingGuidelines\n@@ -210,6 +210,9 @@ For C programs:\n    . since mid 2017 with 512f41cf, we have been using designated\n      initializers for array (e.g. \"int array[10] = { [5] = 2 }\").\n \n+   . since early 2021 with 765dc168882, we have been using variadic\n+     macros, mostly for printf-like trace and debug macros.\n+\n    These used to be forbidden, but we have not heard any breakage\n    report, and they are assumed to be safe.\n \ndiff --git a/banned.h b/banned.h\nindex 7ab4f2e4921..6ccf46bc197 100644\n--- a/banned.h\n+++ b/banned.h\n@@ -21,13 +21,8 @@\n \n #undef sprintf\n #undef vsprintf\n-#ifdef HAVE_VARIADIC_MACROS\n #define sprintf(...) BANNED(sprintf)\n #define vsprintf(...) BANNED(vsprintf)\n-#else\n-#define sprintf(buf,fmt,arg) BANNED(sprintf)\n-#define vsprintf(buf,fmt,arg) BANNED(vsprintf)\n-#endif\n \n #undef gmtime\n #define gmtime(t) BANNED(gmtime)\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 57f20439ef6..9943c182a62 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -1244,24 +1244,12 @@ static inline int regexec_buf(const regex_t *preg, const char *buf, size_t size,\n #endif\n #endif\n \n-/*\n- * This is always defined as a first step towards making the use of variadic\n- * macros unconditional. If it causes compilation problems on your platform,\n- * please report it to the Git mailing list at git@vger.kernel.org.\n- */\n-#define HAVE_VARIADIC_MACROS 1\n-\n /* usage.c: only to be used for testing BUG() implementation (see test-tool) */\n extern int BUG_exit_code;\n \n-#ifdef HAVE_VARIADIC_MACROS\n __attribute__((format (printf, 3, 4))) NORETURN\n void BUG_fl(const char *file, int line, const char *fmt, ...);\n #define BUG(...) BUG_fl(__FILE__, __LINE__, __VA_ARGS__)\n-#else\n-__attribute__((format (printf, 1, 2))) NORETURN\n-void BUG(const char *fmt, ...);\n-#endif\n \n /*\n  * Preserves errno, prints a message, but gives no warning for ENOENT.\ndiff --git a/trace.c b/trace.c\nindex f726686fd92..794a087c21e 100644\n--- a/trace.c\n+++ b/trace.c\n@@ -108,16 +108,11 @@ static int prepare_trace_line(const char *file, int line,\n \tgettimeofday(&tv, NULL);\n \tsecs = tv.tv_sec;\n \tlocaltime_r(&secs, &tm);\n-\tstrbuf_addf(buf, \"%02d:%02d:%02d.%06ld \", tm.tm_hour, tm.tm_min,\n-\t\t    tm.tm_sec, (long) tv.tv_usec);\n-\n-#ifdef HAVE_VARIADIC_MACROS\n-\t/* print file:line */\n-\tstrbuf_addf(buf, \"%s:%d \", file, line);\n+\tstrbuf_addf(buf, \"%02d:%02d:%02d.%06ld %s:%d\", tm.tm_hour, tm.tm_min,\n+\t\t    tm.tm_sec, (long) tv.tv_usec, file, line);\n \t/* align trace output (column 40 catches most files names in git) */\n \twhile (buf->len < 40)\n \t\tstrbuf_addch(buf, ' ');\n-#endif\n \n \treturn 1;\n }\n@@ -229,74 +224,6 @@ static void trace_performance_vprintf_fl(const char *file, int line,\n \tstrbuf_release(&buf);\n }\n \n-#ifndef HAVE_VARIADIC_MACROS\n-\n-void trace_printf(const char *format, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, format);\n-\ttrace_vprintf_fl(NULL, 0, &trace_default_key, format, ap);\n-\tva_end(ap);\n-}\n-\n-void trace_printf_key(struct trace_key *key, const char *format, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, format);\n-\ttrace_vprintf_fl(NULL, 0, key, format, ap);\n-\tva_end(ap);\n-}\n-\n-void trace_argv_printf(const char **argv, const char *format, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, format);\n-\ttrace_argv_vprintf_fl(NULL, 0, argv, format, ap);\n-\tva_end(ap);\n-}\n-\n-void trace_strbuf(struct trace_key *key, const struct strbuf *data)\n-{\n-\ttrace_strbuf_fl(NULL, 0, key, data);\n-}\n-\n-void trace_performance(uint64_t nanos, const char *format, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, format);\n-\ttrace_performance_vprintf_fl(NULL, 0, nanos, format, ap);\n-\tva_end(ap);\n-}\n-\n-void trace_performance_since(uint64_t start, const char *format, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, format);\n-\ttrace_performance_vprintf_fl(NULL, 0, getnanotime() - start,\n-\t\t\t\t     format, ap);\n-\tva_end(ap);\n-}\n-\n-void trace_performance_leave(const char *format, ...)\n-{\n-\tva_list ap;\n-\tuint64_t since;\n-\n-\tif (perf_indent)\n-\t\tperf_indent--;\n-\n-\tif (!format) /* Allow callers to leave without tracing anything */\n-\t\treturn;\n-\n-\tsince = perf_start_times[perf_indent];\n-\tva_start(ap, format);\n-\ttrace_performance_vprintf_fl(NULL, 0, getnanotime() - since,\n-\t\t\t\t     format, ap);\n-\tva_end(ap);\n-}\n-\n-#else\n-\n void trace_printf_key_fl(const char *file, int line, struct trace_key *key,\n \t\t\t const char *format, ...)\n {\n@@ -342,9 +269,6 @@ void trace_performance_leave_fl(const char *file, int line,\n \tva_end(ap);\n }\n \n-#endif /* HAVE_VARIADIC_MACROS */\n-\n-\n static const char *quote_crnl(const char *path)\n {\n \tstatic struct strbuf new_path = STRBUF_INIT;\ndiff --git a/trace.h b/trace.h\nindex e25984051aa..7ad2f03d07c 100644\n--- a/trace.h\n+++ b/trace.h\n@@ -126,85 +126,10 @@ void trace_command_performance(const char **argv);\n void trace_verbatim(struct trace_key *key, const void *buf, unsigned len);\n uint64_t trace_performance_enter(void);\n \n-#ifndef HAVE_VARIADIC_MACROS\n-\n-/**\n- * Prints a formatted message, similar to printf.\n- */\n-__attribute__((format (printf, 1, 2)))\n-void trace_printf(const char *format, ...);\n-\n-__attribute__((format (printf, 2, 3)))\n-void trace_printf_key(struct trace_key *key, const char *format, ...);\n-\n-/**\n- * Prints a formatted message, followed by a quoted list of arguments.\n- */\n-__attribute__((format (printf, 2, 3)))\n-void trace_argv_printf(const char **argv, const char *format, ...);\n-\n-/**\n- * Prints the strbuf, without additional formatting (i.e. doesn't\n- * choke on `%` or even `\\0`).\n- */\n-void trace_strbuf(struct trace_key *key, const struct strbuf *data);\n-\n /**\n- * Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled.\n+ * Macros to add the file:line of the calling code, instead of that of\n+ * the trace function itself.\n  *\n- * Example:\n- * ------------\n- * uint64_t t = 0;\n- * for (;;) {\n- * \t// ignore\n- * t -= getnanotime();\n- * // code section to measure\n- * t += getnanotime();\n- * // ignore\n- * }\n- * trace_performance(t, \"frotz\");\n- * ------------\n- */\n-__attribute__((format (printf, 2, 3)))\n-void trace_performance(uint64_t nanos, const char *format, ...);\n-\n-/**\n- * Prints elapsed time since 'start' if GIT_TRACE_PERFORMANCE is enabled.\n- *\n- * Example:\n- * ------------\n- * uint64_t start = getnanotime();\n- * // code section to measure\n- * trace_performance_since(start, \"foobar\");\n- * ------------\n- */\n-__attribute__((format (printf, 2, 3)))\n-void trace_performance_since(uint64_t start, const char *format, ...);\n-\n-__attribute__((format (printf, 1, 2)))\n-void trace_performance_leave(const char *format, ...);\n-\n-#else\n-\n-/*\n- * Macros to add file:line - see above for C-style declarations of how these\n- * should be used.\n- */\n-\n-/*\n- * TRACE_CONTEXT may be set to __FUNCTION__ if the compiler supports it. The\n- * default is __FILE__, as it is consistent with assert(), and static function\n- * names are not necessarily unique.\n- *\n- * __FILE__ \":\" __FUNCTION__ doesn't work with GNUC, as __FILE__ is supplied\n- * by the preprocessor as a string literal, and __FUNCTION__ is filled in by\n- * the compiler as a string constant.\n- */\n-#ifndef TRACE_CONTEXT\n-# define TRACE_CONTEXT __FILE__\n-#endif\n-\n-/*\n  * Note: with C99 variadic macros, __VA_ARGS__ must include the last fixed\n  * parameter ('format' in this case). Otherwise, a call without variable\n  * arguments will have a surplus ','. E.g.:\n@@ -218,7 +143,23 @@ void trace_performance_leave(const char *format, ...);\n  *\n  * which is invalid (note the ',)'). With GNUC, '##__VA_ARGS__' drops the\n  * comma, but this is non-standard.\n+ *\n+ * TRACE_CONTEXT may be set to __FUNCTION__ if the compiler supports it. The\n+ * default is __FILE__, as it is consistent with assert(), and static function\n+ * names are not necessarily unique.\n+ *\n+ * __FILE__ \":\" __FUNCTION__ doesn't work with GNUC, as __FILE__ is supplied\n+ * by the preprocessor as a string literal, and __FUNCTION__ is filled in by\n+ * the compiler as a string constant.\n+ */\n+#ifndef TRACE_CONTEXT\n+# define TRACE_CONTEXT __FILE__\n+#endif\n+\n+/**\n+ * Prints a formatted message, similar to printf.\n  */\n+#define trace_printf(...) trace_printf_key(&trace_default_key, __VA_ARGS__)\n \n #define trace_printf_key(key, ...)\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n@@ -227,8 +168,9 @@ void trace_performance_leave(const char *format, ...);\n \t\t\t\t\t    __VA_ARGS__);\t\t    \\\n \t} while (0)\n \n-#define trace_printf(...) trace_printf_key(&trace_default_key, __VA_ARGS__)\n-\n+/**\n+ * Prints a formatted message, followed by a quoted list of arguments.\n+ */\n #define trace_argv_printf(argv, ...)\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(&trace_default_key))\t\t\t    \\\n@@ -236,12 +178,32 @@ void trace_performance_leave(const char *format, ...);\n \t\t\t\t\t    argv, __VA_ARGS__);\t\t    \\\n \t} while (0)\n \n+/**\n+ * Prints the strbuf, without additional formatting (i.e. doesn't\n+ * choke on `%` or even `\\0`).\n+ */\n #define trace_strbuf(key, data)\t\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(key))\t\t\t\t\t    \\\n \t\t\ttrace_strbuf_fl(TRACE_CONTEXT, __LINE__, key, data);\\\n \t} while (0)\n \n+/**\n+ * Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled.\n+ *\n+ * Example:\n+ * ------------\n+ * uint64_t t = 0;\n+ * for (;;) {\n+ * \t// ignore\n+ * t -= getnanotime();\n+ * // code section to measure\n+ * t += getnanotime();\n+ * // ignore\n+ * }\n+ * trace_performance(t, \"frotz\");\n+ * ------------\n+ */\n #define trace_performance(nanos, ...)\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(&trace_perf_key))\t\t\t    \\\n@@ -249,6 +211,16 @@ void trace_performance_leave(const char *format, ...);\n \t\t\t\t\t     __VA_ARGS__);\t\t    \\\n \t} while (0)\n \n+/**\n+ * Prints elapsed time since 'start' if GIT_TRACE_PERFORMANCE is enabled.\n+ *\n+ * Example:\n+ * ------------\n+ * uint64_t start = getnanotime();\n+ * // code section to measure\n+ * trace_performance_since(start, \"foobar\");\n+ * ------------\n+ */\n #define trace_performance_since(start, ...)\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(&trace_perf_key))\t\t\t    \\\n@@ -265,6 +237,7 @@ void trace_performance_leave(const char *format, ...);\n \t\t\t\t\t\t   __VA_ARGS__);\t    \\\n \t} while (0)\n \n+\n /* backend functions, use non-*fl macros instead */\n __attribute__((format (printf, 4, 5)))\n void trace_printf_key_fl(const char *file, int line, struct trace_key *key,\n@@ -285,6 +258,4 @@ static inline int trace_pass_fl(struct trace_key *key)\n \treturn key->fd || !key->initialized;\n }\n \n-#endif /* HAVE_VARIADIC_MACROS */\n-\n #endif /* TRACE_H */\ndiff --git a/trace2.c b/trace2.c\nindex b2d471526fd..179caa72cfe 100644\n--- a/trace2.c\n+++ b/trace2.c\n@@ -641,20 +641,6 @@ void trace2_region_enter_printf_fl(const char *file, int line,\n \tva_end(ap);\n }\n \n-#ifndef HAVE_VARIADIC_MACROS\n-void trace2_region_enter_printf(const char *category, const char *label,\n-\t\t\t\tconst struct repository *repo, const char *fmt,\n-\t\t\t\t...)\n-{\n-\tva_list ap;\n-\n-\tva_start(ap, fmt);\n-\ttrace2_region_enter_printf_va_fl(NULL, 0, category, label, repo, fmt,\n-\t\t\t\t\t ap);\n-\tva_end(ap);\n-}\n-#endif\n-\n void trace2_region_leave_printf_va_fl(const char *file, int line,\n \t\t\t\t      const char *category, const char *label,\n \t\t\t\t      const struct repository *repo,\n@@ -717,20 +703,6 @@ void trace2_region_leave_printf_fl(const char *file, int line,\n \tva_end(ap);\n }\n \n-#ifndef HAVE_VARIADIC_MACROS\n-void trace2_region_leave_printf(const char *category, const char *label,\n-\t\t\t\tconst struct repository *repo, const char *fmt,\n-\t\t\t\t...)\n-{\n-\tva_list ap;\n-\n-\tva_start(ap, fmt);\n-\ttrace2_region_leave_printf_va_fl(NULL, 0, category, label, repo, fmt,\n-\t\t\t\t\t ap);\n-\tva_end(ap);\n-}\n-#endif\n-\n void trace2_data_string_fl(const char *file, int line, const char *category,\n \t\t\t   const struct repository *repo, const char *key,\n \t\t\t   const char *value)\n@@ -826,17 +798,6 @@ void trace2_printf_fl(const char *file, int line, const char *fmt, ...)\n \tva_end(ap);\n }\n \n-#ifndef HAVE_VARIADIC_MACROS\n-void trace2_printf(const char *fmt, ...)\n-{\n-\tva_list ap;\n-\n-\tva_start(ap, fmt);\n-\ttrace2_printf_va_fl(NULL, 0, fmt, ap);\n-\tva_end(ap);\n-}\n-#endif\n-\n const char *trace2_session_id(void)\n {\n \treturn tr2_sid_get();\ndiff --git a/trace2.h b/trace2.h\nindex 0cc7b5f5312..1b109f57d0a 100644\n--- a/trace2.h\n+++ b/trace2.h\n@@ -397,18 +397,9 @@ void trace2_region_enter_printf_fl(const char *file, int line,\n \t\t\t\t   const struct repository *repo,\n \t\t\t\t   const char *fmt, ...);\n \n-#ifdef HAVE_VARIADIC_MACROS\n #define trace2_region_enter_printf(category, label, repo, ...)                 \\\n \ttrace2_region_enter_printf_fl(__FILE__, __LINE__, (category), (label), \\\n \t\t\t\t      (repo), __VA_ARGS__)\n-#else\n-/* clang-format off */\n-__attribute__((format (region_enter_printf, 4, 5)))\n-void trace2_region_enter_printf(const char *category, const char *label,\n-\t\t\t\tconst struct repository *repo, const char *fmt,\n-\t\t\t\t...);\n-/* clang-format on */\n-#endif\n \n /**\n  * Emit a 'region_leave' event for <category>.<label> with optional\n@@ -442,18 +433,9 @@ void trace2_region_leave_printf_fl(const char *file, int line,\n \t\t\t\t   const struct repository *repo,\n \t\t\t\t   const char *fmt, ...);\n \n-#ifdef HAVE_VARIADIC_MACROS\n #define trace2_region_leave_printf(category, label, repo, ...)                 \\\n \ttrace2_region_leave_printf_fl(__FILE__, __LINE__, (category), (label), \\\n \t\t\t\t      (repo), __VA_ARGS__)\n-#else\n-/* clang-format off */\n-__attribute__((format (region_leave_printf, 4, 5)))\n-void trace2_region_leave_printf(const char *category, const char *label,\n-\t\t\t\tconst struct repository *repo, const char *fmt,\n-\t\t\t\t...);\n-/* clang-format on */\n-#endif\n \n /**\n  * Emit a key-value pair 'data' event of the form <category>.<key> = <value>.\n@@ -506,14 +488,7 @@ void trace2_printf_va_fl(const char *file, int line, const char *fmt,\n \n void trace2_printf_fl(const char *file, int line, const char *fmt, ...);\n \n-#ifdef HAVE_VARIADIC_MACROS\n #define trace2_printf(...) trace2_printf_fl(__FILE__, __LINE__, __VA_ARGS__)\n-#else\n-/* clang-format off */\n-__attribute__((format (printf, 1, 2)))\n-void trace2_printf(const char *fmt, ...);\n-/* clang-format on */\n-#endif\n \n /*\n  * Optional platform-specific code to dump information about the\ndiff --git a/usage.c b/usage.c\nindex 9943dd8742e..b738dd178b3 100644\n--- a/usage.c\n+++ b/usage.c\n@@ -299,10 +299,7 @@ static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_lis\n \tva_copy(params_copy, params);\n \n \t/* truncation via snprintf is OK here */\n-\tif (file)\n-\t\tsnprintf(prefix, sizeof(prefix), \"BUG: %s:%d: \", file, line);\n-\telse\n-\t\tsnprintf(prefix, sizeof(prefix), \"BUG: \");\n+\tsnprintf(prefix, sizeof(prefix), \"BUG: %s:%d: \", file, line);\n \n \tvreportf(prefix, fmt, params);\n \n@@ -317,7 +314,6 @@ static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_lis\n \tabort();\n }\n \n-#ifdef HAVE_VARIADIC_MACROS\n NORETURN void BUG_fl(const char *file, int line, const char *fmt, ...)\n {\n \tva_list ap;\n@@ -325,15 +321,6 @@ NORETURN void BUG_fl(const char *file, int line, const char *fmt, ...)\n \tBUG_vfl(file, line, fmt, ap);\n \tva_end(ap);\n }\n-#else\n-NORETURN void BUG(const char *fmt, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, fmt);\n-\tBUG_vfl(NULL, 0, fmt, ap);\n-\tva_end(ap);\n-}\n-#endif\n \n #ifdef SUPPRESS_ANNOTATED_LEAKS\n void unleak_memory(const void *ptr, size_t len)\n-- \n2.35.0.912.ga4a35ddedc3\n\n"},{"id":"447246","messageId":"xmqq4k5nwkl5.fsf@gitster.g","threadId":"55472","inReplyTo":"patch-v2-2.2-966d96505cb-20220128T110330Z-avarab@gmail.com","subject":"Re: [PATCH v2 2/2] C99: remove hardcoded-out !HAVE_VARIADIC_MACROS code","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-01-28T22:40:06Z","receivedAt":"2022-01-28T22:40:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> Remove the \"else\" branches of the HAVE_VARIADIC_MACROS macro, which\n> has been unconditionally omitted since 765dc168882 (git-compat-util:\n> always enable variadic macros, 2021-01-28).\n>\n> Since they were hardcoded out anyone trying to compile a version of\n\n\"compile\" -> \"use a compiler without variadic macro support to compile\"\n\n> git v2.31.0 or later would have had a compilation error. 10 months\n> across a few releases since then should have been enough time for\n> anyone who cared to run into that and report the issue.\n\nOK.\n\nThe verb \"hardcode out\" sounds iffy to me, but the reasoning above\nand all the additions and removals in in this patch look quite\nsensible.\n\nSome paragraphs that merely got moved probably needs more work,\nthough.\n\n>  Documentation/CodingGuidelines |   3 +\n>  banned.h                       |   5 --\n>  git-compat-util.h              |  12 ---\n>  trace.c                        |  80 +-------------------\n>  trace.h                        | 133 +++++++++++++--------------------\n>  trace2.c                       |  39 ----------\n>  trace2.h                       |  25 -------\n>  usage.c                        |  15 +---\n>  8 files changed, 58 insertions(+), 254 deletions(-)\n>\n> diff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines\n> index 0e27b5395d8..1858075a159 100644\n> --- a/Documentation/CodingGuidelines\n> +++ b/Documentation/CodingGuidelines\n> @@ -210,6 +210,9 @@ For C programs:\n>     . since mid 2017 with 512f41cf, we have been using designated\n>       initializers for array (e.g. \"int array[10] = { [5] = 2 }\").\n>  \n> +   . since early 2021 with 765dc168882, we have been using variadic\n> +     macros, mostly for printf-like trace and debug macros.\n\nOK.\n\n> @@ -218,7 +143,23 @@ void trace_performance_leave(const char *format, ...);\n>   *\n>   * which is invalid (note the ',)'). With GNUC, '##__VA_ARGS__' drops the\n>   * comma, but this is non-standard.\n> + *\n> + * TRACE_CONTEXT may be set to __FUNCTION__ if the compiler supports it. The\n> + * default is __FILE__, as it is consistent with assert(), and static function\n> + * names are not necessarily unique.\n> + *\n> + * __FILE__ \":\" __FUNCTION__ doesn't work with GNUC, as __FILE__ is supplied\n> + * by the preprocessor as a string literal, and __FUNCTION__ is filled in by\n> + * the compiler as a string constant.\n\nWant to say that it is a possibility to use pasted string literal\nthat involves __FILE__ and __LINE__ here?\n\n> + */\n> +#ifndef TRACE_CONTEXT\n> +# define TRACE_CONTEXT __FILE__\n> +#endif\n\n> +\n> +/**\n> + * Prints a formatted message, similar to printf.\n>   */\n> +#define trace_printf(...) trace_printf_key(&trace_default_key, __VA_ARGS__)\n\nOK.\n\n> +/**\n> + * Prints a formatted message, followed by a quoted list of arguments.\n> + */\n\nThis comment was OK in front of the declaration of the function\nversion of this thing, i.e.\n\n    void trace_argv_printf(const char **argv, const char *format, ...);\n\nbecause the function prototype made it clear what type the initial\nparameters are.\n\nBut it is way too inadequate for the varargs macro version of this\nfunction, since ...\n\n>  #define trace_argv_printf(argv, ...)\t\t\t\t\t    \\\n\n... it is harder to tell what argv is without type and impossible to\nknow the next one is a format string.  Just moving it around is not\nenough; it needs to be a bit more helpful.\n\n> @@ -236,12 +178,32 @@ void trace_performance_leave(const char *format, ...);\n>  \t\t\t\t\t    argv, __VA_ARGS__);\t\t    \\\n>  \t} while (0)\n>  \n> +/**\n> + * Prints the strbuf, without additional formatting (i.e. doesn't\n> + * choke on `%` or even `\\0`).\n> + */\n\nLikewise.  The function version was declared as\n\n\ttrace_strbuf(struct trace_key *key, const struct strbuf *data)\n\nso mention of \"strbuf\" was sufficient to tell that the second\nparameter is what gets printed.  With the macro version, the comment\nneeds to make an extra effort, as the reader has less clue to work\nwith, compared to the function version.\n\nThanks.\n"},{"id":"448845","messageId":"cover-v3-0.3-00000000000-20220219T103752Z-avarab@gmail.com","threadId":"55472","inReplyTo":"cover-v2-0.2-00000000000-20220128T110330Z-avarab@gmail.com","subject":"[PATCH v3 0/3] C99: remove dead !HAVE_VARIADIC_MACROS code","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-02-19T10:41:07Z","receivedAt":"2022-02-19T10:41:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"A too-late re-roll of the v2[1], sorry. But this should address all\nissues raised the last time around.\n\nSince v2 I've:\n\n * Improved the documentation of the now onnly-macros. I opted to just\n   quote the argument list of the old C functions for explanation.\n\n * Some miscellanious commit message grammar fixes.\n\n * Whitespace fixes in 2/3.\n\n * There's a new 3/3 which removes an indirection used to define\n   __FILE__, instead we now use __FILE__ directly.\n\n1. https://lore.kernel.org/git/cover-v2-0.2-00000000000-20220128T110330Z-avarab@gmail.com/\n\nÆvar Arnfjörð Bjarmason (3):\n  git-compat-util.h: clarify GCC v.s. C99-specific in comment\n  C99: remove hardcoded-out !HAVE_VARIADIC_MACROS code\n  trace.h: remove never-used TRACE_CONTEXT\n\n Documentation/CodingGuidelines |   3 +\n banned.h                       |   5 --\n git-compat-util.h              |  16 +---\n trace.c                        |  80 +-----------------\n trace.h                        | 145 ++++++++++++++-------------------\n trace2.c                       |  39 ---------\n trace2.h                       |  25 ------\n usage.c                        |  15 +---\n 8 files changed, 67 insertions(+), 261 deletions(-)\n\nRange-diff against v2:\n1:  31079a71ecb = 1:  1a15fe4fd74 git-compat-util.h: clarify GCC v.s. C99-specific in comment\n2:  966d96505cb ! 2:  bd0f969be55 C99: remove hardcoded-out !HAVE_VARIADIC_MACROS code\n    @@ Commit message\n         C99: remove hardcoded-out !HAVE_VARIADIC_MACROS code\n     \n         Remove the \"else\" branches of the HAVE_VARIADIC_MACROS macro, which\n    -    has been unconditionally omitted since 765dc168882 (git-compat-util:\n    +    have been unconditionally omitted since 765dc168882 (git-compat-util:\n         always enable variadic macros, 2021-01-28).\n     \n    -    Since they were hardcoded out anyone trying to compile a version of\n    +    Since were always omitted, anyone trying to use a compiler without\n    +    variadic macro support to compile a git since version\n         git v2.31.0 or later would have had a compilation error. 10 months\n         across a few releases since then should have been enough time for\n         anyone who cared to run into that and report the issue.\n    @@ trace.h: void trace_command_performance(const char **argv);\n     - */\n     -void trace_strbuf(struct trace_key *key, const struct strbuf *data);\n     -\n    - /**\n    +-/**\n     - * Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled.\n    -+ * Macros to add the file:line of the calling code, instead of that of\n    -+ * the trace function itself.\n    -  *\n    +- *\n     - * Example:\n     - * ------------\n     - * uint64_t t = 0;\n    @@ trace.h: void trace_command_performance(const char **argv);\n     - * should be used.\n     - */\n     -\n    + /*\n    +  * TRACE_CONTEXT may be set to __FUNCTION__ if the compiler supports it. The\n    +  * default is __FILE__, as it is consistent with assert(), and static function\n    +@@ trace.h: void trace_performance_leave(const char *format, ...);\n    + # define TRACE_CONTEXT __FILE__\n    + #endif\n    + \n     -/*\n    -- * TRACE_CONTEXT may be set to __FUNCTION__ if the compiler supports it. The\n    -- * default is __FILE__, as it is consistent with assert(), and static function\n    -- * names are not necessarily unique.\n    -- *\n    -- * __FILE__ \":\" __FUNCTION__ doesn't work with GNUC, as __FILE__ is supplied\n    -- * by the preprocessor as a string literal, and __FUNCTION__ is filled in by\n    -- * the compiler as a string constant.\n    -- */\n    --#ifndef TRACE_CONTEXT\n    --# define TRACE_CONTEXT __FILE__\n    --#endif\n    --\n    --/*\n    ++/**\n    ++ * Macros to add the file:line of the calling code, instead of that of\n    ++ * the trace function itself.\n    ++ *\n       * Note: with C99 variadic macros, __VA_ARGS__ must include the last fixed\n       * parameter ('format' in this case). Otherwise, a call without variable\n       * arguments will have a surplus ','. E.g.:\n     @@ trace.h: void trace_performance_leave(const char *format, ...);\n    -  *\n    -  * which is invalid (note the ',)'). With GNUC, '##__VA_ARGS__' drops the\n       * comma, but this is non-standard.\n    +  */\n    + \n    ++/**\n    ++ * trace_printf(), accepts \"const char *format, ...\".\n     + *\n    -+ * TRACE_CONTEXT may be set to __FUNCTION__ if the compiler supports it. The\n    -+ * default is __FILE__, as it is consistent with assert(), and static function\n    -+ * names are not necessarily unique.\n    -+ *\n    -+ * __FILE__ \":\" __FUNCTION__ doesn't work with GNUC, as __FILE__ is supplied\n    -+ * by the preprocessor as a string literal, and __FUNCTION__ is filled in by\n    -+ * the compiler as a string constant.\n    ++ * Prints a formatted message, similar to printf.\n     + */\n    -+#ifndef TRACE_CONTEXT\n    -+# define TRACE_CONTEXT __FILE__\n    -+#endif\n    ++#define trace_printf(...) trace_printf_key(&trace_default_key, __VA_ARGS__)\n     +\n     +/**\n    -+ * Prints a formatted message, similar to printf.\n    -  */\n    -+#define trace_printf(...) trace_printf_key(&trace_default_key, __VA_ARGS__)\n    - \n    ++ * trace_printf_key(), accepts \"struct trace_key *key, const char *format, ...\".\n    ++ */\n      #define trace_printf_key(key, ...)\t\t\t\t\t    \\\n      \tdo {\t\t\t\t\t\t\t\t    \\\n    + \t\tif (trace_pass_fl(key))\t\t\t\t\t    \\\n     @@ trace.h: void trace_performance_leave(const char *format, ...);\n      \t\t\t\t\t    __VA_ARGS__);\t\t    \\\n      \t} while (0)\n    @@ trace.h: void trace_performance_leave(const char *format, ...);\n     -#define trace_printf(...) trace_printf_key(&trace_default_key, __VA_ARGS__)\n     -\n     +/**\n    ++ * trace_argv_printf(), accepts \"struct trace_key *key, const char *format, ...)\".\n    ++ *\n     + * Prints a formatted message, followed by a quoted list of arguments.\n     + */\n      #define trace_argv_printf(argv, ...)\t\t\t\t\t    \\\n    @@ trace.h: void trace_performance_leave(const char *format, ...);\n      \t} while (0)\n      \n     +/**\n    ++ * trace_strbuf(), accepts \"struct trace_key *key, const struct strbuf *data\".\n    ++ *\n     + * Prints the strbuf, without additional formatting (i.e. doesn't\n     + * choke on `%` or even `\\0`).\n     + */\n    @@ trace.h: void trace_performance_leave(const char *format, ...);\n      \t} while (0)\n      \n     +/**\n    ++ * trace_performance(), accepts \"uint64_t nanos, const char *format, ...\".\n    ++ *\n     + * Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled.\n     + *\n     + * Example:\n    @@ trace.h: void trace_performance_leave(const char *format, ...);\n      \t} while (0)\n      \n     +/**\n    ++ * trace_performance_since(), accepts \"uint64_t start, const char *format, ...\".\n    ++ *\n     + * Prints elapsed time since 'start' if GIT_TRACE_PERFORMANCE is enabled.\n     + *\n     + * Example:\n    @@ trace.h: void trace_performance_leave(const char *format, ...);\n      \tdo {\t\t\t\t\t\t\t\t    \\\n      \t\tif (trace_pass_fl(&trace_perf_key))\t\t\t    \\\n     @@ trace.h: void trace_performance_leave(const char *format, ...);\n    - \t\t\t\t\t\t   __VA_ARGS__);\t    \\\n    + \t\t\t\t\t     __VA_ARGS__);\t\t    \\\n      \t} while (0)\n      \n    -+\n    - /* backend functions, use non-*fl macros instead */\n    - __attribute__((format (printf, 4, 5)))\n    - void trace_printf_key_fl(const char *file, int line, struct trace_key *key,\n    ++/**\n    ++ * trace_performance_leave(), accepts \"const char *format, ...\".\n    ++ */\n    + #define trace_performance_leave(...)\t\t\t\t\t    \\\n    + \tdo {\t\t\t\t\t\t\t\t    \\\n    + \t\tif (trace_pass_fl(&trace_perf_key))\t\t\t    \\\n     @@ trace.h: static inline int trace_pass_fl(struct trace_key *key)\n      \treturn key->fd || !key->initialized;\n      }\n-:  ----------- > 3:  27ea260bbea trace.h: remove never-used TRACE_CONTEXT\n-- \n2.35.1.1129.g03fd99ecb8d\n\n"},{"id":"448846","messageId":"patch-v3-1.3-1a15fe4fd74-20220219T103752Z-avarab@gmail.com","threadId":"55472","inReplyTo":"cover-v3-0.3-00000000000-20220219T103752Z-avarab@gmail.com","subject":"[PATCH v3 1/3] git-compat-util.h: clarify GCC v.s. C99-specific in comment","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-02-19T10:41:08Z","receivedAt":"2022-02-19T10:41:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change a comment added in e208f9cc757 (make error()'s constant return\nvalue more visible, 2012-12-15). It's not correct that this is GCC-ism\nanymore, it's code that uses standard C99 features.\n\nThe comment being changed here pre-dates the HAVE_VARIADIC_MACROS\ndefine, which we got in e05bed960d3 (trace: add 'file:line' to all\ntrace output, 2014-07-12).\n\nThe original implementation of an error() macro) in e208f9cc757 used a\nGCC-ism with the paste operator (see the commit message for mention of\nit), but that was dropped later by 9798f7e5f9 (Use __VA_ARGS__ for all\nof error's arguments, 2013-02-08), giving us the C99-portable version\nwe have now.\n\nWhile we could remove the __GNUC__ define here, it might cause issues\nfor other compilers or static analysis systems, so let's not. See\n87fe5df365 (inline constant return from error() function, 2014-05-06)\nfor one such issue.\n\nSee also e05bed960d3 (trace: add 'file:line' to all trace output,\n2014-07-12) for another comment about GNUC's handling of __VA_ARGS__.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n git-compat-util.h | 4 +---\n 1 file changed, 1 insertion(+), 3 deletions(-)\n\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 876907b9df4..2d9b75a021a 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -534,9 +534,7 @@ void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));\n /*\n  * Let callers be aware of the constant return value; this can help\n  * gcc with -Wuninitialized analysis. We restrict this trick to gcc, though,\n- * because some compilers may not support variadic macros. Since we're only\n- * trying to help gcc, anyway, it's OK; other compilers will fall back to\n- * using the function as usual.\n+ * because other compilers may be confused by this.\n  */\n #if defined(__GNUC__)\n static inline int const_error(void)\n-- \n2.35.1.1129.g03fd99ecb8d\n\n"},{"id":"448847","messageId":"patch-v3-3.3-27ea260bbea-20220219T103752Z-avarab@gmail.com","threadId":"55472","inReplyTo":"cover-v3-0.3-00000000000-20220219T103752Z-avarab@gmail.com","subject":"[PATCH v3 3/3] trace.h: remove never-used TRACE_CONTEXT","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-02-19T10:41:10Z","receivedAt":"2022-02-19T10:42:03Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The definition of \"TRACE_CONTEXT\" as \"__FILE__\" added in\ne05bed960d3 (trace: add 'file:line' to all trace output, 2014-07-12)\nappeared between v6[1] and v7[2] of the series that added it.\n\nIt does not appear to have been something anybody asked for, and\ndoesn't seem to have been used by anyone since then to override it to\nsomething other than the default __FILE__.\n\nWhen trace2 was added in ee4512ed481 (trace2: create new combined\ntrace facility, 2019-02-22) it added no such \"configuration\", and\nwe're unlikely to ever want to configure this through these means.\n\nI.e. I have considered (and probably will) make the usage.c macros\nsupport optional __FUNCTION__ at some point, that would need to have\nthem passed as \"mandatory\" parameters (which might default to NULL) to\nthe underlying function, for the reasons explained in the comment\nbeing removed here.\n\nSo let's just remove this indirection in favor of using __FILE__\ndirectly.\n\n1. https://lore.kernel.org/git/53A4A2CD.8010003@gmail.com/\n2. https://lore.kernel.org/git/53B33DED.3030809@gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n trace.h | 25 ++++++-------------------\n 1 file changed, 6 insertions(+), 19 deletions(-)\n\ndiff --git a/trace.h b/trace.h\nindex 4e771f86ac2..5b52c1e23fd 100644\n--- a/trace.h\n+++ b/trace.h\n@@ -126,19 +126,6 @@ void trace_command_performance(const char **argv);\n void trace_verbatim(struct trace_key *key, const void *buf, unsigned len);\n uint64_t trace_performance_enter(void);\n \n-/*\n- * TRACE_CONTEXT may be set to __FUNCTION__ if the compiler supports it. The\n- * default is __FILE__, as it is consistent with assert(), and static function\n- * names are not necessarily unique.\n- *\n- * __FILE__ \":\" __FUNCTION__ doesn't work with GNUC, as __FILE__ is supplied\n- * by the preprocessor as a string literal, and __FUNCTION__ is filled in by\n- * the compiler as a string constant.\n- */\n-#ifndef TRACE_CONTEXT\n-# define TRACE_CONTEXT __FILE__\n-#endif\n-\n /**\n  * Macros to add the file:line of the calling code, instead of that of\n  * the trace function itself.\n@@ -171,7 +158,7 @@ uint64_t trace_performance_enter(void);\n #define trace_printf_key(key, ...)\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(key))\t\t\t\t\t    \\\n-\t\t\ttrace_printf_key_fl(TRACE_CONTEXT, __LINE__, key,   \\\n+\t\t\ttrace_printf_key_fl(__FILE__, __LINE__, key,\t    \\\n \t\t\t\t\t    __VA_ARGS__);\t\t    \\\n \t} while (0)\n \n@@ -183,7 +170,7 @@ uint64_t trace_performance_enter(void);\n #define trace_argv_printf(argv, ...)\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(&trace_default_key))\t\t\t    \\\n-\t\t\ttrace_argv_printf_fl(TRACE_CONTEXT, __LINE__,\t    \\\n+\t\t\ttrace_argv_printf_fl(__FILE__, __LINE__,\t    \\\n \t\t\t\t\t    argv, __VA_ARGS__);\t\t    \\\n \t} while (0)\n \n@@ -196,7 +183,7 @@ uint64_t trace_performance_enter(void);\n #define trace_strbuf(key, data)\t\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(key))\t\t\t\t\t    \\\n-\t\t\ttrace_strbuf_fl(TRACE_CONTEXT, __LINE__, key, data);\\\n+\t\t\ttrace_strbuf_fl(__FILE__, __LINE__, key, data);\t    \\\n \t} while (0)\n \n /**\n@@ -220,7 +207,7 @@ uint64_t trace_performance_enter(void);\n #define trace_performance(nanos, ...)\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(&trace_perf_key))\t\t\t    \\\n-\t\t\ttrace_performance_fl(TRACE_CONTEXT, __LINE__, nanos,\\\n+\t\t\ttrace_performance_fl(__FILE__, __LINE__, nanos,\t    \\\n \t\t\t\t\t     __VA_ARGS__);\t\t    \\\n \t} while (0)\n \n@@ -239,7 +226,7 @@ uint64_t trace_performance_enter(void);\n #define trace_performance_since(start, ...)\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(&trace_perf_key))\t\t\t    \\\n-\t\t\ttrace_performance_fl(TRACE_CONTEXT, __LINE__,       \\\n+\t\t\ttrace_performance_fl(__FILE__, __LINE__,\t    \\\n \t\t\t\t\t     getnanotime() - (start),\t    \\\n \t\t\t\t\t     __VA_ARGS__);\t\t    \\\n \t} while (0)\n@@ -250,7 +237,7 @@ uint64_t trace_performance_enter(void);\n #define trace_performance_leave(...)\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(&trace_perf_key))\t\t\t    \\\n-\t\t\ttrace_performance_leave_fl(TRACE_CONTEXT, __LINE__, \\\n+\t\t\ttrace_performance_leave_fl(__FILE__, __LINE__,\t    \\\n \t\t\t\t\t\t   getnanotime(),\t    \\\n \t\t\t\t\t\t   __VA_ARGS__);\t    \\\n \t} while (0)\n-- \n2.35.1.1129.g03fd99ecb8d\n\n"},{"id":"448848","messageId":"patch-v3-2.3-bd0f969be55-20220219T103752Z-avarab@gmail.com","threadId":"55472","inReplyTo":"cover-v3-0.3-00000000000-20220219T103752Z-avarab@gmail.com","subject":"[PATCH v3 2/3] C99: remove hardcoded-out !HAVE_VARIADIC_MACROS code","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-02-19T10:41:09Z","receivedAt":"2022-02-19T10:42:05Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove the \"else\" branches of the HAVE_VARIADIC_MACROS macro, which\nhave been unconditionally omitted since 765dc168882 (git-compat-util:\nalways enable variadic macros, 2021-01-28).\n\nSince were always omitted, anyone trying to use a compiler without\nvariadic macro support to compile a git since version\ngit v2.31.0 or later would have had a compilation error. 10 months\nacross a few releases since then should have been enough time for\nanyone who cared to run into that and report the issue.\n\nIn addition to that, for anyone unsetting HAVE_VARIADIC_MACROS we've\nbeen emitting extremely verbose warnings since at least\nee4512ed481 (trace2: create new combined trace facility,\n2019-02-22). That's because there is no such thing as a\n\"region_enter_printf\" or \"region_leave_printf\" format, so at least\nunder GCC and Clang everything that includes trace.h (almost every\nfile) emits a couple of warnings about that.\n\nThere's a large benefit to being able to have a hard dependency rely\non variadic macros, the code surrounding usage.c is hard to maintain\nif we need to write two implementations of everything, and by relying\non \"__FILE__\" and \"__LINE__\" along with \"__VA_ARGS__\" we can in the\nfuture make error(), die() etc. log where they were called from. We've\nalso recently merged d67fc4bf0ba (Merge branch 'bc/require-c99',\n2021-12-10) which further cements our hard dependency on C99.\n\nSo let's delete the fallback code, and update our CodingGuidelines to\nnote that we depend on this. The added bullet-point starts with\nlower-case for consistency with other bullet-points in that section.\n\nThe diff in \"trace.h\" is relatively hard to read, since we need to\nretain the existing API docs, which were comments on the code used if\nHAVE_VARIADIC_MACROS was not defined.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/CodingGuidelines |   3 +\n banned.h                       |   5 --\n git-compat-util.h              |  12 ----\n trace.c                        |  80 +--------------------\n trace.h                        | 128 +++++++++++++++------------------\n trace2.c                       |  39 ----------\n trace2.h                       |  25 -------\n usage.c                        |  15 +---\n 8 files changed, 64 insertions(+), 243 deletions(-)\n\ndiff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines\nindex c37c43186ea..1a7bc4591cd 100644\n--- a/Documentation/CodingGuidelines\n+++ b/Documentation/CodingGuidelines\n@@ -217,6 +217,9 @@ For C programs:\n    . since mid 2017 with 512f41cf, we have been using designated\n      initializers for array (e.g. \"int array[10] = { [5] = 2 }\").\n \n+   . since early 2021 with 765dc168882, we have been using variadic\n+     macros, mostly for printf-like trace and debug macros.\n+\n    These used to be forbidden, but we have not heard any breakage\n    report, and they are assumed to be safe.\n \ndiff --git a/banned.h b/banned.h\nindex 7ab4f2e4921..6ccf46bc197 100644\n--- a/banned.h\n+++ b/banned.h\n@@ -21,13 +21,8 @@\n \n #undef sprintf\n #undef vsprintf\n-#ifdef HAVE_VARIADIC_MACROS\n #define sprintf(...) BANNED(sprintf)\n #define vsprintf(...) BANNED(vsprintf)\n-#else\n-#define sprintf(buf,fmt,arg) BANNED(sprintf)\n-#define vsprintf(buf,fmt,arg) BANNED(vsprintf)\n-#endif\n \n #undef gmtime\n #define gmtime(t) BANNED(gmtime)\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 2d9b75a021a..e50e2fafaec 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -1256,24 +1256,12 @@ static inline int regexec_buf(const regex_t *preg, const char *buf, size_t size,\n #endif\n #endif\n \n-/*\n- * This is always defined as a first step towards making the use of variadic\n- * macros unconditional. If it causes compilation problems on your platform,\n- * please report it to the Git mailing list at git@vger.kernel.org.\n- */\n-#define HAVE_VARIADIC_MACROS 1\n-\n /* usage.c: only to be used for testing BUG() implementation (see test-tool) */\n extern int BUG_exit_code;\n \n-#ifdef HAVE_VARIADIC_MACROS\n __attribute__((format (printf, 3, 4))) NORETURN\n void BUG_fl(const char *file, int line, const char *fmt, ...);\n #define BUG(...) BUG_fl(__FILE__, __LINE__, __VA_ARGS__)\n-#else\n-__attribute__((format (printf, 1, 2))) NORETURN\n-void BUG(const char *fmt, ...);\n-#endif\n \n /*\n  * Preserves errno, prints a message, but gives no warning for ENOENT.\ndiff --git a/trace.c b/trace.c\nindex f726686fd92..794a087c21e 100644\n--- a/trace.c\n+++ b/trace.c\n@@ -108,16 +108,11 @@ static int prepare_trace_line(const char *file, int line,\n \tgettimeofday(&tv, NULL);\n \tsecs = tv.tv_sec;\n \tlocaltime_r(&secs, &tm);\n-\tstrbuf_addf(buf, \"%02d:%02d:%02d.%06ld \", tm.tm_hour, tm.tm_min,\n-\t\t    tm.tm_sec, (long) tv.tv_usec);\n-\n-#ifdef HAVE_VARIADIC_MACROS\n-\t/* print file:line */\n-\tstrbuf_addf(buf, \"%s:%d \", file, line);\n+\tstrbuf_addf(buf, \"%02d:%02d:%02d.%06ld %s:%d\", tm.tm_hour, tm.tm_min,\n+\t\t    tm.tm_sec, (long) tv.tv_usec, file, line);\n \t/* align trace output (column 40 catches most files names in git) */\n \twhile (buf->len < 40)\n \t\tstrbuf_addch(buf, ' ');\n-#endif\n \n \treturn 1;\n }\n@@ -229,74 +224,6 @@ static void trace_performance_vprintf_fl(const char *file, int line,\n \tstrbuf_release(&buf);\n }\n \n-#ifndef HAVE_VARIADIC_MACROS\n-\n-void trace_printf(const char *format, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, format);\n-\ttrace_vprintf_fl(NULL, 0, &trace_default_key, format, ap);\n-\tva_end(ap);\n-}\n-\n-void trace_printf_key(struct trace_key *key, const char *format, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, format);\n-\ttrace_vprintf_fl(NULL, 0, key, format, ap);\n-\tva_end(ap);\n-}\n-\n-void trace_argv_printf(const char **argv, const char *format, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, format);\n-\ttrace_argv_vprintf_fl(NULL, 0, argv, format, ap);\n-\tva_end(ap);\n-}\n-\n-void trace_strbuf(struct trace_key *key, const struct strbuf *data)\n-{\n-\ttrace_strbuf_fl(NULL, 0, key, data);\n-}\n-\n-void trace_performance(uint64_t nanos, const char *format, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, format);\n-\ttrace_performance_vprintf_fl(NULL, 0, nanos, format, ap);\n-\tva_end(ap);\n-}\n-\n-void trace_performance_since(uint64_t start, const char *format, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, format);\n-\ttrace_performance_vprintf_fl(NULL, 0, getnanotime() - start,\n-\t\t\t\t     format, ap);\n-\tva_end(ap);\n-}\n-\n-void trace_performance_leave(const char *format, ...)\n-{\n-\tva_list ap;\n-\tuint64_t since;\n-\n-\tif (perf_indent)\n-\t\tperf_indent--;\n-\n-\tif (!format) /* Allow callers to leave without tracing anything */\n-\t\treturn;\n-\n-\tsince = perf_start_times[perf_indent];\n-\tva_start(ap, format);\n-\ttrace_performance_vprintf_fl(NULL, 0, getnanotime() - since,\n-\t\t\t\t     format, ap);\n-\tva_end(ap);\n-}\n-\n-#else\n-\n void trace_printf_key_fl(const char *file, int line, struct trace_key *key,\n \t\t\t const char *format, ...)\n {\n@@ -342,9 +269,6 @@ void trace_performance_leave_fl(const char *file, int line,\n \tva_end(ap);\n }\n \n-#endif /* HAVE_VARIADIC_MACROS */\n-\n-\n static const char *quote_crnl(const char *path)\n {\n \tstatic struct strbuf new_path = STRBUF_INIT;\ndiff --git a/trace.h b/trace.h\nindex e25984051aa..4e771f86ac2 100644\n--- a/trace.h\n+++ b/trace.h\n@@ -126,71 +126,6 @@ void trace_command_performance(const char **argv);\n void trace_verbatim(struct trace_key *key, const void *buf, unsigned len);\n uint64_t trace_performance_enter(void);\n \n-#ifndef HAVE_VARIADIC_MACROS\n-\n-/**\n- * Prints a formatted message, similar to printf.\n- */\n-__attribute__((format (printf, 1, 2)))\n-void trace_printf(const char *format, ...);\n-\n-__attribute__((format (printf, 2, 3)))\n-void trace_printf_key(struct trace_key *key, const char *format, ...);\n-\n-/**\n- * Prints a formatted message, followed by a quoted list of arguments.\n- */\n-__attribute__((format (printf, 2, 3)))\n-void trace_argv_printf(const char **argv, const char *format, ...);\n-\n-/**\n- * Prints the strbuf, without additional formatting (i.e. doesn't\n- * choke on `%` or even `\\0`).\n- */\n-void trace_strbuf(struct trace_key *key, const struct strbuf *data);\n-\n-/**\n- * Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled.\n- *\n- * Example:\n- * ------------\n- * uint64_t t = 0;\n- * for (;;) {\n- * \t// ignore\n- * t -= getnanotime();\n- * // code section to measure\n- * t += getnanotime();\n- * // ignore\n- * }\n- * trace_performance(t, \"frotz\");\n- * ------------\n- */\n-__attribute__((format (printf, 2, 3)))\n-void trace_performance(uint64_t nanos, const char *format, ...);\n-\n-/**\n- * Prints elapsed time since 'start' if GIT_TRACE_PERFORMANCE is enabled.\n- *\n- * Example:\n- * ------------\n- * uint64_t start = getnanotime();\n- * // code section to measure\n- * trace_performance_since(start, \"foobar\");\n- * ------------\n- */\n-__attribute__((format (printf, 2, 3)))\n-void trace_performance_since(uint64_t start, const char *format, ...);\n-\n-__attribute__((format (printf, 1, 2)))\n-void trace_performance_leave(const char *format, ...);\n-\n-#else\n-\n-/*\n- * Macros to add file:line - see above for C-style declarations of how these\n- * should be used.\n- */\n-\n /*\n  * TRACE_CONTEXT may be set to __FUNCTION__ if the compiler supports it. The\n  * default is __FILE__, as it is consistent with assert(), and static function\n@@ -204,7 +139,10 @@ void trace_performance_leave(const char *format, ...);\n # define TRACE_CONTEXT __FILE__\n #endif\n \n-/*\n+/**\n+ * Macros to add the file:line of the calling code, instead of that of\n+ * the trace function itself.\n+ *\n  * Note: with C99 variadic macros, __VA_ARGS__ must include the last fixed\n  * parameter ('format' in this case). Otherwise, a call without variable\n  * arguments will have a surplus ','. E.g.:\n@@ -220,6 +158,16 @@ void trace_performance_leave(const char *format, ...);\n  * comma, but this is non-standard.\n  */\n \n+/**\n+ * trace_printf(), accepts \"const char *format, ...\".\n+ *\n+ * Prints a formatted message, similar to printf.\n+ */\n+#define trace_printf(...) trace_printf_key(&trace_default_key, __VA_ARGS__)\n+\n+/**\n+ * trace_printf_key(), accepts \"struct trace_key *key, const char *format, ...\".\n+ */\n #define trace_printf_key(key, ...)\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(key))\t\t\t\t\t    \\\n@@ -227,8 +175,11 @@ void trace_performance_leave(const char *format, ...);\n \t\t\t\t\t    __VA_ARGS__);\t\t    \\\n \t} while (0)\n \n-#define trace_printf(...) trace_printf_key(&trace_default_key, __VA_ARGS__)\n-\n+/**\n+ * trace_argv_printf(), accepts \"struct trace_key *key, const char *format, ...)\".\n+ *\n+ * Prints a formatted message, followed by a quoted list of arguments.\n+ */\n #define trace_argv_printf(argv, ...)\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(&trace_default_key))\t\t\t    \\\n@@ -236,12 +187,36 @@ void trace_performance_leave(const char *format, ...);\n \t\t\t\t\t    argv, __VA_ARGS__);\t\t    \\\n \t} while (0)\n \n+/**\n+ * trace_strbuf(), accepts \"struct trace_key *key, const struct strbuf *data\".\n+ *\n+ * Prints the strbuf, without additional formatting (i.e. doesn't\n+ * choke on `%` or even `\\0`).\n+ */\n #define trace_strbuf(key, data)\t\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(key))\t\t\t\t\t    \\\n \t\t\ttrace_strbuf_fl(TRACE_CONTEXT, __LINE__, key, data);\\\n \t} while (0)\n \n+/**\n+ * trace_performance(), accepts \"uint64_t nanos, const char *format, ...\".\n+ *\n+ * Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled.\n+ *\n+ * Example:\n+ * ------------\n+ * uint64_t t = 0;\n+ * for (;;) {\n+ * \t// ignore\n+ * t -= getnanotime();\n+ * // code section to measure\n+ * t += getnanotime();\n+ * // ignore\n+ * }\n+ * trace_performance(t, \"frotz\");\n+ * ------------\n+ */\n #define trace_performance(nanos, ...)\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(&trace_perf_key))\t\t\t    \\\n@@ -249,6 +224,18 @@ void trace_performance_leave(const char *format, ...);\n \t\t\t\t\t     __VA_ARGS__);\t\t    \\\n \t} while (0)\n \n+/**\n+ * trace_performance_since(), accepts \"uint64_t start, const char *format, ...\".\n+ *\n+ * Prints elapsed time since 'start' if GIT_TRACE_PERFORMANCE is enabled.\n+ *\n+ * Example:\n+ * ------------\n+ * uint64_t start = getnanotime();\n+ * // code section to measure\n+ * trace_performance_since(start, \"foobar\");\n+ * ------------\n+ */\n #define trace_performance_since(start, ...)\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(&trace_perf_key))\t\t\t    \\\n@@ -257,6 +244,9 @@ void trace_performance_leave(const char *format, ...);\n \t\t\t\t\t     __VA_ARGS__);\t\t    \\\n \t} while (0)\n \n+/**\n+ * trace_performance_leave(), accepts \"const char *format, ...\".\n+ */\n #define trace_performance_leave(...)\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(&trace_perf_key))\t\t\t    \\\n@@ -285,6 +275,4 @@ static inline int trace_pass_fl(struct trace_key *key)\n \treturn key->fd || !key->initialized;\n }\n \n-#endif /* HAVE_VARIADIC_MACROS */\n-\n #endif /* TRACE_H */\ndiff --git a/trace2.c b/trace2.c\nindex b2d471526fd..179caa72cfe 100644\n--- a/trace2.c\n+++ b/trace2.c\n@@ -641,20 +641,6 @@ void trace2_region_enter_printf_fl(const char *file, int line,\n \tva_end(ap);\n }\n \n-#ifndef HAVE_VARIADIC_MACROS\n-void trace2_region_enter_printf(const char *category, const char *label,\n-\t\t\t\tconst struct repository *repo, const char *fmt,\n-\t\t\t\t...)\n-{\n-\tva_list ap;\n-\n-\tva_start(ap, fmt);\n-\ttrace2_region_enter_printf_va_fl(NULL, 0, category, label, repo, fmt,\n-\t\t\t\t\t ap);\n-\tva_end(ap);\n-}\n-#endif\n-\n void trace2_region_leave_printf_va_fl(const char *file, int line,\n \t\t\t\t      const char *category, const char *label,\n \t\t\t\t      const struct repository *repo,\n@@ -717,20 +703,6 @@ void trace2_region_leave_printf_fl(const char *file, int line,\n \tva_end(ap);\n }\n \n-#ifndef HAVE_VARIADIC_MACROS\n-void trace2_region_leave_printf(const char *category, const char *label,\n-\t\t\t\tconst struct repository *repo, const char *fmt,\n-\t\t\t\t...)\n-{\n-\tva_list ap;\n-\n-\tva_start(ap, fmt);\n-\ttrace2_region_leave_printf_va_fl(NULL, 0, category, label, repo, fmt,\n-\t\t\t\t\t ap);\n-\tva_end(ap);\n-}\n-#endif\n-\n void trace2_data_string_fl(const char *file, int line, const char *category,\n \t\t\t   const struct repository *repo, const char *key,\n \t\t\t   const char *value)\n@@ -826,17 +798,6 @@ void trace2_printf_fl(const char *file, int line, const char *fmt, ...)\n \tva_end(ap);\n }\n \n-#ifndef HAVE_VARIADIC_MACROS\n-void trace2_printf(const char *fmt, ...)\n-{\n-\tva_list ap;\n-\n-\tva_start(ap, fmt);\n-\ttrace2_printf_va_fl(NULL, 0, fmt, ap);\n-\tva_end(ap);\n-}\n-#endif\n-\n const char *trace2_session_id(void)\n {\n \treturn tr2_sid_get();\ndiff --git a/trace2.h b/trace2.h\nindex 0cc7b5f5312..1b109f57d0a 100644\n--- a/trace2.h\n+++ b/trace2.h\n@@ -397,18 +397,9 @@ void trace2_region_enter_printf_fl(const char *file, int line,\n \t\t\t\t   const struct repository *repo,\n \t\t\t\t   const char *fmt, ...);\n \n-#ifdef HAVE_VARIADIC_MACROS\n #define trace2_region_enter_printf(category, label, repo, ...)                 \\\n \ttrace2_region_enter_printf_fl(__FILE__, __LINE__, (category), (label), \\\n \t\t\t\t      (repo), __VA_ARGS__)\n-#else\n-/* clang-format off */\n-__attribute__((format (region_enter_printf, 4, 5)))\n-void trace2_region_enter_printf(const char *category, const char *label,\n-\t\t\t\tconst struct repository *repo, const char *fmt,\n-\t\t\t\t...);\n-/* clang-format on */\n-#endif\n \n /**\n  * Emit a 'region_leave' event for <category>.<label> with optional\n@@ -442,18 +433,9 @@ void trace2_region_leave_printf_fl(const char *file, int line,\n \t\t\t\t   const struct repository *repo,\n \t\t\t\t   const char *fmt, ...);\n \n-#ifdef HAVE_VARIADIC_MACROS\n #define trace2_region_leave_printf(category, label, repo, ...)                 \\\n \ttrace2_region_leave_printf_fl(__FILE__, __LINE__, (category), (label), \\\n \t\t\t\t      (repo), __VA_ARGS__)\n-#else\n-/* clang-format off */\n-__attribute__((format (region_leave_printf, 4, 5)))\n-void trace2_region_leave_printf(const char *category, const char *label,\n-\t\t\t\tconst struct repository *repo, const char *fmt,\n-\t\t\t\t...);\n-/* clang-format on */\n-#endif\n \n /**\n  * Emit a key-value pair 'data' event of the form <category>.<key> = <value>.\n@@ -506,14 +488,7 @@ void trace2_printf_va_fl(const char *file, int line, const char *fmt,\n \n void trace2_printf_fl(const char *file, int line, const char *fmt, ...);\n \n-#ifdef HAVE_VARIADIC_MACROS\n #define trace2_printf(...) trace2_printf_fl(__FILE__, __LINE__, __VA_ARGS__)\n-#else\n-/* clang-format off */\n-__attribute__((format (printf, 1, 2)))\n-void trace2_printf(const char *fmt, ...);\n-/* clang-format on */\n-#endif\n \n /*\n  * Optional platform-specific code to dump information about the\ndiff --git a/usage.c b/usage.c\nindex 9943dd8742e..b738dd178b3 100644\n--- a/usage.c\n+++ b/usage.c\n@@ -299,10 +299,7 @@ static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_lis\n \tva_copy(params_copy, params);\n \n \t/* truncation via snprintf is OK here */\n-\tif (file)\n-\t\tsnprintf(prefix, sizeof(prefix), \"BUG: %s:%d: \", file, line);\n-\telse\n-\t\tsnprintf(prefix, sizeof(prefix), \"BUG: \");\n+\tsnprintf(prefix, sizeof(prefix), \"BUG: %s:%d: \", file, line);\n \n \tvreportf(prefix, fmt, params);\n \n@@ -317,7 +314,6 @@ static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_lis\n \tabort();\n }\n \n-#ifdef HAVE_VARIADIC_MACROS\n NORETURN void BUG_fl(const char *file, int line, const char *fmt, ...)\n {\n \tva_list ap;\n@@ -325,15 +321,6 @@ NORETURN void BUG_fl(const char *file, int line, const char *fmt, ...)\n \tBUG_vfl(file, line, fmt, ap);\n \tva_end(ap);\n }\n-#else\n-NORETURN void BUG(const char *fmt, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, fmt);\n-\tBUG_vfl(NULL, 0, fmt, ap);\n-\tva_end(ap);\n-}\n-#endif\n \n #ifdef SUPPRESS_ANNOTATED_LEAKS\n void unleak_memory(const void *ptr, size_t len)\n-- \n2.35.1.1129.g03fd99ecb8d\n\n"},{"id":"448896","messageId":"xmqq5yp9wxt1.fsf@gitster.g","threadId":"55472","inReplyTo":"patch-v3-3.3-27ea260bbea-20220219T103752Z-avarab@gmail.com","subject":"Re: [PATCH v3 3/3] trace.h: remove never-used TRACE_CONTEXT","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-02-20T12:02:34Z","receivedAt":"2022-02-20T12:02:42Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n\n> The definition of \"TRACE_CONTEXT\" as \"__FILE__\" added in\n> e05bed960d3 (trace: add 'file:line' to all trace output, 2014-07-12)\n> appeared between v6[1] and v7[2] of the series that added it.\n>\n> It does not appear to have been something anybody asked for, and\n> doesn't seem to have been used by anyone since then to override it to\n> something other than the default __FILE__.\n\nSorry, but I do not quite follow.  How can we claim nobody used it,\ngiven that it should be usable with CFLAGS=-DTRACE_CONTEXT=ANYTHING?\n\nOn the other hand, I do not see how it is hurting anybody to have\nthis indirection today.\n\nOr am I missing something obvious?\n"},{"id":"448898","messageId":"220220.86fsodd7sm.gmgdl@evledraar.gmail.com","threadId":"55472","inReplyTo":"xmqq5yp9wxt1.fsf@gitster.g","subject":"Re: [PATCH v3 3/3] trace.h: remove never-used TRACE_CONTEXT","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-02-20T12:38:43Z","receivedAt":"2022-02-20T12:47:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, Feb 20 2022, Junio C Hamano wrote:\n\n[I forgot to CC Karsten Blees]\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> The definition of \"TRACE_CONTEXT\" as \"__FILE__\" added in\n>> e05bed960d3 (trace: add 'file:line' to all trace output, 2014-07-12)\n>> appeared between v6[1] and v7[2] of the series that added it.\n>>\n>> It does not appear to have been something anybody asked for, and\n>> doesn't seem to have been used by anyone since then to override it to\n>> something other than the default __FILE__.\n>\n> Sorry, but I do not quite follow.  How can we claim nobody used it,\n> given that it should be usable with CFLAGS=-DTRACE_CONTEXT=ANYTHING?\n>\n> On the other hand, I do not see how it is hurting anybody to have\n> this indirection today.\n>\n> Or am I missing something obvious?\n\nI don't know that, but:\n\n * From the list discussion at the time it seems nobody asked for this,\n   it was just something the author \"snuck in\" between re-rolls.\n\n * I didn't find any subsequent on-list discussion of people actually\n   using this.\n\nBoth leads me to believe that this was a neat-at-the-time but never used\nfor anything outside one-off testing feature.\n\nPerhaps you'd like a v4 without this. It isn't strictly needed, but\nwhere I'm going with this series is improving the usage.c output/passing\nof these __{FILE,LINE,FUNCTION}__.\n\nI don't need it now, but if we'd eventually like to unify trace & trace2\nwe'll need to deal with this part, since those functions won't deal well\nwith replacing a __FILE__ \"just pass whatever data you'd like here\".\n\n"},{"id":"448921","messageId":"xmqq1qzxwb59.fsf@gitster.g","threadId":"55472","inReplyTo":"220220.86fsodd7sm.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v3 3/3] trace.h: remove never-used TRACE_CONTEXT","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-02-20T20:12:02Z","receivedAt":"2022-02-20T20:12:12Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n\n> Perhaps you'd like a v4 without this. It isn't strictly needed, but\n> where I'm going with this series is improving the usage.c output/passing\n> of these __{FILE,LINE,FUNCTION}__.\n\nMy stance is that\n\n * the removal of this indirection is so small that the current\n   series can live without it---in fact, it does not benefit the\n   current series at all---the only thing it brings us is a possible\n   breakage for those we failed to consider their use case.\n\n * the removal of this indirection is so small but a future and\n   unrelated usage.c improvement may benefit from it, so it should\n   be justified within the context of that future series.  It still\n   may break the same folks whose use case we did not consider, but\n   the other \"improvement\" in that future topic may offset the\n   downside.\n\nSo ...\n\n> ... since those functions won't deal well\n> with replacing a __FILE__ \"just pass whatever data you'd like here\".\n\n... leave that as the justification for the _other_ series.  It does\nnot belong here in this series.\n"},{"id":"449014","messageId":"cover-v4-0.2-00000000000-20220221T160440Z-avarab@gmail.com","threadId":"55472","inReplyTo":"cover-v3-0.3-00000000000-20220219T103752Z-avarab@gmail.com","subject":"[PATCH v4 0/2] C99: remove dead !HAVE_VARIADIC_MACROS code","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-02-21T16:05:25Z","receivedAt":"2022-02-21T16:05:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"I trivial re-roll of v2:\nhttps://lore.kernel.org/git/cover-v3-0.3-00000000000-20220219T103752Z-avarab@gmail.com/\n\nThe only change is removing the 3rd patch, per Junio's feedback.\n\nÆvar Arnfjörð Bjarmason (2):\n  git-compat-util.h: clarify GCC v.s. C99-specific in comment\n  C99: remove hardcoded-out !HAVE_VARIADIC_MACROS code\n\n Documentation/CodingGuidelines |   3 +\n banned.h                       |   5 --\n git-compat-util.h              |  16 +----\n trace.c                        |  80 +--------------------\n trace.h                        | 128 +++++++++++++++------------------\n trace2.c                       |  39 ----------\n trace2.h                       |  25 -------\n usage.c                        |  15 +---\n 8 files changed, 65 insertions(+), 246 deletions(-)\n\nRange-diff against v3:\n1:  1a15fe4fd74 = 1:  1a15fe4fd74 git-compat-util.h: clarify GCC v.s. C99-specific in comment\n2:  bd0f969be55 = 2:  bd0f969be55 C99: remove hardcoded-out !HAVE_VARIADIC_MACROS code\n3:  27ea260bbea < -:  ----------- trace.h: remove never-used TRACE_CONTEXT\n-- \n2.35.1.1132.ga1fe46f8690\n\n"},{"id":"449015","messageId":"patch-v4-1.2-1a15fe4fd74-20220221T160440Z-avarab@gmail.com","threadId":"55472","inReplyTo":"cover-v4-0.2-00000000000-20220221T160440Z-avarab@gmail.com","subject":"[PATCH v4 1/2] git-compat-util.h: clarify GCC v.s. C99-specific in comment","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-02-21T16:05:26Z","receivedAt":"2022-02-21T16:05:36Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change a comment added in e208f9cc757 (make error()'s constant return\nvalue more visible, 2012-12-15). It's not correct that this is GCC-ism\nanymore, it's code that uses standard C99 features.\n\nThe comment being changed here pre-dates the HAVE_VARIADIC_MACROS\ndefine, which we got in e05bed960d3 (trace: add 'file:line' to all\ntrace output, 2014-07-12).\n\nThe original implementation of an error() macro) in e208f9cc757 used a\nGCC-ism with the paste operator (see the commit message for mention of\nit), but that was dropped later by 9798f7e5f9 (Use __VA_ARGS__ for all\nof error's arguments, 2013-02-08), giving us the C99-portable version\nwe have now.\n\nWhile we could remove the __GNUC__ define here, it might cause issues\nfor other compilers or static analysis systems, so let's not. See\n87fe5df365 (inline constant return from error() function, 2014-05-06)\nfor one such issue.\n\nSee also e05bed960d3 (trace: add 'file:line' to all trace output,\n2014-07-12) for another comment about GNUC's handling of __VA_ARGS__.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n git-compat-util.h | 4 +---\n 1 file changed, 1 insertion(+), 3 deletions(-)\n\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 876907b9df4..2d9b75a021a 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -534,9 +534,7 @@ void warning_errno(const char *err, ...) __attribute__((format (printf, 1, 2)));\n /*\n  * Let callers be aware of the constant return value; this can help\n  * gcc with -Wuninitialized analysis. We restrict this trick to gcc, though,\n- * because some compilers may not support variadic macros. Since we're only\n- * trying to help gcc, anyway, it's OK; other compilers will fall back to\n- * using the function as usual.\n+ * because other compilers may be confused by this.\n  */\n #if defined(__GNUC__)\n static inline int const_error(void)\n-- \n2.35.1.1132.ga1fe46f8690\n\n"},{"id":"449016","messageId":"patch-v4-2.2-bd0f969be55-20220221T160440Z-avarab@gmail.com","threadId":"55472","inReplyTo":"cover-v4-0.2-00000000000-20220221T160440Z-avarab@gmail.com","subject":"[PATCH v4 2/2] C99: remove hardcoded-out !HAVE_VARIADIC_MACROS code","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-02-21T16:05:27Z","receivedAt":"2022-02-21T16:05:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Remove the \"else\" branches of the HAVE_VARIADIC_MACROS macro, which\nhave been unconditionally omitted since 765dc168882 (git-compat-util:\nalways enable variadic macros, 2021-01-28).\n\nSince were always omitted, anyone trying to use a compiler without\nvariadic macro support to compile a git since version\ngit v2.31.0 or later would have had a compilation error. 10 months\nacross a few releases since then should have been enough time for\nanyone who cared to run into that and report the issue.\n\nIn addition to that, for anyone unsetting HAVE_VARIADIC_MACROS we've\nbeen emitting extremely verbose warnings since at least\nee4512ed481 (trace2: create new combined trace facility,\n2019-02-22). That's because there is no such thing as a\n\"region_enter_printf\" or \"region_leave_printf\" format, so at least\nunder GCC and Clang everything that includes trace.h (almost every\nfile) emits a couple of warnings about that.\n\nThere's a large benefit to being able to have a hard dependency rely\non variadic macros, the code surrounding usage.c is hard to maintain\nif we need to write two implementations of everything, and by relying\non \"__FILE__\" and \"__LINE__\" along with \"__VA_ARGS__\" we can in the\nfuture make error(), die() etc. log where they were called from. We've\nalso recently merged d67fc4bf0ba (Merge branch 'bc/require-c99',\n2021-12-10) which further cements our hard dependency on C99.\n\nSo let's delete the fallback code, and update our CodingGuidelines to\nnote that we depend on this. The added bullet-point starts with\nlower-case for consistency with other bullet-points in that section.\n\nThe diff in \"trace.h\" is relatively hard to read, since we need to\nretain the existing API docs, which were comments on the code used if\nHAVE_VARIADIC_MACROS was not defined.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n Documentation/CodingGuidelines |   3 +\n banned.h                       |   5 --\n git-compat-util.h              |  12 ----\n trace.c                        |  80 +--------------------\n trace.h                        | 128 +++++++++++++++------------------\n trace2.c                       |  39 ----------\n trace2.h                       |  25 -------\n usage.c                        |  15 +---\n 8 files changed, 64 insertions(+), 243 deletions(-)\n\ndiff --git a/Documentation/CodingGuidelines b/Documentation/CodingGuidelines\nindex c37c43186ea..1a7bc4591cd 100644\n--- a/Documentation/CodingGuidelines\n+++ b/Documentation/CodingGuidelines\n@@ -217,6 +217,9 @@ For C programs:\n    . since mid 2017 with 512f41cf, we have been using designated\n      initializers for array (e.g. \"int array[10] = { [5] = 2 }\").\n \n+   . since early 2021 with 765dc168882, we have been using variadic\n+     macros, mostly for printf-like trace and debug macros.\n+\n    These used to be forbidden, but we have not heard any breakage\n    report, and they are assumed to be safe.\n \ndiff --git a/banned.h b/banned.h\nindex 7ab4f2e4921..6ccf46bc197 100644\n--- a/banned.h\n+++ b/banned.h\n@@ -21,13 +21,8 @@\n \n #undef sprintf\n #undef vsprintf\n-#ifdef HAVE_VARIADIC_MACROS\n #define sprintf(...) BANNED(sprintf)\n #define vsprintf(...) BANNED(vsprintf)\n-#else\n-#define sprintf(buf,fmt,arg) BANNED(sprintf)\n-#define vsprintf(buf,fmt,arg) BANNED(vsprintf)\n-#endif\n \n #undef gmtime\n #define gmtime(t) BANNED(gmtime)\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 2d9b75a021a..e50e2fafaec 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -1256,24 +1256,12 @@ static inline int regexec_buf(const regex_t *preg, const char *buf, size_t size,\n #endif\n #endif\n \n-/*\n- * This is always defined as a first step towards making the use of variadic\n- * macros unconditional. If it causes compilation problems on your platform,\n- * please report it to the Git mailing list at git@vger.kernel.org.\n- */\n-#define HAVE_VARIADIC_MACROS 1\n-\n /* usage.c: only to be used for testing BUG() implementation (see test-tool) */\n extern int BUG_exit_code;\n \n-#ifdef HAVE_VARIADIC_MACROS\n __attribute__((format (printf, 3, 4))) NORETURN\n void BUG_fl(const char *file, int line, const char *fmt, ...);\n #define BUG(...) BUG_fl(__FILE__, __LINE__, __VA_ARGS__)\n-#else\n-__attribute__((format (printf, 1, 2))) NORETURN\n-void BUG(const char *fmt, ...);\n-#endif\n \n /*\n  * Preserves errno, prints a message, but gives no warning for ENOENT.\ndiff --git a/trace.c b/trace.c\nindex f726686fd92..794a087c21e 100644\n--- a/trace.c\n+++ b/trace.c\n@@ -108,16 +108,11 @@ static int prepare_trace_line(const char *file, int line,\n \tgettimeofday(&tv, NULL);\n \tsecs = tv.tv_sec;\n \tlocaltime_r(&secs, &tm);\n-\tstrbuf_addf(buf, \"%02d:%02d:%02d.%06ld \", tm.tm_hour, tm.tm_min,\n-\t\t    tm.tm_sec, (long) tv.tv_usec);\n-\n-#ifdef HAVE_VARIADIC_MACROS\n-\t/* print file:line */\n-\tstrbuf_addf(buf, \"%s:%d \", file, line);\n+\tstrbuf_addf(buf, \"%02d:%02d:%02d.%06ld %s:%d\", tm.tm_hour, tm.tm_min,\n+\t\t    tm.tm_sec, (long) tv.tv_usec, file, line);\n \t/* align trace output (column 40 catches most files names in git) */\n \twhile (buf->len < 40)\n \t\tstrbuf_addch(buf, ' ');\n-#endif\n \n \treturn 1;\n }\n@@ -229,74 +224,6 @@ static void trace_performance_vprintf_fl(const char *file, int line,\n \tstrbuf_release(&buf);\n }\n \n-#ifndef HAVE_VARIADIC_MACROS\n-\n-void trace_printf(const char *format, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, format);\n-\ttrace_vprintf_fl(NULL, 0, &trace_default_key, format, ap);\n-\tva_end(ap);\n-}\n-\n-void trace_printf_key(struct trace_key *key, const char *format, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, format);\n-\ttrace_vprintf_fl(NULL, 0, key, format, ap);\n-\tva_end(ap);\n-}\n-\n-void trace_argv_printf(const char **argv, const char *format, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, format);\n-\ttrace_argv_vprintf_fl(NULL, 0, argv, format, ap);\n-\tva_end(ap);\n-}\n-\n-void trace_strbuf(struct trace_key *key, const struct strbuf *data)\n-{\n-\ttrace_strbuf_fl(NULL, 0, key, data);\n-}\n-\n-void trace_performance(uint64_t nanos, const char *format, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, format);\n-\ttrace_performance_vprintf_fl(NULL, 0, nanos, format, ap);\n-\tva_end(ap);\n-}\n-\n-void trace_performance_since(uint64_t start, const char *format, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, format);\n-\ttrace_performance_vprintf_fl(NULL, 0, getnanotime() - start,\n-\t\t\t\t     format, ap);\n-\tva_end(ap);\n-}\n-\n-void trace_performance_leave(const char *format, ...)\n-{\n-\tva_list ap;\n-\tuint64_t since;\n-\n-\tif (perf_indent)\n-\t\tperf_indent--;\n-\n-\tif (!format) /* Allow callers to leave without tracing anything */\n-\t\treturn;\n-\n-\tsince = perf_start_times[perf_indent];\n-\tva_start(ap, format);\n-\ttrace_performance_vprintf_fl(NULL, 0, getnanotime() - since,\n-\t\t\t\t     format, ap);\n-\tva_end(ap);\n-}\n-\n-#else\n-\n void trace_printf_key_fl(const char *file, int line, struct trace_key *key,\n \t\t\t const char *format, ...)\n {\n@@ -342,9 +269,6 @@ void trace_performance_leave_fl(const char *file, int line,\n \tva_end(ap);\n }\n \n-#endif /* HAVE_VARIADIC_MACROS */\n-\n-\n static const char *quote_crnl(const char *path)\n {\n \tstatic struct strbuf new_path = STRBUF_INIT;\ndiff --git a/trace.h b/trace.h\nindex e25984051aa..4e771f86ac2 100644\n--- a/trace.h\n+++ b/trace.h\n@@ -126,71 +126,6 @@ void trace_command_performance(const char **argv);\n void trace_verbatim(struct trace_key *key, const void *buf, unsigned len);\n uint64_t trace_performance_enter(void);\n \n-#ifndef HAVE_VARIADIC_MACROS\n-\n-/**\n- * Prints a formatted message, similar to printf.\n- */\n-__attribute__((format (printf, 1, 2)))\n-void trace_printf(const char *format, ...);\n-\n-__attribute__((format (printf, 2, 3)))\n-void trace_printf_key(struct trace_key *key, const char *format, ...);\n-\n-/**\n- * Prints a formatted message, followed by a quoted list of arguments.\n- */\n-__attribute__((format (printf, 2, 3)))\n-void trace_argv_printf(const char **argv, const char *format, ...);\n-\n-/**\n- * Prints the strbuf, without additional formatting (i.e. doesn't\n- * choke on `%` or even `\\0`).\n- */\n-void trace_strbuf(struct trace_key *key, const struct strbuf *data);\n-\n-/**\n- * Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled.\n- *\n- * Example:\n- * ------------\n- * uint64_t t = 0;\n- * for (;;) {\n- * \t// ignore\n- * t -= getnanotime();\n- * // code section to measure\n- * t += getnanotime();\n- * // ignore\n- * }\n- * trace_performance(t, \"frotz\");\n- * ------------\n- */\n-__attribute__((format (printf, 2, 3)))\n-void trace_performance(uint64_t nanos, const char *format, ...);\n-\n-/**\n- * Prints elapsed time since 'start' if GIT_TRACE_PERFORMANCE is enabled.\n- *\n- * Example:\n- * ------------\n- * uint64_t start = getnanotime();\n- * // code section to measure\n- * trace_performance_since(start, \"foobar\");\n- * ------------\n- */\n-__attribute__((format (printf, 2, 3)))\n-void trace_performance_since(uint64_t start, const char *format, ...);\n-\n-__attribute__((format (printf, 1, 2)))\n-void trace_performance_leave(const char *format, ...);\n-\n-#else\n-\n-/*\n- * Macros to add file:line - see above for C-style declarations of how these\n- * should be used.\n- */\n-\n /*\n  * TRACE_CONTEXT may be set to __FUNCTION__ if the compiler supports it. The\n  * default is __FILE__, as it is consistent with assert(), and static function\n@@ -204,7 +139,10 @@ void trace_performance_leave(const char *format, ...);\n # define TRACE_CONTEXT __FILE__\n #endif\n \n-/*\n+/**\n+ * Macros to add the file:line of the calling code, instead of that of\n+ * the trace function itself.\n+ *\n  * Note: with C99 variadic macros, __VA_ARGS__ must include the last fixed\n  * parameter ('format' in this case). Otherwise, a call without variable\n  * arguments will have a surplus ','. E.g.:\n@@ -220,6 +158,16 @@ void trace_performance_leave(const char *format, ...);\n  * comma, but this is non-standard.\n  */\n \n+/**\n+ * trace_printf(), accepts \"const char *format, ...\".\n+ *\n+ * Prints a formatted message, similar to printf.\n+ */\n+#define trace_printf(...) trace_printf_key(&trace_default_key, __VA_ARGS__)\n+\n+/**\n+ * trace_printf_key(), accepts \"struct trace_key *key, const char *format, ...\".\n+ */\n #define trace_printf_key(key, ...)\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(key))\t\t\t\t\t    \\\n@@ -227,8 +175,11 @@ void trace_performance_leave(const char *format, ...);\n \t\t\t\t\t    __VA_ARGS__);\t\t    \\\n \t} while (0)\n \n-#define trace_printf(...) trace_printf_key(&trace_default_key, __VA_ARGS__)\n-\n+/**\n+ * trace_argv_printf(), accepts \"struct trace_key *key, const char *format, ...)\".\n+ *\n+ * Prints a formatted message, followed by a quoted list of arguments.\n+ */\n #define trace_argv_printf(argv, ...)\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(&trace_default_key))\t\t\t    \\\n@@ -236,12 +187,36 @@ void trace_performance_leave(const char *format, ...);\n \t\t\t\t\t    argv, __VA_ARGS__);\t\t    \\\n \t} while (0)\n \n+/**\n+ * trace_strbuf(), accepts \"struct trace_key *key, const struct strbuf *data\".\n+ *\n+ * Prints the strbuf, without additional formatting (i.e. doesn't\n+ * choke on `%` or even `\\0`).\n+ */\n #define trace_strbuf(key, data)\t\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(key))\t\t\t\t\t    \\\n \t\t\ttrace_strbuf_fl(TRACE_CONTEXT, __LINE__, key, data);\\\n \t} while (0)\n \n+/**\n+ * trace_performance(), accepts \"uint64_t nanos, const char *format, ...\".\n+ *\n+ * Prints elapsed time (in nanoseconds) if GIT_TRACE_PERFORMANCE is enabled.\n+ *\n+ * Example:\n+ * ------------\n+ * uint64_t t = 0;\n+ * for (;;) {\n+ * \t// ignore\n+ * t -= getnanotime();\n+ * // code section to measure\n+ * t += getnanotime();\n+ * // ignore\n+ * }\n+ * trace_performance(t, \"frotz\");\n+ * ------------\n+ */\n #define trace_performance(nanos, ...)\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(&trace_perf_key))\t\t\t    \\\n@@ -249,6 +224,18 @@ void trace_performance_leave(const char *format, ...);\n \t\t\t\t\t     __VA_ARGS__);\t\t    \\\n \t} while (0)\n \n+/**\n+ * trace_performance_since(), accepts \"uint64_t start, const char *format, ...\".\n+ *\n+ * Prints elapsed time since 'start' if GIT_TRACE_PERFORMANCE is enabled.\n+ *\n+ * Example:\n+ * ------------\n+ * uint64_t start = getnanotime();\n+ * // code section to measure\n+ * trace_performance_since(start, \"foobar\");\n+ * ------------\n+ */\n #define trace_performance_since(start, ...)\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(&trace_perf_key))\t\t\t    \\\n@@ -257,6 +244,9 @@ void trace_performance_leave(const char *format, ...);\n \t\t\t\t\t     __VA_ARGS__);\t\t    \\\n \t} while (0)\n \n+/**\n+ * trace_performance_leave(), accepts \"const char *format, ...\".\n+ */\n #define trace_performance_leave(...)\t\t\t\t\t    \\\n \tdo {\t\t\t\t\t\t\t\t    \\\n \t\tif (trace_pass_fl(&trace_perf_key))\t\t\t    \\\n@@ -285,6 +275,4 @@ static inline int trace_pass_fl(struct trace_key *key)\n \treturn key->fd || !key->initialized;\n }\n \n-#endif /* HAVE_VARIADIC_MACROS */\n-\n #endif /* TRACE_H */\ndiff --git a/trace2.c b/trace2.c\nindex b2d471526fd..179caa72cfe 100644\n--- a/trace2.c\n+++ b/trace2.c\n@@ -641,20 +641,6 @@ void trace2_region_enter_printf_fl(const char *file, int line,\n \tva_end(ap);\n }\n \n-#ifndef HAVE_VARIADIC_MACROS\n-void trace2_region_enter_printf(const char *category, const char *label,\n-\t\t\t\tconst struct repository *repo, const char *fmt,\n-\t\t\t\t...)\n-{\n-\tva_list ap;\n-\n-\tva_start(ap, fmt);\n-\ttrace2_region_enter_printf_va_fl(NULL, 0, category, label, repo, fmt,\n-\t\t\t\t\t ap);\n-\tva_end(ap);\n-}\n-#endif\n-\n void trace2_region_leave_printf_va_fl(const char *file, int line,\n \t\t\t\t      const char *category, const char *label,\n \t\t\t\t      const struct repository *repo,\n@@ -717,20 +703,6 @@ void trace2_region_leave_printf_fl(const char *file, int line,\n \tva_end(ap);\n }\n \n-#ifndef HAVE_VARIADIC_MACROS\n-void trace2_region_leave_printf(const char *category, const char *label,\n-\t\t\t\tconst struct repository *repo, const char *fmt,\n-\t\t\t\t...)\n-{\n-\tva_list ap;\n-\n-\tva_start(ap, fmt);\n-\ttrace2_region_leave_printf_va_fl(NULL, 0, category, label, repo, fmt,\n-\t\t\t\t\t ap);\n-\tva_end(ap);\n-}\n-#endif\n-\n void trace2_data_string_fl(const char *file, int line, const char *category,\n \t\t\t   const struct repository *repo, const char *key,\n \t\t\t   const char *value)\n@@ -826,17 +798,6 @@ void trace2_printf_fl(const char *file, int line, const char *fmt, ...)\n \tva_end(ap);\n }\n \n-#ifndef HAVE_VARIADIC_MACROS\n-void trace2_printf(const char *fmt, ...)\n-{\n-\tva_list ap;\n-\n-\tva_start(ap, fmt);\n-\ttrace2_printf_va_fl(NULL, 0, fmt, ap);\n-\tva_end(ap);\n-}\n-#endif\n-\n const char *trace2_session_id(void)\n {\n \treturn tr2_sid_get();\ndiff --git a/trace2.h b/trace2.h\nindex 0cc7b5f5312..1b109f57d0a 100644\n--- a/trace2.h\n+++ b/trace2.h\n@@ -397,18 +397,9 @@ void trace2_region_enter_printf_fl(const char *file, int line,\n \t\t\t\t   const struct repository *repo,\n \t\t\t\t   const char *fmt, ...);\n \n-#ifdef HAVE_VARIADIC_MACROS\n #define trace2_region_enter_printf(category, label, repo, ...)                 \\\n \ttrace2_region_enter_printf_fl(__FILE__, __LINE__, (category), (label), \\\n \t\t\t\t      (repo), __VA_ARGS__)\n-#else\n-/* clang-format off */\n-__attribute__((format (region_enter_printf, 4, 5)))\n-void trace2_region_enter_printf(const char *category, const char *label,\n-\t\t\t\tconst struct repository *repo, const char *fmt,\n-\t\t\t\t...);\n-/* clang-format on */\n-#endif\n \n /**\n  * Emit a 'region_leave' event for <category>.<label> with optional\n@@ -442,18 +433,9 @@ void trace2_region_leave_printf_fl(const char *file, int line,\n \t\t\t\t   const struct repository *repo,\n \t\t\t\t   const char *fmt, ...);\n \n-#ifdef HAVE_VARIADIC_MACROS\n #define trace2_region_leave_printf(category, label, repo, ...)                 \\\n \ttrace2_region_leave_printf_fl(__FILE__, __LINE__, (category), (label), \\\n \t\t\t\t      (repo), __VA_ARGS__)\n-#else\n-/* clang-format off */\n-__attribute__((format (region_leave_printf, 4, 5)))\n-void trace2_region_leave_printf(const char *category, const char *label,\n-\t\t\t\tconst struct repository *repo, const char *fmt,\n-\t\t\t\t...);\n-/* clang-format on */\n-#endif\n \n /**\n  * Emit a key-value pair 'data' event of the form <category>.<key> = <value>.\n@@ -506,14 +488,7 @@ void trace2_printf_va_fl(const char *file, int line, const char *fmt,\n \n void trace2_printf_fl(const char *file, int line, const char *fmt, ...);\n \n-#ifdef HAVE_VARIADIC_MACROS\n #define trace2_printf(...) trace2_printf_fl(__FILE__, __LINE__, __VA_ARGS__)\n-#else\n-/* clang-format off */\n-__attribute__((format (printf, 1, 2)))\n-void trace2_printf(const char *fmt, ...);\n-/* clang-format on */\n-#endif\n \n /*\n  * Optional platform-specific code to dump information about the\ndiff --git a/usage.c b/usage.c\nindex 9943dd8742e..b738dd178b3 100644\n--- a/usage.c\n+++ b/usage.c\n@@ -299,10 +299,7 @@ static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_lis\n \tva_copy(params_copy, params);\n \n \t/* truncation via snprintf is OK here */\n-\tif (file)\n-\t\tsnprintf(prefix, sizeof(prefix), \"BUG: %s:%d: \", file, line);\n-\telse\n-\t\tsnprintf(prefix, sizeof(prefix), \"BUG: \");\n+\tsnprintf(prefix, sizeof(prefix), \"BUG: %s:%d: \", file, line);\n \n \tvreportf(prefix, fmt, params);\n \n@@ -317,7 +314,6 @@ static NORETURN void BUG_vfl(const char *file, int line, const char *fmt, va_lis\n \tabort();\n }\n \n-#ifdef HAVE_VARIADIC_MACROS\n NORETURN void BUG_fl(const char *file, int line, const char *fmt, ...)\n {\n \tva_list ap;\n@@ -325,15 +321,6 @@ NORETURN void BUG_fl(const char *file, int line, const char *fmt, ...)\n \tBUG_vfl(file, line, fmt, ap);\n \tva_end(ap);\n }\n-#else\n-NORETURN void BUG(const char *fmt, ...)\n-{\n-\tva_list ap;\n-\tva_start(ap, fmt);\n-\tBUG_vfl(NULL, 0, fmt, ap);\n-\tva_end(ap);\n-}\n-#endif\n \n #ifdef SUPPRESS_ANNOTATED_LEAKS\n void unleak_memory(const void *ptr, size_t len)\n-- \n2.35.1.1132.ga1fe46f8690\n\n"}]}