{"thread":{"id":"65898","subject":"[PATCH 0/3] bloom-related leak fixes","startedAt":"2026-07-01T06:35:40Z","lastAt":"2026-07-01T17:33:28Z","messageCount":11,"participants":["Jeff King","Patrick Steinhardt","Derrick Stolee","Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"546820","messageId":"20260701063538.GA2579765@coredump.intra.peff.net","threadId":"65898","inReplyTo":null,"subject":"[PATCH 0/3] bloom-related leak fixes","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2026-07-01T06:35:38Z","receivedAt":"2026-07-01T06:35:40Z","isPatch":true,"body":"Here are a few small leak fixes that only show up when you run the test\nsuite with GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=1.\n\nCombined with the commit-graph leak-fix here:\n\n  https://lore.kernel.org/git/20260630064301.GB3733961@coredump.intra.peff.net/\n\nand Kaartic's pending fix from this thread:\n\n  https://lore.kernel.org/git/20260614141600.620272-1-kaartic.sivaraam@gmail.com/\n\nThis will fix most of the leaks we'd see if we ran linux-TEST-vars jobs\nwith leak-checking. There are a few more related to building with\nopenssl for sha1, but I'll tackle those separately.\n\n  [1/3]: bloom: make bloom-filter slab initialization idempotent\n  [2/3]: revision: avoid leaking bloom keyvecs with multiple traversals\n  [3/3]: line-log: drop extra copy of range with bloom filters\n\n bloom.c    | 5 +++++\n line-log.c | 3 +--\n revision.c | 2 ++\n 3 files changed, 8 insertions(+), 2 deletions(-)\n\n-Peff\n"},{"id":"546821","messageId":"20260701063942.GA2580331@coredump.intra.peff.net","threadId":"65898","inReplyTo":"20260701063538.GA2579765@coredump.intra.peff.net","subject":"[PATCH 1/3] bloom: make bloom-filter slab initialization idempotent","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2026-07-01T06:39:42Z","receivedAt":"2026-07-01T06:39:44Z","isPatch":true,"body":"Before using any of the commit-graph bloom-filter code, somebody needs\nto call init_bloom_filters(). This initializes the commit-slab we use\nfor storing filter information. But we don't want to call it twice\n(without a matching deinit call in the middle), since it overwrites the\nexisting slab pointers, leaking the old values.\n\nUsually this init call is done lazily by parse_commit_graph() when we\nread a graph file that contains bloom data. But this can lead to some\noddities:\n\n  1. We may call parse_commit_graph() multiple times when we have a\n     split commit graph. I think this doesn't produce any user-visible\n     bug, because we parse all of the files back-to-back. So even though\n     we call init_bloom_filters() multiple times, we never look up any\n     commits in between, so the slab is always empty and initializing it\n     again happens to do nothing. This is a little sketchy to rely on,\n     though.\n\n  2. We call init_bloom_filters() directly in the \"test-tool bloom\"\n     helper so we can call get_or_compute_bloom_filter(). Normally this\n     is OK, as there is no bloom data in the on-disk graph file. But if\n     you build with SANITIZE=leak and run:\n\n       GIT_TEST_COMMIT_GRAPH=1 \\\n       GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=1 \\\n       ./t0095-bloom.sh\n\n     there's a leak that happens like this:\n\n       a. Our direct init_bloom_filters() sets up the slab.\n\n       b. In get_or_compute_bloom_filter() we look in the slab for a\n\t  cached entry. We won't find anything yet, but since we don't\n\t  use the read-only \"peek\" accessor (since we'll fill in the\n\t  entry if not present), this actually populates the slab with\n\t  an allocated chunk.\n\n       c. Now we look for an entry in the graph files. So we have to\n\t  load them and end up in parse_commit_graph(), which calls\n\t  init_bloom_filters() again. That trashes our existing slab\n\t  allocation, which is now leaked.\n\n  3. There's a similar case in write_commit_graph(), which calls\n     init_bloom_filters() before get_or_compute_bloom_filter(). I think\n     this code path is lucky to avoid the leak because it reads the\n     graph files first, then calls its init_bloom_filters(), and then\n     starts filling in entries. So even though it has the same overwrite\n     problem, we'd never actually allocate any slab entries between\n     overwrites.\n\nThe easiest solution here is just to make initialization of the slab\nidempotent using an extra flag.\n\nWe could actually get away without using the extra flag, for example by\nchecking whether bloom_filters.stride has been set. But it's probably\nbetter to avoid being too intimate with the commit-slab details.\nLikewise we don't actually need to re-initialize after a deinit call;\nthe slab-clearing function leaves things in a usable state. But it\nseemed less surprising to pair the init/deinit calls explicitly.\n\nI suspect this could all be cleaned up a bit more, but it's tricky. The\nonly function which uses the slab is get_or_compute_bloom_filter(), so\nit would be much simpler if it just lazy-initialized the slab itself.\nBut I think there is a subtle dependency here: we usually only\ninitialize the slab when we find a graph file that has bloom entries. So\nif we were to lose that signal, then even repos without on-disk bloom\ndata would start trying to populate the slab, wasting memory that will\nnever get entries filled in from the disk. So we'd need some other way\nof signaling \"it is worth considering bloom entries at all\".\n\nThis patch takes a smaller and more direct route to just dealing with\nthe potential leak issue.\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n bloom.c | 5 +++++\n 1 file changed, 5 insertions(+)\n\ndiff --git a/bloom.c b/bloom.c\nindex a805ac0c29..c98d1672ad 100644\n--- a/bloom.c\n+++ b/bloom.c\n@@ -16,6 +16,7 @@\n define_commit_slab(bloom_filter_slab, struct bloom_filter);\n \n static struct bloom_filter_slab bloom_filters;\n+static int bloom_filter_slab_initialized;\n \n struct pathmap_hash_entry {\n     struct hashmap_entry entry;\n@@ -263,7 +264,10 @@ void add_key_to_filter(const struct bloom_key *key,\n \n void init_bloom_filters(void)\n {\n+\tif (bloom_filter_slab_initialized)\n+\t\treturn;\n \tinit_bloom_filter_slab(&bloom_filters);\n+\tbloom_filter_slab_initialized = 1;\n }\n \n static void free_one_bloom_filter(struct bloom_filter *filter)\n@@ -276,6 +280,7 @@ static void free_one_bloom_filter(struct bloom_filter *filter)\n void deinit_bloom_filters(void)\n {\n \tdeep_clear_bloom_filter_slab(&bloom_filters, free_one_bloom_filter);\n+\tbloom_filter_slab_initialized = 0;\n }\n \n struct bloom_keyvec *bloom_keyvec_new(const char *path, size_t len,\n-- \n2.55.0.394.gcf1c5597d2\n\n"},{"id":"546822","messageId":"20260701064052.GB2580331@coredump.intra.peff.net","threadId":"65898","inReplyTo":"20260701063538.GA2579765@coredump.intra.peff.net","subject":"[PATCH 2/3] revision: avoid leaking bloom keyvecs with multiple traversals","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2026-07-01T06:40:52Z","receivedAt":"2026-07-01T06:40:53Z","isPatch":true,"body":"In prepare_revision_walk(), we convert the pruning pathspecs into\nbloom-filter \"keyvecs\" via prepare_to_use_bloom_filter(). This allocates\nmemory which is then freed eventually by release_revisions(), via\nrelease_revisions_bloom_keyvecs().\n\nBut there's one case where we leak. If a caller uses the same rev_info\nfor multiple walks, calling prepare_revision_walk() multiple times, then\nsubsequent calls will overwrite the earlier keyvecs, leaking them. This\ncan happen with \"git show foo bar\", which does a separate no-walk\ntraversal for \"foo\" and \"bar\". Building with SANITIZE=leak and running\nthe test suite like:\n\n  GIT_TEST_COMMIT_GRAPH=1 \\\n  GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=1 \\\n  ./t4013-diff-various.sh\n\nwill trigger a complaint from LSan. It does not happen without those\nextra flags because we don't store on-disk bloom filters by default, and\nthus we optimize out the keyvec computation.\n\nWe can fix the leak by discarding the old entries before generating new\nones.\n\nThere's an alternative fix, which is that prepare_to_use_bloom_filter()\ncould notice that we already have keyvec entries and just reuse them.\nBut this is less safe; the keyvec depends on the pruning pathspec, and\nwe don't know if that has changed.\n\nI think it would _probably_ work in practice, since any caller using a\nrev_info for multiple traversals is probably doing so with the same\npathspec. But it would also create a very subtle bug if that assumption\nis violated. So we'll do the safer thing here, and generate fresh keyvec\nentries for each traversal. The efficiency difference is probably not\nnoticeable, and this is what was happening already (we just weren't\nbothering to free the old ones!).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n revision.c | 2 ++\n 1 file changed, 2 insertions(+)\n\ndiff --git a/revision.c b/revision.c\nindex e91d7e1f11..0ef9d895f0 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -707,6 +707,8 @@ static int convert_pathspec_to_bloom_keyvec(struct bloom_keyvec **out,\n \n static void prepare_to_use_bloom_filter(struct rev_info *revs)\n {\n+\trelease_revisions_bloom_keyvecs(revs);\n+\n \tif (!revs->commits)\n \t\treturn;\n \n-- \n2.55.0.394.gcf1c5597d2\n\n"},{"id":"546823","messageId":"20260701064203.GC2580331@coredump.intra.peff.net","threadId":"65898","inReplyTo":"20260701063538.GA2579765@coredump.intra.peff.net","subject":"[PATCH 3/3] line-log: drop extra copy of range with bloom filters","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2026-07-01T06:42:03Z","receivedAt":"2026-07-01T06:42:04Z","isPatch":true,"body":"When line_log_process_ranges_arbitrary_commit() finds out from a Bloom\nfilter that a commit didn't touch the path in question, it can quickly\npass its range on to the parent commit.\n\nIt does so by making a copy of the range, and passing that copy to\nadd_line_range(). But add_line_range() already makes its own copy\n(either directly, or by merging with an existing range for that parent).\nSo the copy we make is leaked.\n\nWe can plug the leak by just passing our range directly, without the\nextra copy.\n\nThe bug goes back to f32dde8c12 (line-log: integrate with changed-path\nBloom filters, 2020-05-11). We didn't notice because the test suite\nnever explicitly combines these features! You can observe it by building\nwith SANITIZE=leak and running t4211 with some extra flags:\n\n  GIT_TEST_COMMIT_GRAPH=1 \\\n  GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=1 \\\n  ./t4211-line-log.sh\n\nIt would probably be useful to have some more targeted test coverage of\nthese features together. But I don't think there's much point in just\nblindly copying the existing tests and adding bloom-filter support. We\nalready do that via the linux-TEST-vars CI job. We just don't run the\nleak-checking build with those flags (so if there were a correctness\nproblem, we'd have noticed, just not a leak).\n\nSo I think we'd benefit from somebody clueful thinking about the\ninteraction of these features and testing the corner cases. But for the\npurposes of this leak fix, I think we can just rely on the recipe above\n(and consider running an extra leak-test job with more TEST-vars set).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\n line-log.c | 3 +--\n 1 file changed, 1 insertion(+), 2 deletions(-)\n\ndiff --git a/line-log.c b/line-log.c\nindex 5fc75ae275..0179f138f7 100644\n--- a/line-log.c\n+++ b/line-log.c\n@@ -1141,8 +1141,7 @@ int line_log_process_ranges_arbitrary_commit(struct rev_info *rev, struct commit\n \n \tif (range) {\n \t\tif (commit->parents && !bloom_filter_check(rev, commit, range)) {\n-\t\t\tstruct line_log_data *prange = line_log_data_copy(range);\n-\t\t\tadd_line_range(rev, commit->parents->item, prange);\n+\t\t\tadd_line_range(rev, commit->parents->item, range);\n \t\t\tclear_commit_line_range(rev, commit);\n \t\t} else if (commit->parents && commit->parents->next)\n \t\t\tchanged = process_ranges_merge_commit(rev, commit, range);\n-- \n2.55.0.394.gcf1c5597d2\n"},{"id":"546857","messageId":"akTPQ1IVqWy8WTk8@pks.im","threadId":"65898","inReplyTo":"20260701064052.GB2580331@coredump.intra.peff.net","subject":"Re: [PATCH 2/3] revision: avoid leaking bloom keyvecs with multiple traversals","fromName":"Patrick Steinhardt","fromEmail":"ps@pks.im","sentAt":"2026-07-01T08:26:43Z","receivedAt":"2026-07-01T08:26:54Z","isPatch":true,"body":"On Wed, Jul 01, 2026 at 02:40:52AM -0400, Jeff King wrote:\n[snip]\n> There's an alternative fix, which is that prepare_to_use_bloom_filter()\n> could notice that we already have keyvec entries and just reuse them.\n> But this is less safe; the keyvec depends on the pruning pathspec, and\n> we don't know if that has changed.\n\nRight. We could of course start to record the pruning pathspec so that\nwe're able to tell these cases apart, and if so we could reuse the bloom\nkeyvec entries safely. But as you mention...\n\n> I think it would _probably_ work in practice, since any caller using a\n> rev_info for multiple traversals is probably doing so with the same\n> pathspec. But it would also create a very subtle bug if that assumption\n> is violated. So we'll do the safer thing here, and generate fresh keyvec\n> entries for each traversal. The efficiency difference is probably not\n> noticeable, and this is what was happening already (we just weren't\n> bothering to free the old ones!).\n\n... we haven't been doing that beforehand, either, so it's fine to not\ncare about that for now and just plug the memory leak.\n\nPatrick\n"},{"id":"546903","messageId":"f7384ae3-bcd3-4191-9ff9-1ab86701c762@gmail.com","threadId":"65898","inReplyTo":"20260701063942.GA2580331@coredump.intra.peff.net","subject":"Re: [PATCH 1/3] bloom: make bloom-filter slab initialization idempotent","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2026-07-01T13:53:29Z","receivedAt":"2026-07-01T13:53:32Z","isPatch":true,"body":"On 7/1/2026 2:39 AM, Jeff King wrote:\n> Before using any of the commit-graph bloom-filter code, somebody needs\n> to call init_bloom_filters(). This initializes the commit-slab we use\n> for storing filter information. But we don't want to call it twice\n> (without a matching deinit call in the middle), since it overwrites the\n> existing slab pointers, leaking the old values.\n...\n\n> This patch takes a smaller and more direct route to just dealing with\n> the potential leak issue.\n\n> +static int bloom_filter_slab_initialized;\n\n>  void init_bloom_filters(void)\n>  {\n> +\tif (bloom_filter_slab_initialized)\n> +\t\treturn;\n>  \tinit_bloom_filter_slab(&bloom_filters);\n> +\tbloom_filter_slab_initialized = 1;\n>  }\n\n>  {\n>  \tdeep_clear_bloom_filter_slab(&bloom_filters, free_one_bloom_filter);\n> +\tbloom_filter_slab_initialized = 0;\n>  }\nThis patch looks like the right fix.\n\nThanks,\n-Stolee\n"},{"id":"546904","messageId":"1b261cfd-c9cb-44bd-a3a1-e653b2cd34ad@gmail.com","threadId":"65898","inReplyTo":"20260701064052.GB2580331@coredump.intra.peff.net","subject":"Re: [PATCH 2/3] revision: avoid leaking bloom keyvecs with multiple traversals","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2026-07-01T14:21:32Z","receivedAt":"2026-07-01T14:21:36Z","isPatch":true,"body":"On 7/1/2026 2:40 AM, Jeff King wrote:\n> In prepare_revision_walk(), we convert the pruning pathspecs into\n> bloom-filter \"keyvecs\" via prepare_to_use_bloom_filter(). This allocates\n> memory which is then freed eventually by release_revisions(), via\n> release_revisions_bloom_keyvecs().\n\n>  static void prepare_to_use_bloom_filter(struct rev_info *revs)\n>  {\n> +\trelease_revisions_bloom_keyvecs(revs);\n> +\nI continue to support the obviously-correct and simple solution to\nthese leaks.\n\nThanks,\n-Stolee\n\n"},{"id":"546905","messageId":"b641aed4-ad52-477b-b1d8-9d8e470be46f@gmail.com","threadId":"65898","inReplyTo":"20260701063538.GA2579765@coredump.intra.peff.net","subject":"Re: [PATCH 0/3] bloom-related leak fixes","fromName":"Derrick Stolee","fromEmail":"stolee@gmail.com","sentAt":"2026-07-01T14:32:39Z","receivedAt":"2026-07-01T14:32:41Z","isPatch":true,"body":"On 7/1/2026 2:35 AM, Jeff King wrote:\n> Here are a few small leak fixes that only show up when you run the test\n> suite with GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=1.\n> \n> Combined with the commit-graph leak-fix here:\n> \n>   https://lore.kernel.org/git/20260630064301.GB3733961@coredump.intra.peff.net/\n> \n> and Kaartic's pending fix from this thread:\n> \n>   https://lore.kernel.org/git/20260614141600.620272-1-kaartic.sivaraam@gmail.com/\n> \n> This will fix most of the leaks we'd see if we ran linux-TEST-vars jobs\n> with leak-checking. There are a few more related to building with\n> openssl for sha1, but I'll tackle those separately.\nThanks for fixing these leaks in the simplest way possible in\neach scenario.\n\nThanks,\n-Stolee\n\n"},{"id":"546909","messageId":"xmqqqzlmpv3b.fsf@gitster.g","threadId":"65898","inReplyTo":"20260701063942.GA2580331@coredump.intra.peff.net","subject":"Re: [PATCH 1/3] bloom: make bloom-filter slab initialization idempotent","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-07-01T15:50:48Z","receivedAt":"2026-07-01T15:50:51Z","isPatch":true,"body":"Jeff King <peff@peff.net> writes:\n\n> Before using any of the commit-graph bloom-filter code, somebody needs\n> to call init_bloom_filters(). This initializes the commit-slab we use\n> for storing filter information. But we don't want to call it twice\n> (without a matching deinit call in the middle), since it overwrites the\n> existing slab pointers, leaking the old values.\n>\n> Usually this init call is done lazily by parse_commit_graph() when we\n> read a graph file that contains bloom data. But this can lead to some\n> oddities:\n>\n>   1. We may call parse_commit_graph() multiple times when we have a\n>      split commit graph. I think this doesn't produce any user-visible\n>      bug, because we parse all of the files back-to-back. So even though\n>      we call init_bloom_filters() multiple times, we never look up any\n>      commits in between, so the slab is always empty and initializing it\n>      again happens to do nothing. This is a little sketchy to rely on,\n>      though.\n\nYeah, that sounds like an accident waiting to happen.\n\n>\n>   2. We call init_bloom_filters() directly in the \"test-tool bloom\"\n>      helper so we can call get_or_compute_bloom_filter(). Normally this\n>      is OK, as there is no bloom data in the on-disk graph file. But if\n>      you build with SANITIZE=leak and run:\n>\n>        GIT_TEST_COMMIT_GRAPH=1 \\\n>        GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=1 \\\n>        ./t0095-bloom.sh\n>\n>      there's a leak that happens like this:\n>\n>        a. Our direct init_bloom_filters() sets up the slab.\n>\n>        b. In get_or_compute_bloom_filter() we look in the slab for a\n> \t  cached entry. We won't find anything yet, but since we don't\n> \t  use the read-only \"peek\" accessor (since we'll fill in the\n> \t  entry if not present), this actually populates the slab with\n> \t  an allocated chunk.\n>\n>        c. Now we look for an entry in the graph files. So we have to\n> \t  load them and end up in parse_commit_graph(), which calls\n> \t  init_bloom_filters() again. That trashes our existing slab\n> \t  allocation, which is now leaked.\n\nBesides, if the test-tool initializes explicitly and the production\ncode does not and relies on lazy initialization, we are not testing\nthe production setting, which may hide bugs in lazy initialization.\n\n>   3. There's a similar case in write_commit_graph(), which calls\n>      init_bloom_filters() before get_or_compute_bloom_filter(). I think\n>      this code path is lucky to avoid the leak because it reads the\n>      graph files first, then calls its init_bloom_filters(), and then\n>      starts filling in entries. So even though it has the same overwrite\n>      problem, we'd never actually allocate any slab entries between\n>      overwrites.\n>\n> The easiest solution here is just to make initialization of the slab\n> idempotent using an extra flag.\n>\n> We could actually get away without using the extra flag, for example by\n> checking whether bloom_filters.stride has been set. But it's probably\n> better to avoid being too intimate with the commit-slab details.\n\n\"bool bloom_filter_slab_initialied()\" that is generated by including\ncommit-slab-impl.h can be as intimate with the implementation as we\nwant, though ;-)\n\n> Likewise we don't actually need to re-initialize after a deinit call;\n> the slab-clearing function leaves things in a usable state. But it\n> seemed less surprising to pair the init/deinit calls explicitly.\n\nGood.\n\n> This patch takes a smaller and more direct route to just dealing with\n> the potential leak issue.\n>\n> Signed-off-by: Jeff King <peff@peff.net>\n> ---\n>  bloom.c | 5 +++++\n>  1 file changed, 5 insertions(+)\n\nLooks trivially correct.\n\n> diff --git a/bloom.c b/bloom.c\n> index a805ac0c29..c98d1672ad 100644\n> --- a/bloom.c\n> +++ b/bloom.c\n> @@ -16,6 +16,7 @@\n>  define_commit_slab(bloom_filter_slab, struct bloom_filter);\n>  \n>  static struct bloom_filter_slab bloom_filters;\n> +static int bloom_filter_slab_initialized;\n>  \n>  struct pathmap_hash_entry {\n>      struct hashmap_entry entry;\n> @@ -263,7 +264,10 @@ void add_key_to_filter(const struct bloom_key *key,\n>  \n>  void init_bloom_filters(void)\n>  {\n> +\tif (bloom_filter_slab_initialized)\n> +\t\treturn;\n>  \tinit_bloom_filter_slab(&bloom_filters);\n> +\tbloom_filter_slab_initialized = 1;\n>  }\n>  \n>  static void free_one_bloom_filter(struct bloom_filter *filter)\n> @@ -276,6 +280,7 @@ static void free_one_bloom_filter(struct bloom_filter *filter)\n>  void deinit_bloom_filters(void)\n>  {\n>  \tdeep_clear_bloom_filter_slab(&bloom_filters, free_one_bloom_filter);\n> +\tbloom_filter_slab_initialized = 0;\n>  }\n>  \n>  struct bloom_keyvec *bloom_keyvec_new(const char *path, size_t len,\n"},{"id":"546910","messageId":"xmqqmrwapuzz.fsf@gitster.g","threadId":"65898","inReplyTo":"20260701064052.GB2580331@coredump.intra.peff.net","subject":"Re: [PATCH 2/3] revision: avoid leaking bloom keyvecs with multiple traversals","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-07-01T15:52:48Z","receivedAt":"2026-07-01T15:52:51Z","isPatch":true,"body":"Jeff King <peff@peff.net> writes:\n\n> I think it would _probably_ work in practice, since any caller using a\n> rev_info for multiple traversals is probably doing so with the same\n> pathspec. But it would also create a very subtle bug if that assumption\n> is violated. So we'll do the safer thing here, and generate fresh keyvec\n> entries for each traversal. The efficiency difference is probably not\n> noticeable, and this is what was happening already (we just weren't\n> bothering to free the old ones!).\n\nGood to see the thinking behind the design recorded so clearly in the log\nmessage.  That thinking being on the more conservative side is a big plus.\n\n\n\n> Signed-off-by: Jeff King <peff@peff.net>\n> ---\n>  revision.c | 2 ++\n>  1 file changed, 2 insertions(+)\n>\n> diff --git a/revision.c b/revision.c\n> index e91d7e1f11..0ef9d895f0 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -707,6 +707,8 @@ static int convert_pathspec_to_bloom_keyvec(struct bloom_keyvec **out,\n>  \n>  static void prepare_to_use_bloom_filter(struct rev_info *revs)\n>  {\n> +\trelease_revisions_bloom_keyvecs(revs);\n> +\n>  \tif (!revs->commits)\n>  \t\treturn;\n"},{"id":"546927","messageId":"xmqqo6gqobrt.fsf@gitster.g","threadId":"65898","inReplyTo":"b641aed4-ad52-477b-b1d8-9d8e470be46f@gmail.com","subject":"Re: [PATCH 0/3] bloom-related leak fixes","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2026-07-01T17:33:26Z","receivedAt":"2026-07-01T17:33:28Z","isPatch":true,"body":"Derrick Stolee <stolee@gmail.com> writes:\n\n> On 7/1/2026 2:35 AM, Jeff King wrote:\n>> Here are a few small leak fixes that only show up when you run the test\n>> suite with GIT_TEST_COMMIT_GRAPH_CHANGED_PATHS=1.\n>> \n>> Combined with the commit-graph leak-fix here:\n>> \n>>   https://lore.kernel.org/git/20260630064301.GB3733961@coredump.intra.peff.net/\n>> \n>> and Kaartic's pending fix from this thread:\n>> \n>>   https://lore.kernel.org/git/20260614141600.620272-1-kaartic.sivaraam@gmail.com/\n>> \n>> This will fix most of the leaks we'd see if we ran linux-TEST-vars jobs\n>> with leak-checking. There are a few more related to building with\n>> openssl for sha1, but I'll tackle those separately.\n> Thanks for fixing these leaks in the simplest way possible in\n> each scenario.\n\nYup, these were delight to read.\n"}]}