hex: add and use strbuf_add_oid_hex()

Add a function for adding the full hexadecimal hash value of an object ID to a strbuf. It's thread-safe and slightly more efficient than using strbuf_addstr() with oid_to_hex() because it doesn't have to determine the length of the string or copy it from the intermediate static buffer. Add and apply a semantic patch to use it throughout the code base. I get a tiny speedup for git log showing a single hash per commit: Benchmark 1: ./git_main log --format=%H Time (mean ± σ): 91.2 ms ± 0.7 ms [User: 51.9 ms, System: 38.6 ms] Range (min … max): 89.8 ms … 92.6 ms 31 runs Benchmark 2: ./git log --format=%H Time (mean ± σ): 90.5 ms ± 0.7 ms [User: 51.0 ms, System: 38.8 ms] Range (min … max): 89.2 ms … 92.3 ms 32 runs Summary ./git log --format=%H ran 1.01 ± 0.01 times faster than ./git_main log --format=%H Signed-off-by: René Scharfe <l.s.r@web.de> Signed-off-by: Junio C Hamano <gitster@pobox.com>

René Scharfe committed May 13, 2026 at 17:49 UTC 63621bcbba81a131794d510bcedfa08d9318219c
14 files changed +37 -17
bisect.c
+1 -1
@@ -512,7 +512,7 @@ static char *join_oid_array_hex(struct oid_array *array, char delim)
512 int i;
513
514 for (i = 0; i < array->nr; i++) {
515 - strbuf_addstr(&joined_hexs, oid_to_hex(array->oid + i));
515 + strbuf_add_oid_hex(&joined_hexs, array->oid + i);
516 if (i + 1 < array->nr)
517 strbuf_addch(&joined_hexs, delim);
518 }
builtin/bisect.c
+1 -1
@@ -833,7 +833,7 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, int argc,
833 if (!repo_get_oid(the_repository, head, &head_oid) &&
834 !starts_with(head, "refs/heads/")) {
835 strbuf_reset(&start_head);
836 - strbuf_addstr(&start_head, oid_to_hex(&head_oid));
836 + strbuf_add_oid_hex(&start_head, &head_oid);
837 } else if (!repo_get_oid(the_repository, head, &head_oid) &&
838 skip_prefix(head, "refs/heads/", &head)) {
839 strbuf_addstr(&start_head, head);
builtin/cat-file.c
+2 -3
@@ -320,7 +320,7 @@ static int expand_atom(struct strbuf *sb, const char *atom, int len,
320 {
321 if (is_atom("objectname", atom, len)) {
322 if (!data->mark_query)
323 - strbuf_addstr(sb, oid_to_hex(&data->oid));
323 + strbuf_add_oid_hex(sb, &data->oid);
324 } else if (is_atom("objecttype", atom, len)) {
325 if (data->mark_query)
326 data->info.typep = &data->type;
@@ -345,8 +345,7 @@ static int expand_atom(struct strbuf *sb, const char *atom, int len,
345 if (data->mark_query)
346 data->info.delta_base_oid = &data->delta_base_oid;
347 else
348 - strbuf_addstr(sb,
349 - oid_to_hex(&data->delta_base_oid));
348 + strbuf_add_oid_hex(sb, &data->delta_base_oid);
349 } else if (is_atom("objectmode", atom, len)) {
350 if (!data->mark_query && !(S_IFINVALID == data->mode))
351 strbuf_addf(sb, "%06o", data->mode);
builtin/replace.c
+1 -1
@@ -127,7 +127,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
127 }
128
129 strbuf_setlen(&ref, base_len);
130 - strbuf_addstr(&ref, oid_to_hex(&oid));
130 + strbuf_add_oid_hex(&ref, &oid);
131 full_hex = ref.buf + base_len;
132
133 if (refs_read_ref(get_main_ref_store(the_repository), ref.buf, &oid)) {
convert.c
+1 -1
@@ -1239,7 +1239,7 @@ static int ident_to_worktree(const char *src, size_t len,
1239
1240 /* step 4: substitute */
1241 strbuf_addstr(buf, "Id: ");
1242 - strbuf_addstr(buf, oid_to_hex(&oid));
1242 + strbuf_add_oid_hex(buf, &oid);
1243 strbuf_addstr(buf, " $");
1244 }
1245 strbuf_add(buf, src, len);
fsck.c
+1 -1
@@ -344,7 +344,7 @@ const char *fsck_describe_object(struct fsck_options *options,
344 buf = bufs + b;
345 b = (b + 1) % ARRAY_SIZE(bufs);
346 strbuf_reset(buf);
347 - strbuf_addstr(buf, oid_to_hex(oid));
347 + strbuf_add_oid_hex(buf, oid);
348 if (name)
349 strbuf_addf(buf, " (%s)", name);
350
hex.c
+10
@@ -3,6 +3,7 @@
3 #include "git-compat-util.h"
4 #include "hash.h"
5 #include "hex.h"
6 +#include "strbuf.h"
7
8 static int get_hash_hex_algop(const char *hex, unsigned char *hash,
9 const struct git_hash_algo *algop)
@@ -122,3 +123,12 @@ char *oid_to_hex(const struct object_id *oid)
123 {
124 return hash_to_hex_algop(oid->hash, &hash_algos[oid->algo]);
125 }
126 +
127 +void strbuf_add_oid_hex(struct strbuf *sb, const struct object_id *oid)
128 +{
129 + const struct git_hash_algo *algop = oid->algo ?
130 + &hash_algos[oid->algo] : the_hash_algo;
131 + strbuf_grow(sb, algop->hexsz);
132 + hash_to_hex_algop_r(sb->buf + sb->len, oid->hash, algop);
133 + strbuf_setlen(sb, sb->len + algop->hexsz);
134 +}
hex.h
+5
@@ -33,6 +33,11 @@ char *oid_to_hex_r(char *out, const struct object_id *oid);
33 char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *); /* static buffer result! */
34 char *oid_to_hex(const struct object_id *oid); /* same static buffer */
35
36 +struct strbuf;
37 +
38 +/* Apply oid_to_hex_r() to a strbuf to append the hexadecimal hash. */
39 +void strbuf_add_oid_hex(struct strbuf *sb, const struct object_id *oid);
40 +
41 /*
42 * Parse a 40-character hexadecimal object ID starting from hex, updating the
43 * pointer specified by end when parsing stops. The resulting object ID is
pretty.c
+4 -4
@@ -662,7 +662,7 @@ static void add_merge_info(const struct pretty_print_context *pp,
662 if (pp->abbrev)
663 strbuf_add_unique_abbrev(sb, oidp, pp->abbrev);
664 else
665 - strbuf_addstr(sb, oid_to_hex(oidp));
665 + strbuf_add_oid_hex(sb, oidp);
666 parent = parent->next;
667 }
668 strbuf_addch(sb, '\n');
@@ -1567,7 +1567,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
1567 switch (placeholder[0]) {
1568 case 'H': /* commit hash */
1569 strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_COMMIT));
1570 - strbuf_addstr(sb, oid_to_hex(&commit->object.oid));
1570 + strbuf_add_oid_hex(sb, &commit->object.oid);
1571 strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET));
1572 return 1;
1573 case 'h': /* abbreviated commit hash */
@@ -1577,7 +1577,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
1577 strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET));
1578 return 1;
1579 case 'T': /* tree hash */
1580 - strbuf_addstr(sb, oid_to_hex(get_commit_tree_oid(commit)));
1580 + strbuf_add_oid_hex(sb, get_commit_tree_oid(commit));
1581 return 1;
1582 case 't': /* abbreviated tree hash */
1583 strbuf_add_unique_abbrev(sb,
@@ -1588,7 +1588,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
1588 for (p = commit->parents; p; p = p->next) {
1589 if (p != commit->parents)
1590 strbuf_addch(sb, ' ');
1591 - strbuf_addstr(sb, oid_to_hex(&p->item->object.oid));
1591 + strbuf_add_oid_hex(sb, &p->item->object.oid);
1592 }
1593 return 1;
1594 case 'p': /* abbreviated parent hashes */
refs.c
+1 -1
@@ -2498,7 +2498,7 @@ int refs_update_symref_extended(struct ref_store *refs, const char *ref,
2498 if (referent && refs_read_symbolic_ref(refs, ref, referent) == NOT_A_SYMREF) {
2499 struct object_id oid;
2500 if (!refs_read_ref(refs, ref, &oid)) {
2501 - strbuf_addstr(referent, oid_to_hex(&oid));
2501 + strbuf_add_oid_hex(referent, &oid);
2502 ret = NOT_A_SYMREF;
2503 }
2504 }
sequencer.c
+2 -2
@@ -2223,7 +2223,7 @@ static void refer_to_commit(struct repository *r, struct strbuf *msgbuf,
2223 repo_format_commit_message(r, commit,
2224 "%h (%s, %ad)", msgbuf, &ctx);
2225 } else {
2226 - strbuf_addstr(msgbuf, oid_to_hex(&commit->object.oid));
2226 + strbuf_add_oid_hex(msgbuf, &commit->object.oid);
2227 }
2228 }
2229
@@ -2395,7 +2395,7 @@ static int do_pick_commit(struct repository *r,
2395 if (!has_conforming_footer(&ctx->message, NULL, 0))
2396 strbuf_addch(&ctx->message, '\n');
2397 strbuf_addstr(&ctx->message, cherry_picked_prefix);
2398 - strbuf_addstr(&ctx->message, oid_to_hex(&commit->object.oid));
2398 + strbuf_add_oid_hex(&ctx->message, &commit->object.oid);
2399 strbuf_addstr(&ctx->message, ")\n");
2400 }
2401 if (!is_fixup(command))
shallow.c
+1 -1
@@ -395,7 +395,7 @@ static int write_shallow_commits_1(struct strbuf *out, int use_pack_protocol,
395 if (!extra)
396 return data.count;
397 for (size_t i = 0; i < extra->nr; i++) {
398 - strbuf_addstr(out, oid_to_hex(extra->oid + i));
398 + strbuf_add_oid_hex(out, extra->oid + i);
399 strbuf_addch(out, '\n');
400 data.count++;
401 }
tools/coccinelle/strbuf.cocci
+6
@@ -78,3 +78,9 @@ struct strbuf SB;
78 @@
79 - SB.buf ? SB.buf : ""
80 + SB.buf
81 +
82 +@@
83 +expression SB, OID;
84 +@@
85 +- strbuf_addstr(SB, oid_to_hex(OID))
86 ++ strbuf_add_oid_hex(SB, OID)
transport-helper.c
+1 -1
@@ -1053,7 +1053,7 @@ static int push_refs_with_push(struct transport *transport,
1053 if (ref->peer_ref)
1054 strbuf_addstr(&buf, ref->peer_ref->name);
1055 else
1056 - strbuf_addstr(&buf, oid_to_hex(&ref->new_oid));
1056 + strbuf_add_oid_hex(&buf, &ref->new_oid);
1057 }
1058 strbuf_addch(&buf, ':');
1059 strbuf_addstr(&buf, ref->name);