hash: make git_hash_discard() idempotent

You must always either finalize or discard a hash context to release any resources, but you must call only one such function. This creates extra work for some callers, since their cleanup code paths need to know whether they got there via their happy path (and the finalization happened) or due to an error (in which case they need to discard). Let's add an "active" flag that turns a redundant discard into a noop. That lets you safely do this: git_hash_init(&ctx, algo); ... if (some_error) goto out; ... git_hash_final(result, &ctx); out: git_hash_discard(&ctx); This should avoid future errors, and will also let us simplify a few existing callers (in future patches). Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Jul 7, 2026 at 23:52 UTC 2c51615d3f57e116c60e825b4a0d587a6f0da12a
2 files changed +7
hash.c
+6
@@ -285,6 +285,7 @@ void git_hash_free(struct git_hash_ctx *ctx)
285 void git_hash_init(struct git_hash_ctx *ctx, const struct git_hash_algo *algop)
286 {
287 algop->init_fn(ctx);
288 + ctx->active = true;
289 }
290
291 void git_hash_clone(struct git_hash_ctx *dst, const struct git_hash_ctx *src)
@@ -300,16 +301,21 @@ void git_hash_update(struct git_hash_ctx *ctx, const void *in, size_t len)
301 void git_hash_final(unsigned char *hash, struct git_hash_ctx *ctx)
302 {
303 ctx->algop->final_fn(hash, ctx);
304 + ctx->active = false;
305 }
306
307 void git_hash_final_oid(struct object_id *oid, struct git_hash_ctx *ctx)
308 {
309 ctx->algop->final_oid_fn(oid, ctx);
310 + ctx->active = false;
311 }
312
313 void git_hash_discard(struct git_hash_ctx *ctx)
314 {
315 + if (!ctx->active)
316 + return;
317 ctx->algop->discard_fn(ctx);
318 + ctx->active = false;
319 }
320
321 uint32_t hash_algo_by_name(const char *name)
hash.h
+1
@@ -281,6 +281,7 @@ struct git_hash_ctx {
281 git_SHA_CTX_unsafe sha1_unsafe;
282 git_SHA256_CTX sha256;
283 } state;
284 + bool active;
285 };
286
287 typedef void (*git_hash_init_fn)(struct git_hash_ctx *ctx);