* [PATCH] hex: add and use strbuf_add_oid_hex()
@ 2026-05-13 15:49 René Scharfe
2026-05-13 16:01 ` Jeff King
0 siblings, 1 reply; 3+ messages in thread
From: René Scharfe @ 2026-05-13 15:49 UTC (permalink / raw)
To: Git List
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>
---
bisect.c | 2 +-
builtin/bisect.c | 2 +-
builtin/cat-file.c | 5 ++---
builtin/replace.c | 2 +-
convert.c | 2 +-
fsck.c | 2 +-
hex.c | 10 ++++++++++
hex.h | 5 +++++
pretty.c | 8 ++++----
refs.c | 2 +-
sequencer.c | 4 ++--
shallow.c | 2 +-
tools/coccinelle/strbuf.cocci | 6 ++++++
transport-helper.c | 2 +-
14 files changed, 37 insertions(+), 17 deletions(-)
diff --git a/bisect.c b/bisect.c
index ef17a442e5..e67226a6dc 100644
--- a/bisect.c
+++ b/bisect.c
@@ -512,7 +512,7 @@ static char *join_oid_array_hex(struct oid_array *array, char delim)
int i;
for (i = 0; i < array->nr; i++) {
- strbuf_addstr(&joined_hexs, oid_to_hex(array->oid + i));
+ strbuf_add_oid_hex(&joined_hexs, array->oid + i);
if (i + 1 < array->nr)
strbuf_addch(&joined_hexs, delim);
}
diff --git a/builtin/bisect.c b/builtin/bisect.c
index 4520e585d0..0f679e7af9 100644
--- a/builtin/bisect.c
+++ b/builtin/bisect.c
@@ -833,7 +833,7 @@ static enum bisect_error bisect_start(struct bisect_terms *terms, int argc,
if (!repo_get_oid(the_repository, head, &head_oid) &&
!starts_with(head, "refs/heads/")) {
strbuf_reset(&start_head);
- strbuf_addstr(&start_head, oid_to_hex(&head_oid));
+ strbuf_add_oid_hex(&start_head, &head_oid);
} else if (!repo_get_oid(the_repository, head, &head_oid) &&
skip_prefix(head, "refs/heads/", &head)) {
strbuf_addstr(&start_head, head);
diff --git a/builtin/cat-file.c b/builtin/cat-file.c
index d9fbad5358..f015e5f415 100644
--- a/builtin/cat-file.c
+++ b/builtin/cat-file.c
@@ -320,7 +320,7 @@ static int expand_atom(struct strbuf *sb, const char *atom, int len,
{
if (is_atom("objectname", atom, len)) {
if (!data->mark_query)
- strbuf_addstr(sb, oid_to_hex(&data->oid));
+ strbuf_add_oid_hex(sb, &data->oid);
} else if (is_atom("objecttype", atom, len)) {
if (data->mark_query)
data->info.typep = &data->type;
@@ -345,8 +345,7 @@ static int expand_atom(struct strbuf *sb, const char *atom, int len,
if (data->mark_query)
data->info.delta_base_oid = &data->delta_base_oid;
else
- strbuf_addstr(sb,
- oid_to_hex(&data->delta_base_oid));
+ strbuf_add_oid_hex(sb, &data->delta_base_oid);
} else if (is_atom("objectmode", atom, len)) {
if (!data->mark_query && !(S_IFINVALID == data->mode))
strbuf_addf(sb, "%06o", data->mode);
diff --git a/builtin/replace.c b/builtin/replace.c
index 4c62c5ab58..aed6b2c8de 100644
--- a/builtin/replace.c
+++ b/builtin/replace.c
@@ -127,7 +127,7 @@ static int for_each_replace_name(const char **argv, each_replace_name_fn fn)
}
strbuf_setlen(&ref, base_len);
- strbuf_addstr(&ref, oid_to_hex(&oid));
+ strbuf_add_oid_hex(&ref, &oid);
full_hex = ref.buf + base_len;
if (refs_read_ref(get_main_ref_store(the_repository), ref.buf, &oid)) {
diff --git a/convert.c b/convert.c
index eae36c8a59..036506842c 100644
--- a/convert.c
+++ b/convert.c
@@ -1239,7 +1239,7 @@ static int ident_to_worktree(const char *src, size_t len,
/* step 4: substitute */
strbuf_addstr(buf, "Id: ");
- strbuf_addstr(buf, oid_to_hex(&oid));
+ strbuf_add_oid_hex(buf, &oid);
strbuf_addstr(buf, " $");
}
strbuf_add(buf, src, len);
diff --git a/fsck.c b/fsck.c
index b72200c352..b4ffee6a04 100644
--- a/fsck.c
+++ b/fsck.c
@@ -344,7 +344,7 @@ const char *fsck_describe_object(struct fsck_options *options,
buf = bufs + b;
b = (b + 1) % ARRAY_SIZE(bufs);
strbuf_reset(buf);
- strbuf_addstr(buf, oid_to_hex(oid));
+ strbuf_add_oid_hex(buf, oid);
if (name)
strbuf_addf(buf, " (%s)", name);
diff --git a/hex.c b/hex.c
index bc756722ca..f02832140d 100644
--- a/hex.c
+++ b/hex.c
@@ -3,6 +3,7 @@
#include "git-compat-util.h"
#include "hash.h"
#include "hex.h"
+#include "strbuf.h"
static int get_hash_hex_algop(const char *hex, unsigned char *hash,
const struct git_hash_algo *algop)
@@ -122,3 +123,12 @@ char *oid_to_hex(const struct object_id *oid)
{
return hash_to_hex_algop(oid->hash, &hash_algos[oid->algo]);
}
+
+void strbuf_add_oid_hex(struct strbuf *sb, const struct object_id *oid)
+{
+ const struct git_hash_algo *algop = oid->algo ?
+ &hash_algos[oid->algo] : the_hash_algo;
+ strbuf_grow(sb, algop->hexsz);
+ hash_to_hex_algop_r(sb->buf + sb->len, oid->hash, algop);
+ strbuf_setlen(sb, sb->len + algop->hexsz);
+}
diff --git a/hex.h b/hex.h
index 1e9a65d83a..f15c7e2220 100644
--- a/hex.h
+++ b/hex.h
@@ -33,6 +33,11 @@ char *oid_to_hex_r(char *out, const struct object_id *oid);
char *hash_to_hex_algop(const unsigned char *hash, const struct git_hash_algo *); /* static buffer result! */
char *oid_to_hex(const struct object_id *oid); /* same static buffer */
+struct strbuf;
+
+/* Apply oid_to_hex_r() to a strbuf to append the hexadecimal hash. */
+void strbuf_add_oid_hex(struct strbuf *sb, const struct object_id *oid);
+
/*
* Parse a 40-character hexadecimal object ID starting from hex, updating the
* pointer specified by end when parsing stops. The resulting object ID is
diff --git a/pretty.c b/pretty.c
index 814803980b..2684223946 100644
--- a/pretty.c
+++ b/pretty.c
@@ -662,7 +662,7 @@ static void add_merge_info(const struct pretty_print_context *pp,
if (pp->abbrev)
strbuf_add_unique_abbrev(sb, oidp, pp->abbrev);
else
- strbuf_addstr(sb, oid_to_hex(oidp));
+ strbuf_add_oid_hex(sb, oidp);
parent = parent->next;
}
strbuf_addch(sb, '\n');
@@ -1567,7 +1567,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
switch (placeholder[0]) {
case 'H': /* commit hash */
strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_COMMIT));
- strbuf_addstr(sb, oid_to_hex(&commit->object.oid));
+ strbuf_add_oid_hex(sb, &commit->object.oid);
strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET));
return 1;
case 'h': /* abbreviated commit hash */
@@ -1577,7 +1577,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET));
return 1;
case 'T': /* tree hash */
- strbuf_addstr(sb, oid_to_hex(get_commit_tree_oid(commit)));
+ strbuf_add_oid_hex(sb, get_commit_tree_oid(commit));
return 1;
case 't': /* abbreviated tree hash */
strbuf_add_unique_abbrev(sb,
@@ -1588,7 +1588,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
for (p = commit->parents; p; p = p->next) {
if (p != commit->parents)
strbuf_addch(sb, ' ');
- strbuf_addstr(sb, oid_to_hex(&p->item->object.oid));
+ strbuf_add_oid_hex(sb, &p->item->object.oid);
}
return 1;
case 'p': /* abbreviated parent hashes */
diff --git a/refs.c b/refs.c
index 844785219d..ee92f18d41 100644
--- a/refs.c
+++ b/refs.c
@@ -2498,7 +2498,7 @@ int refs_update_symref_extended(struct ref_store *refs, const char *ref,
if (referent && refs_read_symbolic_ref(refs, ref, referent) == NOT_A_SYMREF) {
struct object_id oid;
if (!refs_read_ref(refs, ref, &oid)) {
- strbuf_addstr(referent, oid_to_hex(&oid));
+ strbuf_add_oid_hex(referent, &oid);
ret = NOT_A_SYMREF;
}
}
diff --git a/sequencer.c b/sequencer.c
index b7d8dca47f..b4df04b672 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -2223,7 +2223,7 @@ static void refer_to_commit(struct repository *r, struct strbuf *msgbuf,
repo_format_commit_message(r, commit,
"%h (%s, %ad)", msgbuf, &ctx);
} else {
- strbuf_addstr(msgbuf, oid_to_hex(&commit->object.oid));
+ strbuf_add_oid_hex(msgbuf, &commit->object.oid);
}
}
@@ -2395,7 +2395,7 @@ static int do_pick_commit(struct repository *r,
if (!has_conforming_footer(&ctx->message, NULL, 0))
strbuf_addch(&ctx->message, '\n');
strbuf_addstr(&ctx->message, cherry_picked_prefix);
- strbuf_addstr(&ctx->message, oid_to_hex(&commit->object.oid));
+ strbuf_add_oid_hex(&ctx->message, &commit->object.oid);
strbuf_addstr(&ctx->message, ")\n");
}
if (!is_fixup(command))
diff --git a/shallow.c b/shallow.c
index a8ad92e303..b4b4e2e32a 100644
--- a/shallow.c
+++ b/shallow.c
@@ -395,7 +395,7 @@ static int write_shallow_commits_1(struct strbuf *out, int use_pack_protocol,
if (!extra)
return data.count;
for (size_t i = 0; i < extra->nr; i++) {
- strbuf_addstr(out, oid_to_hex(extra->oid + i));
+ strbuf_add_oid_hex(out, extra->oid + i);
strbuf_addch(out, '\n');
data.count++;
}
diff --git a/tools/coccinelle/strbuf.cocci b/tools/coccinelle/strbuf.cocci
index f586128329..667903d1d4 100644
--- a/tools/coccinelle/strbuf.cocci
+++ b/tools/coccinelle/strbuf.cocci
@@ -78,3 +78,9 @@ struct strbuf SB;
@@
- SB.buf ? SB.buf : ""
+ SB.buf
+
+@@
+expression SB, OID;
+@@
+- strbuf_addstr(SB, oid_to_hex(OID))
++ strbuf_add_oid_hex(SB, OID)
diff --git a/transport-helper.c b/transport-helper.c
index 4614036c99..4a54769789 100644
--- a/transport-helper.c
+++ b/transport-helper.c
@@ -1051,7 +1051,7 @@ static int push_refs_with_push(struct transport *transport,
if (ref->peer_ref)
strbuf_addstr(&buf, ref->peer_ref->name);
else
- strbuf_addstr(&buf, oid_to_hex(&ref->new_oid));
+ strbuf_add_oid_hex(&buf, &ref->new_oid);
}
strbuf_addch(&buf, ':');
strbuf_addstr(&buf, ref->name);
--
2.54.0
^ permalink raw reply related [flat|nested] 3+ messages in thread
* Re: [PATCH] hex: add and use strbuf_add_oid_hex()
2026-05-13 15:49 [PATCH] hex: add and use strbuf_add_oid_hex() René Scharfe
@ 2026-05-13 16:01 ` Jeff King
2026-05-13 16:55 ` René Scharfe
0 siblings, 1 reply; 3+ messages in thread
From: Jeff King @ 2026-05-13 16:01 UTC (permalink / raw)
To: René Scharfe; +Cc: Git List
On Wed, May 13, 2026 at 05:49:11PM +0200, René Scharfe wrote:
> 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
Probably the most extreme benchmark would be:
git cat-file --batch-all-objects --batch-check='%(objectname)'
which is really just dumping the oids from packfiles. I got ~3% speedup,
though like yours it's within the run-to-run noise.
I think this is worth doing solely for removing more instances of global
buffers, though.
-Peff
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: [PATCH] hex: add and use strbuf_add_oid_hex()
2026-05-13 16:01 ` Jeff King
@ 2026-05-13 16:55 ` René Scharfe
0 siblings, 0 replies; 3+ messages in thread
From: René Scharfe @ 2026-05-13 16:55 UTC (permalink / raw)
To: Jeff King; +Cc: Git List
On 5/13/26 6:01 PM, Jeff King wrote:
> On Wed, May 13, 2026 at 05:49:11PM +0200, René Scharfe wrote:
>
>> 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
>
> Probably the most extreme benchmark would be:
>
> git cat-file --batch-all-objects --batch-check='%(objectname)'
>
> which is really just dumping the oids from packfiles. I got ~3% speedup,
> though like yours it's within the run-to-run noise.
Hmm, should've lead with that; on an Apple M1:
Benchmark 1: ./git_main cat-file --batch-all-objects --batch-check='%(objectname)'
Time (mean ± σ): 117.9 ms ± 0.2 ms [User: 111.3 ms, System: 5.6 ms]
Range (min … max): 117.5 ms … 118.5 ms 24 runs
Benchmark 2: ./git cat-file --batch-all-objects --batch-check='%(objectname)'
Time (mean ± σ): 109.9 ms ± 0.2 ms [User: 103.2 ms, System: 5.6 ms]
Range (min … max): 109.5 ms … 110.8 ms 26 runs
Summary
./git cat-file --batch-all-objects --batch-check='%(objectname)' ran
1.07 ± 0.00 times faster than ./git_main cat-file --batch-all-objects --batch-check='%(objectname)'
... and on an M5:
Benchmark 1: ./git_main cat-file --batch-all-objects --batch-check='%(objectname)'
Time (mean ± σ): 76.8 ms ± 1.9 ms [User: 73.0 ms, System: 3.2 ms]
Range (min … max): 73.7 ms … 80.7 ms 38 runs
Benchmark 2: ./git cat-file --batch-all-objects --batch-check='%(objectname)'
Time (mean ± σ): 70.8 ms ± 1.0 ms [User: 66.9 ms, System: 3.3 ms]
Range (min … max): 69.4 ms … 73.7 ms 41 runs
Summary
./git cat-file --batch-all-objects --batch-check='%(objectname)' ran
1.08 ± 0.03 times faster than ./git_main cat-file --batch-all-objects --batch-check='%(objectname)'‚
René
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2026-05-13 16:55 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-05-13 15:49 [PATCH] hex: add and use strbuf_add_oid_hex() René Scharfe
2026-05-13 16:01 ` Jeff King
2026-05-13 16:55 ` René Scharfe
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox