{"thread":{"id":"57525","subject":"[PATCH 00/24] revision.[ch]: add and use release_revisions()","startedAt":"2022-03-09T13:17:49Z","lastAt":"2022-07-12T07:04:14Z","messageCount":252,"participants":["Ævar Arnfjörð Bjarmason","Derrick Stolee","Junio C Hamano","Taylor Blau","Phillip Wood","Jeff King"],"isPatch":true,"patchVersion":1,"patchTotal":24},"messages":[{"id":"450839","messageId":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":null,"subject":"[PATCH 00/24] revision.[ch]: add and use release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:30Z","receivedAt":"2022-03-09T13:17:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"== For Junio ==\n\nThis has a trivially resolved conflict with Derrick Stolee's\naaf251cdc5c (revision: put object filter into struct rev_info,\n2022-03-08) currently in \"seen\" in builtin/rev-list.c.\n\nThe resolution is to just keep the \"goto cleanup\" in place of \"return\n0\" in the conflicting lines, but to otherwise keep Derrick's version.\n\nIt will pass with/without SANITIZE=leak when applied to both \"master\"\nand \"seen\". I omitted one test change (described in a relevant commit\nmessage) due to the latter not being true (no fault of \"seen\", just a\nnew leaking command being added to a test).\n\n== Summary ==\n\nEver since we started running regular leak testing in CI with\n956d2e4639b (tests: add a test mode for SANITIZE=leak, run it in CI,\n2021-09-23) a major roadblock for making headway in marking tests as\nleak-free has been the leaks in the revision.[ch] API.\n\nSince we use it for an revision walking, including \"log\", \"rev-list\"\netc. we haven't been able to run common test setup in many cases\nwithout dying when compiled with SANITIZE=leak, and cases where we\ngenuinely leak memory are often obscured by several concurrent leaks\nin the revisions API.\n\nThis series adds a release_revisions() and works towards fixing those\nmemory leaks one at a time. As the diffstat to t/* shows we can mark a\n*lot* of tests leak-free as a result, and those are only the the tests\nthat 100% pass with this series.\n\nAn approximate count of the total number of memory leaks in the test\nsuite (approximate because some are \"hidden\" due to SANITIZE=leak\ncausing \"git\" to die) is, before this series (running with \"--tee\" and\n\"--verbose-log\"):\n\n    $ grep -a \"SUMMARY: LeakSanitizer.*leaked in .*allocation\" test-results/*.out|wc -l\n    12032\n\nAfter:\n\n    $ grep -a \"SUMMARY: LeakSanitizer.*leaked in .*allocation\" test-results/*.out|wc -l\n    6652\n\nI.e. this recudes the number of times \"git\" dies due to having any of\nN number of leaks by around half, and that's severely undercounting\nthe amount of individual memory leaks.\n\nThis is a rather large series, but it should be fairly easy to\nreview. I've leaned towards splitting commits up for ease of review,\nsuch that when there's repetitive changes (e.g. additions of\nrelease_revisions()) that they're split such that each commit has\nhunks that make the same sort of change, whenever possible.\n\nAll of the changes to \"t/*\" are then just adding\n\"TEST_PASSES_SANITIZE_LEAK=true\" lines as we go along.\n\nThis doesn't fix all of the leaks in the revision.[ch] API. In\nparticular there's harder edge cases where the ownership of a struct\nmember isn't very straightforward (sometimes revision.c will allocate\nit, sometimes not). This does fix all of the common cases though, and\nwhat's left is best suited for a follow-up series.\n\nÆvar Arnfjörð Bjarmason (24):\n  t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"\n  blame: use \"goto cleanup\" for cleanup_scoreboard()\n  format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"\n  revision.[ch]: split freeing of revs->commit into a function\n  revision.[ch]: provide and start using a release_revisions()\n  revisions API users: add straightforward release_revisions()\n  revisions API users: use release_revisions() needing \"{ 0 }\" init\n  revisions API users: add \"goto cleanup\" for release_revisions()\n  revisions API users: use release_revisions() in http-push.c\n  revisions API users: use release_revisions() in builtin/log.c\n  revisions API users: use release_revisions() with UNLEAK()\n  revisions API users: use release_revisions() in submodule.c edge case\n  revisions API users: use release_revisions() in wt-status.c edge case\n  revisions API users: use release_revisions() for \"prune_data\" users\n  revisions API: have release_revisions() release \"commits\"\n  revisions API: have release_revisions() release \"mailmap\"\n  revisions API: have release_revisions() release \"cmdline\"\n  revisions API: have release_revisions() release \"grep_filter\"\n  revisions API: have release_revisions() release \"prune_data\"\n  revisions API: clear \"boundary_commits\" in release_revisions()\n  revisions API: release \"reflog_info\" in release revisions()\n  revisions API: call diff_free(&revs->pruning) in revisions_release()\n  revisions API: have release_revisions() release \"date_mode\"\n  revisions API: have release_revisions() release \"topo_walk_info\"\n\n add-interactive.c                             |  3 +-\n bisect.c                                      | 20 ++++--\n builtin/add.c                                 |  3 +-\n builtin/am.c                                  |  3 +\n builtin/bisect--helper.c                      |  2 +\n builtin/blame.c                               |  4 +-\n builtin/checkout.c                            |  3 +-\n builtin/commit.c                              |  7 +-\n builtin/describe.c                            |  2 +\n builtin/diff-files.c                          |  8 ++-\n builtin/diff-index.c                          |  5 +-\n builtin/diff.c                                |  2 +-\n builtin/fast-export.c                         |  1 +\n builtin/log.c                                 | 35 +++++++---\n builtin/merge.c                               |  2 +\n builtin/pack-objects.c                        |  2 +\n builtin/prune.c                               |  1 +\n builtin/reflog.c                              |  1 +\n builtin/rev-list.c                            | 19 +++--\n builtin/shortlog.c                            |  2 +\n builtin/stash.c                               | 20 +++---\n builtin/submodule--helper.c                   | 15 ++--\n bundle.c                                      | 12 ++--\n diff-lib.c                                    |  8 ++-\n fmt-merge-msg.c                               |  1 +\n http-push.c                                   |  3 +-\n merge-ort.c                                   |  1 +\n merge-recursive.c                             |  1 +\n midx.c                                        |  1 +\n pack-bitmap-write.c                           |  1 +\n range-diff.c                                  |  2 +-\n ref-filter.c                                  |  1 +\n reflog-walk.c                                 | 26 ++++++-\n reflog-walk.h                                 |  1 +\n remote.c                                      |  1 +\n revision.c                                    | 69 ++++++++++++++++---\n revision.h                                    |  6 ++\n sequencer.c                                   | 26 +++++--\n shallow.c                                     |  1 +\n submodule.c                                   |  5 +-\n t/helper/test-fast-rebase.c                   | 22 ++++--\n t/helper/test-revision-walking.c              |  1 +\n t/lib-git-svn.sh                              |  4 ++\n t/t0056-git-C.sh                              |  1 +\n t/t0062-revision-walking.sh                   |  1 +\n t/t0100-previous.sh                           |  1 +\n t/t0101-at-syntax.sh                          |  2 +\n t/t1060-object-corruption.sh                  |  2 +\n t/t1401-symbolic-ref.sh                       |  2 +\n t/t1411-reflog-show.sh                        |  1 +\n t/t1412-reflog-loop.sh                        |  2 +\n t/t1415-worktree-refs.sh                      |  1 +\n t/t2015-checkout-unborn.sh                    |  1 +\n t/t2016-checkout-patch.sh                     |  1 +\n t/t3408-rebase-multi-line.sh                  |  1 +\n t/t4021-format-patch-numbered.sh              |  1 +\n t/t4028-format-patch-mime-headers.sh          |  2 +\n t/t4036-format-patch-signer-mime.sh           |  1 +\n t/t4039-diff-assume-unchanged.sh              |  1 +\n t/t4055-diff-context.sh                       |  1 +\n t/t4066-diff-emit-delay.sh                    |  1 +\n t/t4122-apply-symlink-inside.sh               |  1 +\n t/t4126-apply-empty.sh                        |  1 -\n t/t4206-log-follow-harder-copies.sh           |  2 +\n t/t4207-log-decoration-colors.sh              |  1 +\n t/t4212-log-corrupt.sh                        |  1 +\n t/t5313-pack-bounds-checks.sh                 |  2 +\n t/t5322-pack-objects-sparse.sh                |  1 +\n t/t5506-remote-groups.sh                      |  1 +\n t/t5513-fetch-track.sh                        |  1 +\n t/t5515-fetch-merge-logic.sh                  |  1 +\n t/t5518-fetch-exit-status.sh                  |  1 +\n t/t5532-fetch-proxy.sh                        |  2 +\n t/t5900-repo-selection.sh                     |  2 +\n t/t6002-rev-list-bisect.sh                    |  1 +\n t/t6003-rev-list-topo-order.sh                |  1 +\n t/t6018-rev-list-glob.sh                      |  1 +\n t/t6019-rev-list-ancestry-path.sh             |  1 +\n t/t6100-rev-list-in-order.sh                  |  1 +\n t/t6101-rev-parse-parents.sh                  |  1 +\n t/t6110-rev-list-sparse.sh                    |  1 +\n t/t6114-keep-packs.sh                         |  2 +\n t/t6131-pathspec-icase.sh                     |  2 +\n t/t7008-filter-branch-null-sha1.sh            |  2 +\n t/t7702-repack-cyclic-alternate.sh            |  2 +\n t/t9001-send-email.sh                         |  1 +\n t/t9100-git-svn-basic.sh                      |  1 +\n t/t9101-git-svn-props.sh                      |  2 +\n t/t9104-git-svn-follow-parent.sh              |  2 +\n t/t9106-git-svn-commit-diff-clobber.sh        |  2 +\n t/t9122-git-svn-author.sh                     |  2 +\n t/t9129-git-svn-i18n-commitencoding.sh        |  1 +\n t/t9139-git-svn-non-utf8-commitencoding.sh    |  1 +\n t/t9148-git-svn-propset.sh                    |  1 +\n t/t9151-svn-mergeinfo.sh                      |  1 +\n t/t9157-git-svn-fetch-merge.sh                |  2 +\n t/t9159-git-svn-no-parent-mergeinfo.sh        |  2 +\n t/t9161-git-svn-mergeinfo-push.sh             |  1 +\n t/t9162-git-svn-dcommit-interactive.sh        |  2 +\n t/t9163-git-svn-reset-clears-caches.sh        |  1 +\n ...65-git-svn-fetch-merge-branch-of-branch.sh |  2 +\n ...6-git-svn-fetch-merge-branch-of-branch2.sh |  2 +\n wt-status.c                                   | 15 ++--\n 103 files changed, 359 insertions(+), 90 deletions(-)\n\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450840","messageId":"patch-01.24-017c67c7540-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 01/24] t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:31Z","receivedAt":"2022-03-09T13:17:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak that's been with us since f9500261e0a (fast-rebase:\nwrite conflict state to working tree, index, and HEAD, 2021-05-20)\nchanged this code to move these strbuf_release() into an if/else\nblock.\n\nWe'll also add to \"reflog_msg\" in the \"else\" arm of the \"if\" block\nbeing modified here, and we'll append to \"branch_msg\" in both\ncases. But after f9500261e0a only the \"if\" block would free these two\n\"struct strbuf\".\n\nThis change is needed so that a subsequent addition of a \"goto\ncleanup\" pattern when adding release_revisions() doesn't solve this\nunrelated bug while it's at it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/helper/test-fast-rebase.c | 5 +++--\n 1 file changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c\nindex fc2d4609043..993b90eaedd 100644\n--- a/t/helper/test-fast-rebase.c\n+++ b/t/helper/test-fast-rebase.c\n@@ -201,8 +201,6 @@ int cmd__fast_rebase(int argc, const char **argv)\n \t\t}\n \t\tif (create_symref(\"HEAD\", branch_name.buf, reflog_msg.buf) < 0)\n \t\t\tdie(_(\"unable to update HEAD\"));\n-\t\tstrbuf_release(&reflog_msg);\n-\t\tstrbuf_release(&branch_name);\n \n \t\tprime_cache_tree(the_repository, the_repository->index,\n \t\t\t\t result.tree);\n@@ -221,5 +219,8 @@ int cmd__fast_rebase(int argc, const char **argv)\n \tif (write_locked_index(&the_index, &lock,\n \t\t\t       COMMIT_LOCK | SKIP_IF_UNCHANGED))\n \t\tdie(_(\"unable to write %s\"), get_index_file());\n+\n+\tstrbuf_release(&reflog_msg);\n+\tstrbuf_release(&branch_name);\n \treturn (result.clean == 0);\n }\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450841","messageId":"patch-02.24-db0b04e53a9-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 02/24] blame: use \"goto cleanup\" for cleanup_scoreboard()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:32Z","receivedAt":"2022-03-09T13:17:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Amend a freeing pattern added in 0906ac2b54b (blame: use changed-path\nBloom filters, 2020-04-16) to use a \"goto cleanup\", so that we can be\nsure that we call cleanup_scoreboard().\n\nThis change is needed so that a subsequent addition of a\nrelease_revisions() doesn't solve this unrelated issue while it's at\nit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/blame.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 8d15b68afc9..885b381ab83 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1167,7 +1167,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tif (!incremental)\n \t\tsetup_pager();\n \telse\n-\t\treturn 0;\n+\t\tgoto cleanup;\n \n \tblame_sort_final(&sb);\n \n@@ -1201,6 +1201,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\tprintf(\"num commits: %d\\n\", sb.num_commits);\n \t}\n \n+cleanup:\n \tcleanup_scoreboard(&sb);\n \treturn 0;\n }\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450842","messageId":"patch-03.24-ab1afe6c0c8-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 03/24] format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:33Z","receivedAt":"2022-03-09T13:17:55Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix two memory leaks in \"struct rev_info\" by freeing that memory in\ncmd_format_patch(). These two are unusual special-cases in being in\nthe \"struct rev_info\", but not being \"owned\" by the code in\nrevision.c. I.e. they're members of the struct so that this code in\n\"builtin/log.c\" can pass information code in log-tree.c.\n\nSee 20ff06805c6 (format-patch: resurrect extra headers from config,\n2006-06-02) and d1566f7883f (git-format-patch: Make the second and\nsubsequent mails replies to the first, 2006-07-14) for the initial\nintroduction of \"extra_headers\" and \"ref_message_ids\".\n\nWe can count on repo_init_revisions() memset()-ing this data to 0\nhowever, so we can count on it being either NULL or something we\nallocated. In the case of \"extra_headers\" let's add a local \"char *\"\nvariable to hold it, to avoid the eventual cast from \"const char *\"\nwhen we free() it.\n\nWhile we're at it let's also move to using string_list_init_nodup()\ninstead of relying on calloc() implicitly coming up with the same\nresult. See 770fedaf9fb (string-list.[ch]: add a\nstring_list_init_{nodup,dup}(), 2021-07-01) for the introduction of\nthe string_list_init_nodup() helper. The string_list_clear() here is\nredundant due to the *_nodup() initialization, but let's add it anyway\nfor consistency with other API use.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/log.c | 15 ++++++++++++---\n 1 file changed, 12 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex c211d66d1d0..00846c2c8ac 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1746,6 +1746,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tstruct commit *commit;\n \tstruct commit **list = NULL;\n \tstruct rev_info rev;\n+\tchar *extra_headers = NULL;\n \tstruct setup_revision_opt s_r_opt;\n \tint nr = 0, total, i;\n \tint use_stdout = 0;\n@@ -1946,7 +1947,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tstrbuf_addch(&buf, '\\n');\n \t}\n \n-\trev.extra_headers = strbuf_detach(&buf, NULL);\n+\textra_headers = strbuf_detach(&buf, NULL);\n+\trev.extra_headers = extra_headers;\n \n \tif (from) {\n \t\tif (split_ident_line(&rev.from_ident, from, strlen(from)))\n@@ -2173,8 +2175,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tprepare_bases(&bases, base, list, nr);\n \t}\n \n-\tif (in_reply_to || thread || cover_letter)\n-\t\trev.ref_message_ids = xcalloc(1, sizeof(struct string_list));\n+\tif (in_reply_to || thread || cover_letter) {\n+\t\trev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));\n+\t\tstring_list_init_nodup(rev.ref_message_ids);\n+\t}\n \tif (in_reply_to) {\n \t\tconst char *msgid = clean_message_id(in_reply_to);\n \t\tstring_list_append(rev.ref_message_ids, msgid);\n@@ -2281,6 +2285,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tstrbuf_release(&rdiff1);\n \tstrbuf_release(&rdiff2);\n \tstrbuf_release(&rdiff_title);\n+\tfree(extra_headers);\n+\tif (rev.ref_message_ids) {\n+\t\tstring_list_clear(rev.ref_message_ids, 0);\n+\t\tfree(rev.ref_message_ids);\n+\t}\n \tUNLEAK(rev);\n \treturn 0;\n }\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450843","messageId":"patch-04.24-763e0ab3425-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 04/24] revision.[ch]: split freeing of revs->commit into a function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:34Z","receivedAt":"2022-03-09T13:17:59Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Move the existing code for invoking free_commit_list() and setting\nrevs->commits to NULL into a new release_revisions_commit_list()\nfunction. This will be used as part of a general free()-ing mechanism\nfor \"struct rev_info\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 15 +++++++++++----\n 1 file changed, 11 insertions(+), 4 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex 2bb913f2f3f..5824fdeddfd 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2916,6 +2916,16 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \treturn left;\n }\n \n+static void release_revisions_commit_list(struct rev_info *revs)\n+{\n+\tstruct commit_list *commits = revs->commits;\n+\n+\tif (!commits)\n+\t\treturn;\n+\tfree_commit_list(commits);\n+\trevs->commits = NULL;\n+}\n+\n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n {\n \tstruct commit_list *l = xcalloc(1, sizeof(*l));\n@@ -4073,10 +4083,7 @@ static void create_boundary_commit_list(struct rev_info *revs)\n \t * boundary commits anyway.  (This is what the code has always\n \t * done.)\n \t */\n-\tif (revs->commits) {\n-\t\tfree_commit_list(revs->commits);\n-\t\trevs->commits = NULL;\n-\t}\n+\trelease_revisions_commit_list(revs);\n \n \t/*\n \t * Put all of the actual boundary commits from revs->boundary_commits\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450844","messageId":"patch-05.24-b89dcadcc22-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 05/24] revision.[ch]: provide and start using a release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:35Z","receivedAt":"2022-03-09T13:18:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The users of the revision.[ch] API's \"struct rev_info\" are a major\nsource of memory leaks in the test suite under SANITIZE=leak, which in\nturn adds a lot of noise when trying to mark up tests with\n\"TEST_PASSES_SANITIZE_LEAK=true\".\n\nThe users of that API are largely one-shot, e.g. \"git log\", so\narguably freeing the memory is a waste of time, but in many cases\nthey've actually been trying to free the memory, and just doing that\nin a buggy manner.\n\nLet's provide a release_revisions() function for these users, and\nstart migrating them over per the plan outlined in [1]. Right now this\nonly handles the \"pending\" member of the struct, but more will be\nadded in subsequent commits.\n\nEven though we only clear the \"pending\" member now, let's not leave a\ntrap in code like the pre-image of index_differs_from(), where we'd\nstart doing the wrong thing as soon as the release_revisions() learned\nto clear its \"diffopt\". I.e. we need to call release_revisions() after\nwe've inspected any state in \"struct rev_info\".\n\n1. https://lore.kernel.org/git/87a6k8daeu.fsf@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/checkout.c | 2 +-\n builtin/stash.c    | 3 +--\n diff-lib.c         | 6 ++++--\n range-diff.c       | 2 +-\n revision.c         | 7 +++++++\n revision.h         | 6 ++++++\n wt-status.c        | 7 ++++---\n 7 files changed, 24 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 9244827ca02..ed993383531 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -629,7 +629,7 @@ static void show_local_changes(struct object *head,\n \tdiff_setup_done(&rev.diffopt);\n \tadd_pending_object(&rev, head, NULL);\n \trun_diff_index(&rev, 0);\n-\tobject_array_clear(&rev.pending);\n+\trelease_revisions(&rev);\n }\n \n static void describe_detached_head(const char *msg, struct commit *commit)\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 3e8af210fde..af9b61ef17a 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1277,9 +1277,8 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps\n \n done:\n \tdiscard_index(&istate);\n-\tUNLEAK(rev);\n-\tobject_array_clear(&rev.pending);\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n \tstrbuf_release(&diff_output);\n \tremove_path(stash_index_path.buf);\n \treturn ret;\ndiff --git a/diff-lib.c b/diff-lib.c\nindex ca085a03efc..ad1dd4834f7 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -651,6 +651,7 @@ int index_differs_from(struct repository *r,\n {\n \tstruct rev_info rev;\n \tstruct setup_revision_opt opt;\n+\tunsigned int has_changes;\n \n \trepo_init_revisions(r, &rev, NULL);\n \tmemset(&opt, 0, sizeof(opt));\n@@ -662,8 +663,9 @@ int index_differs_from(struct repository *r,\n \t\tdiff_flags_or(&rev.diffopt.flags, flags);\n \trev.diffopt.ita_invisible_in_index = ita_invisible_in_index;\n \trun_diff_index(&rev, 1);\n-\tobject_array_clear(&rev.pending);\n-\treturn (rev.diffopt.flags.has_changes != 0);\n+\thas_changes = rev.diffopt.flags.has_changes;\n+\trelease_revisions(&rev);\n+\treturn (has_changes != 0);\n }\n \n static struct strbuf *idiff_prefix_cb(struct diff_options *opt, void *data)\ndiff --git a/range-diff.c b/range-diff.c\nindex 30a4de5c2d8..2d0cd271a9e 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -600,6 +600,6 @@ int is_range_diff_range(const char *arg)\n \t}\n \n \tfree(copy);\n-\tobject_array_clear(&revs.pending);\n+\trelease_revisions(&revs);\n \treturn negative > 0 && positive > 0;\n }\ndiff --git a/revision.c b/revision.c\nindex 5824fdeddfd..831f2cf977b 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2926,6 +2926,13 @@ static void release_revisions_commit_list(struct rev_info *revs)\n \trevs->commits = NULL;\n }\n \n+void release_revisions(struct rev_info *revs)\n+{\n+\tif (!revs)\n+\t\treturn;\n+\tobject_array_clear(&revs->pending);\n+}\n+\n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n {\n \tstruct commit_list *l = xcalloc(1, sizeof(*l));\ndiff --git a/revision.h b/revision.h\nindex b9c2421687f..c28e901021d 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -370,6 +370,12 @@ void repo_init_revisions(struct repository *r,\n int setup_revisions(int argc, const char **argv, struct rev_info *revs,\n \t\t    struct setup_revision_opt *);\n \n+/**\n+ * Free data allocated in a \"struct rev_info\" after it's been setup\n+ * with repo_init_revisions() and setup_revisions().\n+ */\n+void release_revisions(struct rev_info *revs);\n+\n void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,\n \t\t\tconst struct option *options,\n \t\t\tconst char * const usagestr[]);\ndiff --git a/wt-status.c b/wt-status.c\nindex 335e723a71e..398d0951aca 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -653,7 +653,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)\n \trev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;\n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_index(&rev, 1);\n-\tobject_array_clear(&rev.pending);\n+\trelease_revisions(&rev);\n \tclear_pathspec(&rev.prune_data);\n }\n \n@@ -2568,8 +2568,9 @@ int has_uncommitted_changes(struct repository *r,\n \n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_index(&rev_info, 1);\n-\tobject_array_clear(&rev_info.pending);\n-\treturn diff_result_code(&rev_info.diffopt, result);\n+\tresult = diff_result_code(&rev_info.diffopt, result);\n+\trelease_revisions(&rev_info);\n+\treturn result;\n }\n \n /**\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450845","messageId":"patch-06.24-b5d55539b6d-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 06/24] revisions API users: add straightforward release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:36Z","receivedAt":"2022-03-09T13:18:03Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a release_revisions() to various users of \"struct rev_list\" in\nthose straightforward cases where we only need to add the\nrelease_revisions() call to the end of a block, and don't need to\ne.g. refactor anything to use a \"goto cleanup\" pattern.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n add-interactive.c                | 1 +\n bisect.c                         | 2 ++\n builtin/add.c                    | 1 +\n builtin/am.c                     | 3 +++\n builtin/bisect--helper.c         | 2 ++\n builtin/blame.c                  | 1 +\n builtin/checkout.c               | 1 +\n builtin/commit.c                 | 1 +\n builtin/describe.c               | 2 ++\n builtin/fast-export.c            | 1 +\n builtin/merge.c                  | 2 ++\n builtin/pack-objects.c           | 2 ++\n builtin/prune.c                  | 1 +\n builtin/reflog.c                 | 1 +\n builtin/shortlog.c               | 2 ++\n builtin/submodule--helper.c      | 1 +\n fmt-merge-msg.c                  | 1 +\n merge-ort.c                      | 1 +\n merge-recursive.c                | 1 +\n midx.c                           | 1 +\n pack-bitmap-write.c              | 1 +\n ref-filter.c                     | 1 +\n remote.c                         | 1 +\n sequencer.c                      | 3 +++\n shallow.c                        | 1 +\n submodule.c                      | 2 ++\n t/helper/test-revision-walking.c | 1 +\n wt-status.c                      | 1 +\n 28 files changed, 39 insertions(+)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex e1ab39cce30..13d5ec6f49f 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -568,6 +568,7 @@ static int get_modified_files(struct repository *r,\n \n \t\tif (ps)\n \t\t\tclear_pathspec(&rev.prune_data);\n+\t\trelease_revisions(&rev);\n \t}\n \thashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);\n \tif (unmerged_count)\ndiff --git a/bisect.c b/bisect.c\nindex 9e6a2b7f201..cc6b8b6230d 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -884,6 +884,7 @@ static int check_ancestors(struct repository *r, int rev_nr,\n \t/* Clean up objects used, as they will be reused. */\n \tclear_commit_marks_many(rev_nr, rev, ALL_REV_FLAGS);\n \n+\trelease_revisions(&revs);\n \treturn res;\n }\n \n@@ -964,6 +965,7 @@ static void show_diff_tree(struct repository *r,\n \n \tsetup_revisions(ARRAY_SIZE(argv) - 1, argv, &opt, NULL);\n \tlog_tree_commit(&opt, commit);\n+\trelease_revisions(&opt);\n }\n \n /*\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 3ffb86a4338..f507d2191cd 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -340,6 +340,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)\n \n \tunlink(file);\n \tfree(file);\n+\trelease_revisions(&rev);\n \treturn 0;\n }\n \ndiff --git a/builtin/am.c b/builtin/am.c\nindex 0f4111bafa0..93bec62afa9 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -1397,6 +1397,7 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm\n \tadd_pending_object(&rev_info, &commit->object, \"\");\n \tdiff_setup_done(&rev_info.diffopt);\n \tlog_tree_commit(&rev_info, commit);\n+\trelease_revisions(&rev_info);\n }\n \n /**\n@@ -1429,6 +1430,7 @@ static void write_index_patch(const struct am_state *state)\n \tadd_pending_object(&rev_info, &tree->object, \"\");\n \tdiff_setup_done(&rev_info.diffopt);\n \trun_diff_index(&rev_info, 1);\n+\trelease_revisions(&rev_info);\n }\n \n /**\n@@ -1582,6 +1584,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \t\tadd_pending_oid(&rev_info, \"HEAD\", &our_tree, 0);\n \t\tdiff_setup_done(&rev_info.diffopt);\n \t\trun_diff_index(&rev_info, 1);\n+\t\trelease_revisions(&rev_info);\n \t}\n \n \tif (run_apply(state, index_path))\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex 8b2b259ff0d..e4d7b6779ae 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -555,6 +555,7 @@ static int bisect_skipped_commits(struct bisect_terms *terms)\n \treset_revision_walk();\n \n \tstrbuf_release(&commit_name);\n+\trelease_revisions(&revs);\n \tfclose(fp);\n \treturn 0;\n }\n@@ -1041,6 +1042,7 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar\n \t\t\t\t\t\toid_to_hex(&commit->object.oid));\n \n \t\t\treset_revision_walk();\n+\t\t\trelease_revisions(&revs);\n \t\t} else {\n \t\t\tstrvec_push(&argv_state, argv[i]);\n \t\t}\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 885b381ab83..24bac822c56 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1203,5 +1203,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \n cleanup:\n \tcleanup_scoreboard(&sb);\n+\trelease_revisions(&revs);\n \treturn 0;\n }\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex ed993383531..93fe46c6424 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1075,6 +1075,7 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne\n \n \t/* Clean up objects used, as they will be reused. */\n \trepo_clear_commit_marks(the_repository, ALL_REV_FLAGS);\n+\trelease_revisions(&revs);\n }\n \n static int switch_branches(const struct checkout_opts *opts,\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 8b8bdad3959..74388a6b9a1 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1120,6 +1120,7 @@ static const char *find_author_by_nickname(const char *name)\n \t\tstrbuf_release(&buf);\n \t\tformat_commit_message(commit, \"%aN <%aE>\", &buf, &ctx);\n \t\tclear_mailmap(&mailmap);\n+\t\trelease_revisions(&revs);\n \t\treturn strbuf_detach(&buf, NULL);\n \t}\n \tdie(_(\"--author '%s' is not 'Name <email>' and matches no existing author\"), name);\ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex 42159cd26bd..a76f1a1a7a7 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -517,6 +517,7 @@ static void describe_blob(struct object_id oid, struct strbuf *dst)\n \n \ttraverse_commit_list(&revs, process_commit, process_object, &pcd);\n \treset_revision_walk();\n+\trelease_revisions(&revs);\n }\n \n static void describe(const char *arg, int last_one)\n@@ -667,6 +668,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)\n \t\t\t\tsuffix = NULL;\n \t\t\telse\n \t\t\t\tsuffix = dirty;\n+\t\t\trelease_revisions(&revs);\n \t\t}\n \t\tdescribe(\"HEAD\", 1);\n \t} else if (dirty) {\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex 510139e9b54..ade0120b3a8 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -1275,6 +1275,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)\n \t\tprintf(\"done\\n\");\n \n \trefspec_clear(&refspecs);\n+\trelease_revisions(&revs);\n \n \treturn 0;\n }\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex a94a03384ae..e99549db6fd 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -443,6 +443,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead\n \t}\n \twrite_file_buf(git_path_squash_msg(the_repository), out.buf, out.len);\n \tstrbuf_release(&out);\n+\trelease_revisions(&rev);\n }\n \n static void finish(struct commit *head_commit,\n@@ -992,6 +993,7 @@ static int evaluate_result(void)\n \t */\n \tcnt += count_unmerged_entries();\n \n+\trelease_revisions(&rev);\n \treturn cnt;\n }\n \ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 178e611f09d..17194a58155 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -3384,6 +3384,7 @@ static void read_packs_list_from_stdin(void)\n \tstrbuf_release(&buf);\n \tstring_list_clear(&include_packs, 0);\n \tstring_list_clear(&exclude_packs, 0);\n+\trelease_revisions(&revs);\n }\n \n static void read_object_list_from_stdin(void)\n@@ -3799,6 +3800,7 @@ static void get_object_list(int ac, const char **av)\n \tif (unpack_unreachable)\n \t\tloosen_unused_packed_objects();\n \n+\trelease_revisions(&revs);\n \toid_array_clear(&recent_objects);\n }\n \ndiff --git a/builtin/prune.c b/builtin/prune.c\nindex c2bcdc07db4..df376b2ed1e 100644\n--- a/builtin/prune.c\n+++ b/builtin/prune.c\n@@ -196,5 +196,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix)\n \t\tprune_shallow(show_only ? PRUNE_SHOW_ONLY : 0);\n \t}\n \n+\trelease_revisions(&revs);\n \treturn 0;\n }\ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex 016466852f1..5d870b4f2d1 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -648,6 +648,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)\n \t\tif (verbose)\n \t\t\tprintf(_(\"Marking reachable objects...\"));\n \t\tmark_reachable_objects(&revs, 0, 0, NULL);\n+\t\trelease_revisions(&revs);\n \t\tif (verbose)\n \t\t\tputchar('\\n');\n \t}\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex 228d782754a..e609357bd23 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -420,6 +420,8 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)\n \telse\n \t\tget_from_rev(&rev, &log);\n \n+\trelease_revisions(&rev);\n+\n \tshortlog_output(&log);\n \tif (log.file != stdout)\n \t\tfclose(log.file);\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d8638434dcb..7fbba812583 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1259,6 +1259,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \t\trun_diff_files(&rev, 0);\n \tprepare_submodule_summary(info, &list);\n \tstrvec_clear(&diff_args);\n+\trelease_revisions(&rev);\n \treturn 0;\n }\n \ndiff --git a/fmt-merge-msg.c b/fmt-merge-msg.c\nindex baca57d5b64..f48f44f9cd1 100644\n--- a/fmt-merge-msg.c\n+++ b/fmt-merge-msg.c\n@@ -699,6 +699,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,\n \t\t\tshortlog(origins.items[i].string,\n \t\t\t\t origins.items[i].util,\n \t\t\t\t head, &rev, opts, out);\n+\t\trelease_revisions(&rev);\n \t}\n \n \tstrbuf_complete_line(out);\ndiff --git a/merge-ort.c b/merge-ort.c\nindex ff739d4b360..658c580ab91 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -1593,6 +1593,7 @@ static int find_first_merges(struct repository *repo,\n \t}\n \n \tobject_array_clear(&merges);\n+\trelease_revisions(&revs);\n \treturn result->nr;\n }\n \ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 9ec1e6d043a..6296685d00b 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1160,6 +1160,7 @@ static int find_first_merges(struct repository *repo,\n \t}\n \n \tobject_array_clear(&merges);\n+\trelease_revisions(&revs);\n \treturn result->nr;\n }\n \ndiff --git a/midx.c b/midx.c\nindex 865170bad05..702c8a9b178 100644\n--- a/midx.c\n+++ b/midx.c\n@@ -1061,6 +1061,7 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr\n \tif (indexed_commits_nr_p)\n \t\t*indexed_commits_nr_p = cb.commits_nr;\n \n+\trelease_revisions(&revs);\n \treturn cb.commits;\n }\n \ndiff --git a/pack-bitmap-write.c b/pack-bitmap-write.c\nindex cab3eaa2acd..ea8e0b51cdf 100644\n--- a/pack-bitmap-write.c\n+++ b/pack-bitmap-write.c\n@@ -326,6 +326,7 @@ static void bitmap_builder_init(struct bitmap_builder *bb,\n \ttrace2_data_intmax(\"pack-bitmap-write\", the_repository,\n \t\t\t   \"num_maximal_commits\", num_maximal);\n \n+\trelease_revisions(&revs);\n \tfree_commit_list(reusable);\n }\n \ndiff --git a/ref-filter.c b/ref-filter.c\nindex 7838bd22b8d..a91688bbf17 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -2392,6 +2392,7 @@ static void reach_filter(struct ref_array *array,\n \t\tclear_commit_marks(merge_commit, ALL_REV_FLAGS);\n \t}\n \n+\trelease_revisions(&revs);\n \tfree(to_clear);\n }\n \ndiff --git a/remote.c b/remote.c\nindex c97c626eaa8..0a7d8757d99 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -2172,6 +2172,7 @@ static int stat_branch_pair(const char *branch_name, const char *base,\n \tclear_commit_marks(theirs, ALL_REV_FLAGS);\n \n \tstrvec_clear(&argv);\n+\trelease_revisions(&revs);\n \treturn 1;\n }\n \ndiff --git a/sequencer.c b/sequencer.c\nindex 35006c0cea6..b15f8d1ff50 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1347,6 +1347,7 @@ void print_commit_summary(struct repository *r,\n \t\tlog_tree_commit(&rev, commit);\n \t}\n \n+\trelease_revisions(&rev);\n \tstrbuf_release(&format);\n }\n \n@@ -3415,6 +3416,7 @@ static int make_patch(struct repository *r,\n \t\tunuse_commit_buffer(commit, commit_buffer);\n \t}\n \tstrbuf_release(&buf);\n+\trelease_revisions(&log_tree_opt);\n \n \treturn res;\n }\n@@ -4527,6 +4529,7 @@ static int pick_commits(struct repository *r,\n \t\t\t\t\t      &log_tree_opt.diffopt);\n \t\t\t\tlog_tree_diff_flush(&log_tree_opt);\n \t\t\t}\n+\t\t\trelease_revisions(&log_tree_opt);\n \t\t}\n \t\tflush_rewritten_pending();\n \t\tif (!stat(rebase_path_rewritten_list(), &st) &&\ndiff --git a/shallow.c b/shallow.c\nindex 71e5876f377..2552f139f61 100644\n--- a/shallow.c\n+++ b/shallow.c\n@@ -261,6 +261,7 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,\n \t\tif ((o->flags & both_flags) == both_flags)\n \t\t\to->flags &= ~not_shallow_flag;\n \t}\n+\trelease_revisions(&revs);\n \treturn result;\n }\n \ndiff --git a/submodule.c b/submodule.c\nindex 5ace18a7d94..0510cb193b6 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -902,9 +902,11 @@ static void collect_changed_submodules(struct repository *r,\n \t\tdiff_rev.diffopt.format_callback_data = &data;\n \t\tdiff_rev.dense_combined_merges = 1;\n \t\tdiff_tree_combined_merge(commit, &diff_rev);\n+\t\trelease_revisions(&diff_rev);\n \t}\n \n \treset_revision_walk();\n+\trelease_revisions(&rev);\n }\n \n static void free_submodules_oids(struct string_list *submodules)\ndiff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c\nindex 625b2dbf822..4a45d5bac2a 100644\n--- a/t/helper/test-revision-walking.c\n+++ b/t/helper/test-revision-walking.c\n@@ -43,6 +43,7 @@ static int run_revision_walk(void)\n \t}\n \n \treset_revision_walk();\n+\trelease_revisions(&rev);\n \treturn got_revision;\n }\n \ndiff --git a/wt-status.c b/wt-status.c\nindex 398d0951aca..a68731ea0f7 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1143,6 +1143,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)\n \t\trev.diffopt.b_prefix = \"w/\";\n \t\trun_diff_files(&rev, 0);\n \t}\n+\trelease_revisions(&rev);\n }\n \n static void wt_longstatus_print_tracking(struct wt_status *s)\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450846","messageId":"patch-07.24-295fa35174f-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 07/24] revisions API users: use release_revisions() needing \"{ 0 }\" init","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:37Z","receivedAt":"2022-03-09T13:18:09Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use release_revisions() to various users of \"struct rev_list\" which\nneed to have their \"struct rev_info\" zero-initialized before we can\nstart using it. In all of these cases we might \"goto cleanup\" (or equivalent),\n\nFor the bundle.c code see the early exit case added in\n3bbbe467f29 (bundle verify: error out if called without an object\ndatabase, 2019-05-27).\n\nFor the relevant bisect.c code see 45b6370812c (bisect: libify\n`check_good_are_ancestors_of_bad` and its dependents, 2020-02-17).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n bisect.c                    | 18 ++++++++++++------\n builtin/submodule--helper.c |  3 ++-\n bundle.c                    | 12 ++++++++----\n 3 files changed, 22 insertions(+), 11 deletions(-)\n\ndiff --git a/bisect.c b/bisect.c\nindex cc6b8b6230d..159a4b644df 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -1010,7 +1010,7 @@ void read_bisect_terms(const char **read_bad, const char **read_good)\n  */\n enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n {\n-\tstruct rev_info revs;\n+\tstruct rev_info revs = { 0 };\n \tstruct commit_list *tried;\n \tint reaches = 0, all = 0, nr, steps;\n \tenum bisect_error res = BISECT_OK;\n@@ -1035,7 +1035,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \n \tres = check_good_are_ancestors_of_bad(r, prefix, no_checkout);\n \tif (res)\n-\t\treturn res;\n+\t\tgoto cleanup;\n \n \tbisect_rev_setup(r, &revs, prefix, \"%s\", \"^%s\", 1);\n \n@@ -1060,14 +1060,16 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \t\t       term_good,\n \t\t       term_bad);\n \n-\t\treturn BISECT_FAILED;\n+\t\tres = BISECT_FAILED;\n+\t\tgoto cleanup;\n \t}\n \n \tif (!all) {\n \t\tfprintf(stderr, _(\"No testable commit found.\\n\"\n \t\t\t\"Maybe you started with bad path arguments?\\n\"));\n \n-\t\treturn BISECT_NO_TESTABLE_COMMIT;\n+\t\tres = BISECT_NO_TESTABLE_COMMIT;\n+\t\tgoto cleanup;\n \t}\n \n \tbisect_rev = &revs.commits->item->object.oid;\n@@ -1087,7 +1089,8 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \t\t * for negative return values for early returns up\n \t\t * until the cmd_bisect__helper() caller.\n \t\t */\n-\t\treturn BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;\n+\t\tres = BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;\n+\t\tgoto cleanup;\n \t}\n \n \tnr = all - reaches - 1;\n@@ -1106,7 +1109,10 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \t/* Clean up objects used, as they will be reused. */\n \trepo_clear_commit_marks(r, ALL_REV_FLAGS);\n \n-\treturn bisect_checkout(bisect_rev, no_checkout);\n+\tres = bisect_checkout(bisect_rev, no_checkout);\n+cleanup:\n+\trelease_revisions(&revs);\n+\treturn res;\n }\n \n static inline int log2i(int n)\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 7fbba812583..09db2620829 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -756,7 +756,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,\n {\n \tchar *displaypath;\n \tstruct strvec diff_files_args = STRVEC_INIT;\n-\tstruct rev_info rev;\n+\tstruct rev_info rev = { 0 };\n \tint diff_files_result;\n \tstruct strbuf buf = STRBUF_INIT;\n \tconst char *git_dir;\n@@ -843,6 +843,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,\n cleanup:\n \tstrvec_clear(&diff_files_args);\n \tfree(displaypath);\n+\trelease_revisions(&rev);\n }\n \n static void status_submodule_cb(const struct cache_entry *list_item,\ndiff --git a/bundle.c b/bundle.c\nindex a0bb687b0f4..207541a3410 100644\n--- a/bundle.c\n+++ b/bundle.c\n@@ -191,14 +191,16 @@ int verify_bundle(struct repository *r,\n \t * to be verbose about the errors\n \t */\n \tstruct string_list *p = &header->prerequisites;\n-\tstruct rev_info revs;\n+\tstruct rev_info revs = { 0 };\n \tconst char *argv[] = {NULL, \"--all\", NULL};\n \tstruct commit *commit;\n \tint i, ret = 0, req_nr;\n \tconst char *message = _(\"Repository lacks these prerequisite commits:\");\n \n-\tif (!r || !r->objects || !r->objects->odb)\n-\t\treturn error(_(\"need a repository to verify a bundle\"));\n+\tif (!r || !r->objects || !r->objects->odb) {\n+\t\tret = error(_(\"need a repository to verify a bundle\"));\n+\t\tgoto cleanup;\n+\t}\n \n \trepo_init_revisions(r, &revs, NULL);\n \tfor (i = 0; i < p->nr; i++) {\n@@ -216,7 +218,7 @@ int verify_bundle(struct repository *r,\n \t\terror(\"%s %s\", oid_to_hex(oid), name);\n \t}\n \tif (revs.pending.nr != p->nr)\n-\t\treturn ret;\n+\t\tgoto cleanup;\n \treq_nr = revs.pending.nr;\n \tsetup_revisions(2, argv, &revs, NULL);\n \n@@ -270,6 +272,8 @@ int verify_bundle(struct repository *r,\n \t\t\tlist_refs(r, 0, NULL);\n \t\t}\n \t}\n+cleanup:\n+\trelease_revisions(&revs);\n \treturn ret;\n }\n \n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450847","messageId":"patch-08.24-0f9d313b338-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 08/24] revisions API users: add \"goto cleanup\" for release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:38Z","receivedAt":"2022-03-09T13:18:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a release_revisions() to various users of \"struct rev_list\" which\nrequires a minor refactoring to a \"goto cleanup\" pattern to use that\nfunction.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/diff-files.c        |  8 ++++++--\n builtin/rev-list.c          | 19 ++++++++++++-------\n builtin/stash.c             | 14 ++++++++++----\n builtin/submodule--helper.c | 11 ++++++++---\n sequencer.c                 | 23 ++++++++++++++++-------\n t/helper/test-fast-rebase.c | 19 +++++++++++++------\n 6 files changed, 65 insertions(+), 29 deletions(-)\n\ndiff --git a/builtin/diff-files.c b/builtin/diff-files.c\nindex 70103c40952..92cf6e1e922 100644\n--- a/builtin/diff-files.c\n+++ b/builtin/diff-files.c\n@@ -77,8 +77,12 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)\n \n \tif (read_cache_preload(&rev.diffopt.pathspec) < 0) {\n \t\tperror(\"read_cache_preload\");\n-\t\treturn -1;\n+\t\tresult = -1;\n+\t\tgoto cleanup;\n \t}\n \tresult = run_diff_files(&rev, options);\n-\treturn diff_result_code(&rev.diffopt, result);\n+\tresult = diff_result_code(&rev.diffopt, result);\n+cleanup:\n+\trelease_revisions(&rev);\n+\treturn result;\n }\ndiff --git a/builtin/rev-list.c b/builtin/rev-list.c\nindex 38528c7f156..5db8e71645f 100644\n--- a/builtin/rev-list.c\n+++ b/builtin/rev-list.c\n@@ -506,6 +506,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \tint use_bitmap_index = 0;\n \tint filter_provided_objects = 0;\n \tconst char *show_progress = NULL;\n+\tint ret = 0;\n \n \tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n \t\tusage(rev_list_usage);\n@@ -589,7 +590,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \t\t}\n \t\tif (!strcmp(arg, \"--test-bitmap\")) {\n \t\t\ttest_bitmap_walk(&revs);\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t\t}\n \t\tif (skip_prefix(arg, \"--progress=\", &arg)) {\n \t\t\tshow_progress = arg;\n@@ -689,11 +690,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \n \tif (use_bitmap_index) {\n \t\tif (!try_bitmap_count(&revs, &filter_options, filter_provided_objects))\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t\tif (!try_bitmap_disk_usage(&revs, &filter_options, filter_provided_objects))\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t\tif (!try_bitmap_traversal(&revs, &filter_options, filter_provided_objects))\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t}\n \n \tif (prepare_revision_walk(&revs))\n@@ -713,8 +714,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \n \t\tfind_bisection(&revs.commits, &reaches, &all, bisect_flags);\n \n-\t\tif (bisect_show_vars)\n-\t\t\treturn show_bisect_vars(&info, reaches, all);\n+\t\tif (bisect_show_vars) {\n+\t\t\tret = show_bisect_vars(&info, reaches, all);\n+\t\t\tgoto cleanup;\n+\t\t}\n \t}\n \n \tif (filter_provided_objects) {\n@@ -769,5 +772,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \tif (show_disk_usage)\n \t\tprintf(\"%\"PRIuMAX\"\\n\", (uintmax_t)total_disk_usage);\n \n-\treturn 0;\n+cleanup:\n+\trelease_revisions(&revs);\n+\treturn ret;\n }\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex af9b61ef17a..0b9d55d09bb 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -874,8 +874,10 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \n \tret = get_stash_info(&info, stash_args.nr, stash_args.v);\n \tstrvec_clear(&stash_args);\n-\tif (ret)\n-\t\treturn -1;\n+\tif (ret) {\n+\t\tret = -1;\n+\t\tgoto cleanup;\n+\t}\n \n \t/*\n \t * The config settings are applied only if there are not passed\n@@ -890,7 +892,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \n \t\tif (!show_stat && !show_patch) {\n \t\t\tfree_stash_info(&info);\n-\t\t\treturn 0;\n+\t\t\tret = 0;\n+\t\t\tgoto cleanup;\n \t\t}\n \t}\n \n@@ -924,7 +927,10 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \tlog_tree_diff_flush(&rev);\n \n \tfree_stash_info(&info);\n-\treturn diff_result_code(&rev.diffopt, 0);\n+\tret = diff_result_code(&rev.diffopt, 0);\n+cleanup:\n+\trelease_revisions(&rev);\n+\treturn ret;\n }\n \n static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 09db2620829..19393da4e31 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1222,6 +1222,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \tstruct strvec diff_args = STRVEC_INIT;\n \tstruct rev_info rev;\n \tstruct module_cb_list list = MODULE_CB_LIST_INIT;\n+\tint ret = 0;\n \n \tstrvec_push(&diff_args, get_diff_cmd(diff_cmd));\n \tif (info->cached)\n@@ -1247,11 +1248,13 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \t\t\tsetup_work_tree();\n \t\tif (read_cache_preload(&rev.diffopt.pathspec) < 0) {\n \t\t\tperror(\"read_cache_preload\");\n-\t\t\treturn -1;\n+\t\t\tret = -1;\n+\t\t\tgoto cleanup;\n \t\t}\n \t} else if (read_cache() < 0) {\n \t\tperror(\"read_cache\");\n-\t\treturn -1;\n+\t\tret = -1;\n+\t\tgoto cleanup;\n \t}\n \n \tif (diff_cmd == DIFF_INDEX)\n@@ -1259,9 +1262,11 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \telse\n \t\trun_diff_files(&rev, 0);\n \tprepare_submodule_summary(info, &list);\n+\tret = 0;\n+cleanup:\n \tstrvec_clear(&diff_args);\n \trelease_revisions(&rev);\n-\treturn 0;\n+\treturn ret;\n }\n \n static int module_summary(int argc, const char **argv, const char *prefix)\ndiff --git a/sequencer.c b/sequencer.c\nindex b15f8d1ff50..40d41c09523 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -5356,6 +5356,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \tint rebase_merges = flags & TODO_LIST_REBASE_MERGES;\n \tint reapply_cherry_picks = flags & TODO_LIST_REAPPLY_CHERRY_PICKS;\n \tint skipped_commit = 0;\n+\tint ret = 0;\n \n \trepo_init_revisions(r, &revs, NULL);\n \trevs.verbose_header = 1;\n@@ -5379,14 +5380,20 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \tpp.fmt = revs.commit_format;\n \tpp.output_encoding = get_log_output_encoding();\n \n-\tif (setup_revisions(argc, argv, &revs, NULL) > 1)\n-\t\treturn error(_(\"make_script: unhandled options\"));\n+\tif (setup_revisions(argc, argv, &revs, NULL) > 1) {\n+\t\tret = error(_(\"make_script: unhandled options\"));\n+\t\tgoto cleanup;\n+\t}\n \n-\tif (prepare_revision_walk(&revs) < 0)\n-\t\treturn error(_(\"make_script: error preparing revisions\"));\n+\tif (prepare_revision_walk(&revs) < 0) {\n+\t\tret = error(_(\"make_script: error preparing revisions\"));\n+\t\tgoto cleanup;\n+\t}\n \n-\tif (rebase_merges)\n-\t\treturn make_script_with_merges(&pp, &revs, out, flags);\n+\tif (rebase_merges) {\n+\t\tret = make_script_with_merges(&pp, &revs, out, flags);\n+\t\tgoto cleanup;\n+\t}\n \n \twhile ((commit = get_revision(&revs))) {\n \t\tint is_empty = is_original_commit_empty(commit);\n@@ -5410,7 +5417,9 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \tif (skipped_commit)\n \t\tadvise_if_enabled(ADVICE_SKIPPED_CHERRY_PICKS,\n \t\t\t\t  _(\"use --reapply-cherry-picks to include skipped commits\"));\n-\treturn 0;\n+cleanup:\n+\trelease_revisions(&revs);\n+\treturn ret;\n }\n \n /*\ndiff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c\nindex 993b90eaedd..e49a0244836 100644\n--- a/t/helper/test-fast-rebase.c\n+++ b/t/helper/test-fast-rebase.c\n@@ -99,6 +99,7 @@ int cmd__fast_rebase(int argc, const char **argv)\n \tstruct merge_result result;\n \tstruct strbuf reflog_msg = STRBUF_INIT;\n \tstruct strbuf branch_name = STRBUF_INIT;\n+\tint ret = 0;\n \n \t/*\n \t * test-tool stuff doesn't set up the git directory by default; need to\n@@ -137,13 +138,17 @@ int cmd__fast_rebase(int argc, const char **argv)\n \trevs.topo_order = 1;\n \tstrvec_pushl(&rev_walk_args, \"\", argv[4], \"--not\", argv[3], NULL);\n \n-\tif (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1)\n-\t\treturn error(_(\"unhandled options\"));\n+\tif (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) {\n+\t\tret =  error(_(\"unhandled options\"));\n+\t\tgoto cleanup;\n+\t}\n \n \tstrvec_clear(&rev_walk_args);\n \n-\tif (prepare_revision_walk(&revs) < 0)\n-\t\treturn error(_(\"error preparing revisions\"));\n+\tif (prepare_revision_walk(&revs) < 0) {\n+\t\tret = error(_(\"error preparing revisions\"));\n+\t\tgoto cleanup;\n+\t}\n \n \tinit_merge_options(&merge_opt, the_repository);\n \tmemset(&result, 0, sizeof(result));\n@@ -219,8 +224,10 @@ int cmd__fast_rebase(int argc, const char **argv)\n \tif (write_locked_index(&the_index, &lock,\n \t\t\t       COMMIT_LOCK | SKIP_IF_UNCHANGED))\n \t\tdie(_(\"unable to write %s\"), get_index_file());\n-\n+\tret = (result.clean == 0);\n+cleanup:\n \tstrbuf_release(&reflog_msg);\n \tstrbuf_release(&branch_name);\n-\treturn (result.clean == 0);\n+\trelease_revisions(&revs);\n+\treturn ret;\n }\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450848","messageId":"patch-10.24-a89f0da4fd7-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 10/24] revisions API users: use release_revisions() in builtin/log.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:40Z","receivedAt":"2022-03-09T13:18:29Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In preparation for having the \"log\" family of functions make wider use\nof release_revisions() let's have them call it just before\nexiting. This changes the \"log\", \"whatchanged\", \"show\",\n\"format-patch\", etc. commands, all of which live in this file.\n\nThe release_revisions() API still only frees the \"pending\" member, but\nwill learn to more members of \"struct rev_info\" in subsequent commits.\n\nIn the case of \"format-patch\" revert the addition of UNLEAK() in\ndee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16), and\nwhich will cause several tests that previously passed under\n\"TEST_PASSES_SANITIZE_LEAK=true\" to start failing.\n\nIn subsequent commits we'll now be able to use those tests to check\nwhether that part of the API is really leaking memory, and will fix\nall of those memory leaks. Removing the UNLEAK() allows us to make\nincremental progress in that direction. See [1] for further details\nabout this approach.\n\n1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/log.c          | 20 ++++++++++++--------\n t/t4126-apply-empty.sh |  2 --\n 2 files changed, 12 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 00846c2c8ac..3c1edc27605 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -294,6 +294,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,\n \tcmd_log_init_finish(argc, argv, prefix, rev, opt);\n }\n \n+static int cmd_log_deinit(int ret, struct rev_info *rev)\n+{\n+\trelease_revisions(rev);\n+\treturn ret;\n+}\n+\n /*\n  * This gives a rough estimate for how many commits we\n  * will print out in the list.\n@@ -557,7 +563,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)\n \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n \tif (!rev.diffopt.output_format)\n \t\trev.diffopt.output_format = DIFF_FORMAT_RAW;\n-\treturn cmd_log_walk(&rev);\n+\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n }\n \n static void show_tagger(const char *buf, struct rev_info *rev)\n@@ -676,7 +682,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n \n \tif (!rev.no_walk)\n-\t\treturn cmd_log_walk(&rev);\n+\t\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n \n \tcount = rev.pending.nr;\n \tobjects = rev.pending.objects;\n@@ -731,8 +737,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \t\t\tret = error(_(\"unknown type: %d\"), o->type);\n \t\t}\n \t}\n-\tfree(objects);\n-\treturn ret;\n+\treturn cmd_log_deinit(ret, &rev);\n }\n \n /*\n@@ -760,7 +765,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)\n \trev.always_show_header = 1;\n \tcmd_log_init_finish(argc, argv, prefix, &rev, &opt);\n \n-\treturn cmd_log_walk(&rev);\n+\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n }\n \n static void log_setup_revisions_tweak(struct rev_info *rev,\n@@ -791,7 +796,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)\n \topt.revarg_opt = REVARG_COMMITTISH;\n \topt.tweak = log_setup_revisions_tweak;\n \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n-\treturn cmd_log_walk(&rev);\n+\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n }\n \n /* format-patch */\n@@ -2290,8 +2295,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tstring_list_clear(rev.ref_message_ids, 0);\n \t\tfree(rev.ref_message_ids);\n \t}\n-\tUNLEAK(rev);\n-\treturn 0;\n+\treturn cmd_log_deinit(0, &rev);\n }\n \n static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)\ndiff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh\nindex 33860d38290..66a7ba8ab8f 100755\n--- a/t/t4126-apply-empty.sh\n+++ b/t/t4126-apply-empty.sh\n@@ -2,8 +2,6 @@\n \n test_description='apply empty'\n \n-\n-TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450849","messageId":"patch-09.24-8f93b87f369-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 09/24] revisions API users: use release_revisions() in http-push.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:39Z","receivedAt":"2022-03-09T13:18:32Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In the case of cmd_main() in http-push.c we need to move the\ndeceleration of the \"struct rev-list\" into the loop over the\n\"remote_refs\" when adding a release_revisions().\n\nWe'd previously set up the \"revs\" for each remote, but would\npotentially leak memory on each one.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n http-push.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/http-push.c b/http-push.c\nindex 3309aaf004a..8c18f01bb60 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -1689,7 +1689,6 @@ int cmd_main(int argc, const char **argv)\n \tstruct refspec rs = REFSPEC_INIT_PUSH;\n \tstruct remote_lock *ref_lock = NULL;\n \tstruct remote_lock *info_ref_lock = NULL;\n-\tstruct rev_info revs;\n \tint delete_branch = 0;\n \tint force_delete = 0;\n \tint objects_to_send;\n@@ -1825,6 +1824,7 @@ int cmd_main(int argc, const char **argv)\n \n \tnew_refs = 0;\n \tfor (ref = remote_refs; ref; ref = ref->next) {\n+\t\tstruct rev_info revs;\n \t\tstruct strvec commit_argv = STRVEC_INIT;\n \n \t\tif (!ref->peer_ref)\n@@ -1941,6 +1941,7 @@ int cmd_main(int argc, const char **argv)\n \t\tunlock_remote(ref_lock);\n \t\tcheck_locks();\n \t\tstrvec_clear(&commit_argv);\n+\t\trelease_revisions(&revs);\n \t}\n \n \t/* Update remote server info if appropriate */\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450850","messageId":"patch-11.24-a93c8838257-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 11/24] revisions API users: use release_revisions() with UNLEAK()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:41Z","receivedAt":"2022-03-09T13:18:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use a release_revisions() with those \"struct rev_list\" users which\nalready \"UNLEAK\" the struct. It may seem odd to simultaneously attempt\nto free() memory, but also to explicitly ignore whether we have memory\nleaks in the same.\n\nAs explained in preceding commits this is being done to use the\nbuilt-in commands as a guinea pig for whether the release_revisions()\nfunction works as expected, we'd like to test e.g. whether we segfault\nas we change it. In subsequent commits we'll then remove these\nUNLEAK() as the function is made to free the memory that caused us to\nadd them in the first place.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/diff-index.c | 4 +++-\n builtin/diff.c       | 1 +\n 2 files changed, 4 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/diff-index.c b/builtin/diff-index.c\nindex 5fd23ab5b6c..3a83183c312 100644\n--- a/builtin/diff-index.c\n+++ b/builtin/diff-index.c\n@@ -71,5 +71,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)\n \t}\n \tresult = run_diff_index(&rev, option);\n \tUNLEAK(rev);\n-\treturn diff_result_code(&rev.diffopt, result);\n+\tresult = diff_result_code(&rev.diffopt, result);\n+\trelease_revisions(&rev);\n+\treturn result;\n }\ndiff --git a/builtin/diff.c b/builtin/diff.c\nindex bb7fafca618..dd48336da56 100644\n--- a/builtin/diff.c\n+++ b/builtin/diff.c\n@@ -595,6 +595,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \tif (1 < rev.diffopt.skip_stat_unmatch)\n \t\trefresh_index_quietly();\n \tUNLEAK(rev);\n+\trelease_revisions(&rev);\n \tUNLEAK(ent);\n \tUNLEAK(blob);\n \treturn result;\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450851","messageId":"patch-14.24-761bbd1763c-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 14/24] revisions API users: use release_revisions() for \"prune_data\" users","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:44Z","receivedAt":"2022-03-09T13:18:36Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use release_revisions() for users of \"struct rev_list\" that reach into\nthe \"struct rev_info\" and clear the \"prune_data\" already.\n\nIn a subsequent commit we'll teach release_revisions() to clear this\nitself, but in the meantime let's invoke release_revisions() here to\nclear anything else we may have missed, and for reasons of having\nconsistent boilerplate.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/add.c   | 1 +\n builtin/stash.c | 1 +\n diff-lib.c      | 1 +\n wt-status.c     | 1 +\n 4 files changed, 4 insertions(+)\n\ndiff --git a/builtin/add.c b/builtin/add.c\nindex f507d2191cd..115a26ea633 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -143,6 +143,7 @@ int add_files_to_cache(const char *prefix,\n \trev.max_count = 0; /* do not compare unmerged paths with stage #2 */\n \trun_diff_files(&rev, DIFF_RACY_IS_MODIFIED);\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n \treturn !!data.add_errors;\n }\n \ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 0b9d55d09bb..b9fe6f7c712 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1073,6 +1073,7 @@ static int check_changes_tracked_files(const struct pathspec *ps)\n \n done:\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n \treturn ret;\n }\n \ndiff --git a/diff-lib.c b/diff-lib.c\nindex ad1dd4834f7..68858a39964 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -642,6 +642,7 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)\n \tif (diff_cache(&revs, tree_oid, NULL, 1))\n \t\texit(128);\n \tclear_pathspec(&revs.prune_data);\n+\trelease_revisions(&revs);\n \treturn 0;\n }\n \ndiff --git a/wt-status.c b/wt-status.c\nindex 89289e52603..aaef10451f3 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -617,6 +617,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)\n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_files(&rev, 0);\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n }\n \n static void wt_status_collect_changes_index(struct wt_status *s)\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450852","messageId":"patch-15.24-4a0adf6fd00-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 15/24] revisions API: have release_revisions() release \"commits\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:45Z","receivedAt":"2022-03-09T13:18:37Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"commits\" in the \"struct ref_info\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/revision.c b/revision.c\nindex 831f2cf977b..475901d77e3 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2930,6 +2930,7 @@ void release_revisions(struct rev_info *revs)\n {\n \tif (!revs)\n \t\treturn;\n+\trelease_revisions_commit_list(revs);\n \tobject_array_clear(&revs->pending);\n }\n \n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450853","messageId":"patch-21.24-ccf276641d6-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 21/24] revisions API: release \"reflog_info\" in release revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:51Z","receivedAt":"2022-03-09T13:18:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a missing reflog_walk_info_release() to \"reflog-walk.c\" and use it\nin release_revisions().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n reflog-walk.c            | 26 ++++++++++++++++++++++++--\n reflog-walk.h            |  1 +\n revision.c               |  1 +\n t/t0100-previous.sh      |  1 +\n t/t1401-symbolic-ref.sh  |  2 ++\n t/t1411-reflog-show.sh   |  1 +\n t/t1412-reflog-loop.sh   |  2 ++\n t/t1415-worktree-refs.sh |  1 +\n 8 files changed, 33 insertions(+), 2 deletions(-)\n\ndiff --git a/reflog-walk.c b/reflog-walk.c\nindex 8ac4b284b6b..4322228d122 100644\n--- a/reflog-walk.c\n+++ b/reflog-walk.c\n@@ -7,7 +7,7 @@\n #include \"reflog-walk.h\"\n \n struct complete_reflogs {\n-\tchar *ref;\n+\tconst char *ref;\n \tconst char *short_ref;\n \tstruct reflog_info {\n \t\tstruct object_id ooid, noid;\n@@ -50,10 +50,17 @@ static void free_complete_reflog(struct complete_reflogs *array)\n \t\tfree(array->items[i].message);\n \t}\n \tfree(array->items);\n-\tfree(array->ref);\n+\tfree((char *)array->ref);\n+\tfree((char *)array->short_ref);\n \tfree(array);\n }\n \n+static void complete_reflogs_clear(void *util, const char *str)\n+{\n+\tstruct complete_reflogs *array = util;\n+\tfree_complete_reflog(array);\n+}\n+\n static struct complete_reflogs *read_complete_reflog(const char *ref)\n {\n \tstruct complete_reflogs *reflogs =\n@@ -116,6 +123,21 @@ void init_reflog_walk(struct reflog_walk_info **info)\n \t(*info)->complete_reflogs.strdup_strings = 1;\n }\n \n+void reflog_walk_info_release(struct reflog_walk_info *info)\n+{\n+\tsize_t i;\n+\n+\tif (!info)\n+\t\treturn;\n+\n+\tfor (i = 0; i < info->nr; i++)\n+\t\tfree(info->logs[i]);\n+\tstring_list_clear_func(&info->complete_reflogs,\n+\t\t\t       complete_reflogs_clear);\n+\tfree(info->logs);\n+\tfree(info);\n+}\n+\n int add_reflog_for_walk(struct reflog_walk_info *info,\n \t\tstruct commit *commit, const char *name)\n {\ndiff --git a/reflog-walk.h b/reflog-walk.h\nindex e9e00ffd479..8076f10d9fb 100644\n--- a/reflog-walk.h\n+++ b/reflog-walk.h\n@@ -8,6 +8,7 @@ struct reflog_walk_info;\n struct date_mode;\n \n void init_reflog_walk(struct reflog_walk_info **info);\n+void reflog_walk_info_release(struct reflog_walk_info *info);\n int add_reflog_for_walk(struct reflog_walk_info *info,\n \t\t\tstruct commit *commit, const char *name);\n void show_reflog_message(struct reflog_walk_info *info, int,\ndiff --git a/revision.c b/revision.c\nindex a73e76bed4a..ec6cd662467 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2956,6 +2956,7 @@ void release_revisions(struct rev_info *revs)\n \tclear_pathspec(&revs->prune_data);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n+\treflog_walk_info_release(revs->reflog_info);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\ndiff --git a/t/t0100-previous.sh b/t/t0100-previous.sh\nindex 69beb59f627..a16cc3d2983 100755\n--- a/t/t0100-previous.sh\n+++ b/t/t0100-previous.sh\n@@ -5,6 +5,7 @@ test_description='previous branch syntax @{-n}'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'branch -d @{-1}' '\ndiff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh\nindex 132a1b885ac..9fb0b90f252 100755\n--- a/t/t1401-symbolic-ref.sh\n+++ b/t/t1401-symbolic-ref.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='basic symbolic-ref tests'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # If the tests munging HEAD fail, they can break detection of\ndiff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh\nindex 0bb319b944a..3770ceffafd 100755\n--- a/t/t1411-reflog-show.sh\n+++ b/t/t1411-reflog-show.sh\n@@ -4,6 +4,7 @@ test_description='Test reflog display routines'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t1412-reflog-loop.sh b/t/t1412-reflog-loop.sh\nindex 977603f7f1c..ff30874f940 100755\n--- a/t/t1412-reflog-loop.sh\n+++ b/t/t1412-reflog-loop.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='reflog walk shows repeated commits again'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup commits' '\ndiff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh\nindex a3e6ea08088..3b531842dd4 100755\n--- a/t/t1415-worktree-refs.sh\n+++ b/t/t1415-worktree-refs.sh\n@@ -2,6 +2,7 @@\n \n test_description='per-worktree refs'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450854","messageId":"patch-20.24-fa53e81c7c0-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 20/24] revisions API: clear \"boundary_commits\" in release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:50Z","receivedAt":"2022-03-09T13:18:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Clear the \"boundary_commits\" object_array in release_revisions(). This\nmakes a *lot* of tests pass under SANITIZE=leak, including most of the\nt/t[0-9]*git-svn*.sh tests.\n\nThis includes the tests we had false-positive passes on before my\n6798b08e848 (perl Git.pm: don't ignore signalled failure in\n_cmd_close(), 2022-02-01), now they pass for real.\n\nSince there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to\nlist those that don't pass than to touch most of those 66. So let's\nintroduce a \"TEST_FAILS_SANITIZE_LEAK=true\", which if set in the tests\nwon't cause lib-git-svn.sh to set \"TEST_PASSES_SANITIZE_LEAK=true.\n\nThis change also marks all the tests that we removed\n\"TEST_FAILS_SANITIZE_LEAK=true\" from in an earlier commit due to\nremoving the UNLEAK() from cmd_format_patch(), we can now assert that\nits API use doesn't leak any \"struct rev_info\" memory.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c                           | 1 +\n t/t4021-format-patch-numbered.sh     | 1 +\n t/t4028-format-patch-mime-headers.sh | 2 ++\n t/t4036-format-patch-signer-mime.sh  | 1 +\n t/t4122-apply-symlink-inside.sh      | 1 +\n t/t4126-apply-empty.sh               | 1 +\n t/t6110-rev-list-sparse.sh           | 1 +\n t/t9001-send-email.sh                | 1 +\n 8 files changed, 9 insertions(+)\n\ndiff --git a/revision.c b/revision.c\nindex 290700ea66f..a73e76bed4a 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2952,6 +2952,7 @@ void release_revisions(struct rev_info *revs)\n \trelease_revisions_commit_list(revs);\n \tobject_array_clear(&revs->pending);\n \trelease_revisions_cmdline(&revs->cmdline);\n+\tobject_array_clear(&revs->boundary_commits);\n \tclear_pathspec(&revs->prune_data);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\ndiff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh\nindex 9be65fd4440..1219aa226dc 100755\n--- a/t/t4021-format-patch-numbered.sh\n+++ b/t/t4021-format-patch-numbered.sh\n@@ -5,6 +5,7 @@\n \n test_description='Format-patch numbering options'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4028-format-patch-mime-headers.sh b/t/t4028-format-patch-mime-headers.sh\nindex 204ba673cb5..60cb819c42e 100755\n--- a/t/t4028-format-patch-mime-headers.sh\n+++ b/t/t4028-format-patch-mime-headers.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='format-patch mime headers and extra headers do not conflict'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'create commit with utf-8 body' '\ndiff --git a/t/t4036-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh\nindex 98d9713d8b2..48655bcc789 100755\n--- a/t/t4036-format-patch-signer-mime.sh\n+++ b/t/t4036-format-patch-signer-mime.sh\n@@ -2,6 +2,7 @@\n \n test_description='format-patch -s should force MIME encoding as needed'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh\nindex aa52de401b9..96965373036 100755\n--- a/t/t4122-apply-symlink-inside.sh\n+++ b/t/t4122-apply-symlink-inside.sh\n@@ -4,6 +4,7 @@ test_description='apply to deeper directory without getting fooled with symlink'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh\nindex 66a7ba8ab8f..ece9fae207d 100755\n--- a/t/t4126-apply-empty.sh\n+++ b/t/t4126-apply-empty.sh\n@@ -2,6 +2,7 @@\n \n test_description='apply empty'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t6110-rev-list-sparse.sh b/t/t6110-rev-list-sparse.sh\nindex 13c1da53528..ddefc7f24ee 100755\n--- a/t/t6110-rev-list-sparse.sh\n+++ b/t/t6110-rev-list-sparse.sh\n@@ -4,6 +4,7 @@ test_description='operations that cull histories in unusual ways'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh\nindex 84d0f40d76a..dfa6b20f7a6 100755\n--- a/t/t9001-send-email.sh\n+++ b/t/t9001-send-email.sh\n@@ -4,6 +4,7 @@ test_description='git send-email'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # May be altered later in the test\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450855","messageId":"patch-19.24-b5948be0d4e-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 19/24] revisions API: have release_revisions() release \"prune_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:49Z","receivedAt":"2022-03-09T13:18:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"prune_data\" in the \"struct ref_info\". This means that any code that\ncalls \"release_revisions()\" already can get rid of adjacent calls to\nclear_pathspec().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n add-interactive.c | 2 --\n builtin/add.c     | 1 -\n builtin/stash.c   | 2 --\n diff-lib.c        | 1 -\n revision.c        | 1 +\n wt-status.c       | 2 --\n 6 files changed, 1 insertion(+), 8 deletions(-)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex 13d5ec6f49f..bd612270001 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -566,8 +566,6 @@ static int get_modified_files(struct repository *r,\n \t\t\trun_diff_files(&rev, 0);\n \t\t}\n \n-\t\tif (ps)\n-\t\t\tclear_pathspec(&rev.prune_data);\n \t\trelease_revisions(&rev);\n \t}\n \thashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 115a26ea633..fc729e14c17 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -142,7 +142,6 @@ int add_files_to_cache(const char *prefix,\n \trev.diffopt.flags.override_submodule_config = 1;\n \trev.max_count = 0; /* do not compare unmerged paths with stage #2 */\n \trun_diff_files(&rev, DIFF_RACY_IS_MODIFIED);\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n \treturn !!data.add_errors;\n }\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex b9fe6f7c712..53bfa82eeb1 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1072,7 +1072,6 @@ static int check_changes_tracked_files(const struct pathspec *ps)\n \t}\n \n done:\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n \treturn ret;\n }\n@@ -1284,7 +1283,6 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps\n \n done:\n \tdiscard_index(&istate);\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n \tstrbuf_release(&diff_output);\n \tremove_path(stash_index_path.buf);\ndiff --git a/diff-lib.c b/diff-lib.c\nindex 68858a39964..255abb4959f 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -641,7 +641,6 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)\n \n \tif (diff_cache(&revs, tree_oid, NULL, 1))\n \t\texit(128);\n-\tclear_pathspec(&revs.prune_data);\n \trelease_revisions(&revs);\n \treturn 0;\n }\ndiff --git a/revision.c b/revision.c\nindex f51a60b2a96..290700ea66f 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2952,6 +2952,7 @@ void release_revisions(struct rev_info *revs)\n \trelease_revisions_commit_list(revs);\n \tobject_array_clear(&revs->pending);\n \trelease_revisions_cmdline(&revs->cmdline);\n+\tclear_pathspec(&revs->prune_data);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n }\ndiff --git a/wt-status.c b/wt-status.c\nindex aaef10451f3..4e6b2ca9b82 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -616,7 +616,6 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)\n \trev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;\n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_files(&rev, 0);\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n }\n \n@@ -655,7 +654,6 @@ static void wt_status_collect_changes_index(struct wt_status *s)\n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_index(&rev, 1);\n \trelease_revisions(&rev);\n-\tclear_pathspec(&rev.prune_data);\n }\n \n static int add_file_to_list(const struct object_id *oid,\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450856","messageId":"patch-17.24-b5687ed1355-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 17/24] revisions API: have release_revisions() release \"cmdline\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:47Z","receivedAt":"2022-03-09T13:18:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"cmdline\" in the \"struct ref_info\". This in combination with a\npreceding change to free \"commits\" and \"mailmap\" means that we can\nwhitelist another test under \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nThere was a proposal in [1] to do away with xstrdup()-ing this\nadd_rev_cmdline(), perhaps that would be worthwhile, but for now let's\njust free() it.\n\nWe could also make that a \"char *\" in \"struct rev_cmdline_entry\"\nitself, but since we own it let's expose it as a constant to outside\ncallers. I proposed that in [2] but have since changed my mind. See\n14d30cdfc04 (ref-filter: fix memory leak in `free_array_item()`,\n2019-07-10), c514c62a4fd (checkout: fix leak of non-existent branch\nnames, 2020-08-14) and other log history hits for \"free((char *)\" for\nprior art.\n\nThis includes the tests we had false-positive passes on before my\n6798b08e848 (perl Git.pm: don't ignore signalled failure in\n_cmd_close(), 2022-02-01), now they pass for real.\n\nSince there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to\nlist those that don't pass than to touch most of those 66. So let's\nintroduce a \"TEST_FAILS_SANITIZE_LEAK=true\", which if set in the tests\nwon't cause lib-git-svn.sh to set \"TEST_PASSES_SANITIZE_LEAK=true.\n\nThis change also marks all the tests that we removed\n\"TEST_FAILS_SANITIZE_LEAK=true\" from in an earlier commit due to\nremoving the UNLEAK() from cmd_format_patch(), we can now assert that\nits API use doesn't leak any \"struct rev_info\" memory.\n\nThis change also made commit \"t5503-tagfollow.sh\" pass on current\nmaster, but that would regress when combined with\nps/fetch-atomic-fixup's de004e848a9 (t5503: simplify setup of test\nwhich exercises failure of backfill, 2022-03-03) (through no fault of\nthat topic, that change started using \"git clone\" in the test, which\nhas an outstanding leak). Let's leave that test out for now to avoid\nin-flight semantic conflicts.\n\n1. https://lore.kernel.org/git/YUj%2FgFRh6pwrZalY@carlos-mbp.lan/\n2. https://lore.kernel.org/git/87o88obkb1.fsf@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nfixup with bc0e0439040?\n---\n revision.c                                       | 12 ++++++++++++\n t/lib-git-svn.sh                                 |  4 ++++\n t/t0062-revision-walking.sh                      |  1 +\n t/t0101-at-syntax.sh                             |  2 ++\n t/t1060-object-corruption.sh                     |  2 ++\n t/t2015-checkout-unborn.sh                       |  1 +\n t/t3408-rebase-multi-line.sh                     |  1 +\n t/t4207-log-decoration-colors.sh                 |  1 +\n t/t4212-log-corrupt.sh                           |  1 +\n t/t5313-pack-bounds-checks.sh                    |  2 ++\n t/t5322-pack-objects-sparse.sh                   |  1 +\n t/t5506-remote-groups.sh                         |  1 +\n t/t5513-fetch-track.sh                           |  1 +\n t/t5515-fetch-merge-logic.sh                     |  1 +\n t/t5518-fetch-exit-status.sh                     |  1 +\n t/t5532-fetch-proxy.sh                           |  2 ++\n t/t5900-repo-selection.sh                        |  2 ++\n t/t6002-rev-list-bisect.sh                       |  1 +\n t/t6003-rev-list-topo-order.sh                   |  1 +\n t/t6018-rev-list-glob.sh                         |  1 +\n t/t6019-rev-list-ancestry-path.sh                |  1 +\n t/t6100-rev-list-in-order.sh                     |  1 +\n t/t6101-rev-parse-parents.sh                     |  1 +\n t/t6114-keep-packs.sh                            |  2 ++\n t/t7702-repack-cyclic-alternate.sh               |  2 ++\n t/t9100-git-svn-basic.sh                         |  1 +\n t/t9101-git-svn-props.sh                         |  2 ++\n t/t9104-git-svn-follow-parent.sh                 |  2 ++\n t/t9106-git-svn-commit-diff-clobber.sh           |  2 ++\n t/t9122-git-svn-author.sh                        |  2 ++\n t/t9129-git-svn-i18n-commitencoding.sh           |  1 +\n t/t9139-git-svn-non-utf8-commitencoding.sh       |  1 +\n t/t9148-git-svn-propset.sh                       |  1 +\n t/t9151-svn-mergeinfo.sh                         |  1 +\n t/t9157-git-svn-fetch-merge.sh                   |  2 ++\n t/t9159-git-svn-no-parent-mergeinfo.sh           |  2 ++\n t/t9161-git-svn-mergeinfo-push.sh                |  1 +\n t/t9162-git-svn-dcommit-interactive.sh           |  2 ++\n t/t9163-git-svn-reset-clears-caches.sh           |  1 +\n t/t9165-git-svn-fetch-merge-branch-of-branch.sh  |  2 ++\n t/t9166-git-svn-fetch-merge-branch-of-branch2.sh |  2 ++\n 41 files changed, 71 insertions(+)\n\ndiff --git a/revision.c b/revision.c\nindex 2050a691683..6c83a715dee 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2926,6 +2926,17 @@ static void release_revisions_commit_list(struct rev_info *revs)\n \trevs->commits = NULL;\n }\n \n+static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)\n+{\n+\tunsigned int i;\n+\n+\tif (!cmdline)\n+\t\treturn;\n+\tfor (i = 0; i < cmdline->nr; i++)\n+\t\tfree((char *)cmdline->rev[i].name);\n+\tFREE_AND_NULL(cmdline->rev);\n+}\n+\n static void release_revisions_mailmap(struct string_list *mailmap)\n {\n \tif (!mailmap)\n@@ -2940,6 +2951,7 @@ void release_revisions(struct rev_info *revs)\n \t\treturn;\n \trelease_revisions_commit_list(revs);\n \tobject_array_clear(&revs->pending);\n+\trelease_revisions_cmdline(&revs->cmdline);\n \trelease_revisions_mailmap(revs->mailmap);\n }\n \ndiff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh\nindex 2fde2353fd3..ea28971e8ee 100644\n--- a/t/lib-git-svn.sh\n+++ b/t/lib-git-svn.sh\n@@ -1,3 +1,7 @@\n+if test -z \"$TEST_FAILS_SANITIZE_LEAK\"\n+then\n+\tTEST_PASSES_SANITIZE_LEAK=true\n+fi\n . ./test-lib.sh\n \n if test -n \"$NO_SVN_TESTS\"\ndiff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh\nindex 8e215867b8c..b9480c81781 100755\n--- a/t/t0062-revision-walking.sh\n+++ b/t/t0062-revision-walking.sh\n@@ -5,6 +5,7 @@\n \n test_description='Test revision walking api'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n cat >run_twice_expected <<-EOF\ndiff --git a/t/t0101-at-syntax.sh b/t/t0101-at-syntax.sh\nindex a1998b558f9..878aadd64c9 100755\n--- a/t/t0101-at-syntax.sh\n+++ b/t/t0101-at-syntax.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='various @{whatever} syntax tests'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh\nindex bc89371f534..5b8e47e346c 100755\n--- a/t/t1060-object-corruption.sh\n+++ b/t/t1060-object-corruption.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='see how we handle various forms of corruption'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # convert \"1234abcd\" to \".git/objects/12/34abcd\"\ndiff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh\nindex a9721215fab..9425aae6395 100755\n--- a/t/t2015-checkout-unborn.sh\n+++ b/t/t2015-checkout-unborn.sh\n@@ -4,6 +4,7 @@ test_description='checkout from unborn branch'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh\nindex cde3562e3a6..7b4607d72f2 100755\n--- a/t/t3408-rebase-multi-line.sh\n+++ b/t/t3408-rebase-multi-line.sh\n@@ -5,6 +5,7 @@ test_description='rebasing a commit with multi-line first paragraph.'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh\nindex b8709424981..36ac6aff1e4 100755\n--- a/t/t4207-log-decoration-colors.sh\n+++ b/t/t4207-log-decoration-colors.sh\n@@ -8,6 +8,7 @@ test_description='Test for \"git log --decorate\" colors'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh\nindex 0244888a5a7..30a219894bb 100755\n--- a/t/t4212-log-corrupt.sh\n+++ b/t/t4212-log-corrupt.sh\n@@ -2,6 +2,7 @@\n \n test_description='git log with invalid commit headers'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh\nindex 535313e4dc8..cc4cfaa9d37 100755\n--- a/t/t5313-pack-bounds-checks.sh\n+++ b/t/t5313-pack-bounds-checks.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='bounds-checking of access to mmapped on-disk file formats'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n clear_base () {\ndiff --git a/t/t5322-pack-objects-sparse.sh b/t/t5322-pack-objects-sparse.sh\nindex d39958c066d..770695c9278 100755\n--- a/t/t5322-pack-objects-sparse.sh\n+++ b/t/t5322-pack-objects-sparse.sh\n@@ -4,6 +4,7 @@ test_description='pack-objects object selection using sparse algorithm'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup repo' '\ndiff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh\nindex 8f150c0793e..5bac03ede81 100755\n--- a/t/t5506-remote-groups.sh\n+++ b/t/t5506-remote-groups.sh\n@@ -4,6 +4,7 @@ test_description='git remote group handling'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n mark() {\ndiff --git a/t/t5513-fetch-track.sh b/t/t5513-fetch-track.sh\nindex 65d1e05bd62..c46c4dbaefc 100755\n--- a/t/t5513-fetch-track.sh\n+++ b/t/t5513-fetch-track.sh\n@@ -2,6 +2,7 @@\n \n test_description='fetch follows remote-tracking branches correctly'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh\nindex 320d26796d2..c100a809c5e 100755\n--- a/t/t5515-fetch-merge-logic.sh\n+++ b/t/t5515-fetch-merge-logic.sh\n@@ -14,6 +14,7 @@ export GIT_TEST_PROTOCOL_VERSION\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n build_script () {\ndiff --git a/t/t5518-fetch-exit-status.sh b/t/t5518-fetch-exit-status.sh\nindex 5c4ac2556e7..c13120088fa 100755\n--- a/t/t5518-fetch-exit-status.sh\n+++ b/t/t5518-fetch-exit-status.sh\n@@ -8,6 +8,7 @@ test_description='fetch exit status test'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh\nindex 9c2798603b4..d664912799b 100755\n--- a/t/t5532-fetch-proxy.sh\n+++ b/t/t5532-fetch-proxy.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='fetching via git:// using core.gitproxy'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup remote repo' '\ndiff --git a/t/t5900-repo-selection.sh b/t/t5900-repo-selection.sh\nindex 14e59c5b3e4..a84faac242d 100755\n--- a/t/t5900-repo-selection.sh\n+++ b/t/t5900-repo-selection.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='selecting remote repo in ambiguous cases'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n reset() {\ndiff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh\nindex b95a0212adf..162cf50778d 100755\n--- a/t/t6002-rev-list-bisect.sh\n+++ b/t/t6002-rev-list-bisect.sh\n@@ -4,6 +4,7 @@\n #\n test_description='Tests git rev-list --bisect functionality'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-t6000.sh # t6xxx specific functions\n \ndiff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh\nindex 24d1836f417..1f7d7dd20c1 100755\n--- a/t/t6003-rev-list-topo-order.sh\n+++ b/t/t6003-rev-list-topo-order.sh\n@@ -5,6 +5,7 @@\n \n test_description='Tests git rev-list --topo-order functionality'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-t6000.sh # t6xxx specific functions\n \ndiff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh\nindex 24b34add833..e1abc5c2b32 100755\n--- a/t/t6018-rev-list-glob.sh\n+++ b/t/t6018-rev-list-glob.sh\n@@ -5,6 +5,7 @@ test_description='rev-list/rev-parse --glob'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n commit () {\ndiff --git a/t/t6019-rev-list-ancestry-path.sh b/t/t6019-rev-list-ancestry-path.sh\nindex af57a04b7ff..66f787606ec 100755\n--- a/t/t6019-rev-list-ancestry-path.sh\n+++ b/t/t6019-rev-list-ancestry-path.sh\n@@ -24,6 +24,7 @@ test_description='--ancestry-path'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_merge () {\ndiff --git a/t/t6100-rev-list-in-order.sh b/t/t6100-rev-list-in-order.sh\nindex e934bc239c5..88ed7bd75a7 100755\n--- a/t/t6100-rev-list-in-order.sh\n+++ b/t/t6100-rev-list-in-order.sh\n@@ -2,6 +2,7 @@\n \n test_description='rev-list testing in-commit-order'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup a commit history with trees, blobs' '\ndiff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh\nindex c571fa51797..a3a41c7a3e4 100755\n--- a/t/t6101-rev-parse-parents.sh\n+++ b/t/t6101-rev-parse-parents.sh\n@@ -8,6 +8,7 @@ test_description='Test git rev-parse with different parent options'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_cmp_rev_output () {\ndiff --git a/t/t6114-keep-packs.sh b/t/t6114-keep-packs.sh\nindex 9239d8aa46d..44246f8a63e 100755\n--- a/t/t6114-keep-packs.sh\n+++ b/t/t6114-keep-packs.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='rev-list with .keep packs'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t7702-repack-cyclic-alternate.sh b/t/t7702-repack-cyclic-alternate.sh\nindex 93b74867ac8..f3cdb98eec2 100755\n--- a/t/t7702-repack-cyclic-alternate.sh\n+++ b/t/t7702-repack-cyclic-alternate.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='repack involving cyclic alternate'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh\nindex fea41b3c360..7c5b847f584 100755\n--- a/t/t9100-git-svn-basic.sh\n+++ b/t/t9100-git-svn-basic.sh\n@@ -8,6 +8,7 @@ test_description='git svn basic tests'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n prepare_utf8_locale\ndiff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh\nindex 8b5681dd68a..d043e80fc34 100755\n--- a/t/t9101-git-svn-props.sh\n+++ b/t/t9101-git-svn-props.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn property tests'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n mkdir import\ndiff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh\nindex c7d8e0bf00f..5cf2ef4b8b0 100755\n--- a/t/t9104-git-svn-follow-parent.sh\n+++ b/t/t9104-git-svn-follow-parent.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn fetching'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh\nindex aec45bca3b7..3cab0b9720a 100755\n--- a/t/t9106-git-svn-commit-diff-clobber.sh\n+++ b/t/t9106-git-svn-commit-diff-clobber.sh\n@@ -2,6 +2,8 @@\n #\n # Copyright (c) 2006 Eric Wong\n test_description='git svn commit-diff clobber'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh\nindex 9e8fe38e7ef..527ba3d2932 100755\n--- a/t/t9122-git-svn-author.sh\n+++ b/t/t9122-git-svn-author.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='git svn authorship'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup svn repository' '\ndiff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh\nindex 01e1e8a8f76..185248a4cd7 100755\n--- a/t/t9129-git-svn-i18n-commitencoding.sh\n+++ b/t/t9129-git-svn-i18n-commitencoding.sh\n@@ -4,6 +4,7 @@\n \n test_description='git svn honors i18n.commitEncoding in config'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n compare_git_head_with () {\ndiff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh\nindex 22d80b0be2b..b7f756b2b7f 100755\n--- a/t/t9139-git-svn-non-utf8-commitencoding.sh\n+++ b/t/t9139-git-svn-non-utf8-commitencoding.sh\n@@ -4,6 +4,7 @@\n \n test_description='git svn refuses to dcommit non-UTF8 messages'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n # ISO-2022-JP can pass for valid UTF-8, so skipping that in this test\ndiff --git a/t/t9148-git-svn-propset.sh b/t/t9148-git-svn-propset.sh\nindex aebb28995e5..6cc76a07b39 100755\n--- a/t/t9148-git-svn-propset.sh\n+++ b/t/t9148-git-svn-propset.sh\n@@ -5,6 +5,7 @@\n \n test_description='git svn propset tests'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup propset via import' '\ndiff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh\nindex c93a5beab25..85221d439bd 100755\n--- a/t/t9151-svn-mergeinfo.sh\n+++ b/t/t9151-svn-mergeinfo.sh\n@@ -5,6 +5,7 @@\n \n test_description='git-svn svn mergeinfo properties'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'load svn dump' \"\ndiff --git a/t/t9157-git-svn-fetch-merge.sh b/t/t9157-git-svn-fetch-merge.sh\nindex 991d2aa1be6..4d8cfdf8446 100755\n--- a/t/t9157-git-svn-fetch-merge.sh\n+++ b/t/t9157-git-svn-fetch-merge.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn merge detection'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n svn_ver=\"$(svn --version --quiet)\"\ndiff --git a/t/t9159-git-svn-no-parent-mergeinfo.sh b/t/t9159-git-svn-no-parent-mergeinfo.sh\nindex 69e4815781c..ce42487a507 100755\n--- a/t/t9159-git-svn-no-parent-mergeinfo.sh\n+++ b/t/t9159-git-svn-no-parent-mergeinfo.sh\n@@ -1,5 +1,7 @@\n #!/bin/sh\n test_description='git svn handling of root commits in merge ranges'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n svn_ver=\"$(svn --version --quiet)\"\ndiff --git a/t/t9161-git-svn-mergeinfo-push.sh b/t/t9161-git-svn-mergeinfo-push.sh\nindex f113acaa6c2..89f2518685c 100755\n--- a/t/t9161-git-svn-mergeinfo-push.sh\n+++ b/t/t9161-git-svn-mergeinfo-push.sh\n@@ -6,6 +6,7 @@\n \n test_description='git-svn svn mergeinfo propagation'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'load svn dump' \"\ndiff --git a/t/t9162-git-svn-dcommit-interactive.sh b/t/t9162-git-svn-dcommit-interactive.sh\nindex e38d9fa37b5..e2aa8ed88a9 100755\n--- a/t/t9162-git-svn-dcommit-interactive.sh\n+++ b/t/t9162-git-svn-dcommit-interactive.sh\n@@ -3,6 +3,8 @@\n # Copyright (c) 2011 Frédéric Heitzmann\n \n test_description='git svn dcommit --interactive series'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9163-git-svn-reset-clears-caches.sh b/t/t9163-git-svn-reset-clears-caches.sh\nindex 978eb62ff4f..e457cf28153 100755\n--- a/t/t9163-git-svn-reset-clears-caches.sh\n+++ b/t/t9163-git-svn-reset-clears-caches.sh\n@@ -7,6 +7,7 @@ test_description='git svn reset clears memoized caches'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n svn_ver=\"$(svn --version --quiet)\"\ndiff --git a/t/t9165-git-svn-fetch-merge-branch-of-branch.sh b/t/t9165-git-svn-fetch-merge-branch-of-branch.sh\nindex a4813c2b09c..58b736dc132 100755\n--- a/t/t9165-git-svn-fetch-merge-branch-of-branch.sh\n+++ b/t/t9165-git-svn-fetch-merge-branch-of-branch.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn merge detection'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n svn_ver=\"$(svn --version --quiet)\"\ndiff --git a/t/t9166-git-svn-fetch-merge-branch-of-branch2.sh b/t/t9166-git-svn-fetch-merge-branch-of-branch2.sh\nindex 52f2e46a5b4..9f7deb26d0e 100755\n--- a/t/t9166-git-svn-fetch-merge-branch-of-branch2.sh\n+++ b/t/t9166-git-svn-fetch-merge-branch-of-branch2.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn merge detection'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n svn_ver=\"$(svn --version --quiet)\"\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450857","messageId":"patch-18.24-fc486e1bc46-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 18/24] revisions API: have release_revisions() release \"grep_filter\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:48Z","receivedAt":"2022-03-09T13:18:44Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"grep_filter\" in the \"struct ref_info\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/revision.c b/revision.c\nindex 6c83a715dee..f51a60b2a96 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2953,6 +2953,7 @@ void release_revisions(struct rev_info *revs)\n \tobject_array_clear(&revs->pending);\n \trelease_revisions_cmdline(&revs->cmdline);\n \trelease_revisions_mailmap(revs->mailmap);\n+\tfree_grep_patterns(&revs->grep_filter);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450858","messageId":"patch-16.24-1a988c96371-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 16/24] revisions API: have release_revisions() release \"mailmap\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:46Z","receivedAt":"2022-03-09T13:18:45Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"prune_data\" in the \"struct ref_info\".\n\nThe log family of functions now calls the clear_mailmap() function\nadded in fa8afd18e5a (revisions API: provide and use a\nrelease_revisions(), 2021-09-19), allowing us to whitelist some tests\nwith \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nUnfortunately having a pointer to a mailmap in \"struct rev_info\"\ninstead of an embedded member that we \"own\" get a bit messy, as can be\nseen in the change to builtin/commit.c.\n\nWhen we free() this data we won't be able to tell apart a pointer to a\n\"mailmap\" on the heap from one on the stack. As seen in\nea57bc0d41b (log: add --use-mailmap option, 2013-01-05) the \"log\"\nfamily allocates it on the heap, but in the find_author_by_nickname()\ncode added in ea16794e430 (commit: search author pattern against\nmailmap, 2013-08-23) we allocated it on the stack instead.\n\nIdeally we'd simply change that member to a \"struct string_list\nmailmap\" and never free() the \"mailmap\" itself, but that would be a\nmuch larger change to the revisions API.\n\nWe have code that needs to hand an existing \"mailmap\" to a \"struct\nrev_info\", while we could change all of that, let's not go there\nnow.\n\nThe complexity isn't in the ownership of the \"mailmap\" per-se, but\nthat various things assume a \"rev_info.mailmap == NULL\" means \"doesn't\nwant mailmap\", if we changed that to an init'd \"struct string_list\nwe'd need to carefully refactor things to change those assumptions.\n\nLet's instead always free() it, and simply declare that if you add\nsuch a \"mailmap\" it must be allocated on the heap. Any modern libc\nwill correctly panic if we free() a stack variable, so this should be\nsafe going forward.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/commit.c                   | 6 +++---\n revision.c                         | 9 +++++++++\n t/t0056-git-C.sh                   | 1 +\n t/t4055-diff-context.sh            | 1 +\n t/t4066-diff-emit-delay.sh         | 1 +\n t/t7008-filter-branch-null-sha1.sh | 2 ++\n 6 files changed, 17 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 74388a6b9a1..9a6f38db165 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1097,7 +1097,7 @@ static const char *find_author_by_nickname(const char *name)\n \tstruct rev_info revs;\n \tstruct commit *commit;\n \tstruct strbuf buf = STRBUF_INIT;\n-\tstruct string_list mailmap = STRING_LIST_INIT_NODUP;\n+\tstruct string_list *mailmap = xmalloc(sizeof(struct string_list));\n \tconst char *av[20];\n \tint ac = 0;\n \n@@ -1108,7 +1108,8 @@ static const char *find_author_by_nickname(const char *name)\n \tav[++ac] = buf.buf;\n \tav[++ac] = NULL;\n \tsetup_revisions(ac, av, &revs, NULL);\n-\trevs.mailmap = &mailmap;\n+\tstring_list_init_nodup(mailmap);\n+\trevs.mailmap = mailmap;\n \tread_mailmap(revs.mailmap);\n \n \tif (prepare_revision_walk(&revs))\n@@ -1119,7 +1120,6 @@ static const char *find_author_by_nickname(const char *name)\n \t\tctx.date_mode.type = DATE_NORMAL;\n \t\tstrbuf_release(&buf);\n \t\tformat_commit_message(commit, \"%aN <%aE>\", &buf, &ctx);\n-\t\tclear_mailmap(&mailmap);\n \t\trelease_revisions(&revs);\n \t\treturn strbuf_detach(&buf, NULL);\n \t}\ndiff --git a/revision.c b/revision.c\nindex 475901d77e3..2050a691683 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2926,12 +2926,21 @@ static void release_revisions_commit_list(struct rev_info *revs)\n \trevs->commits = NULL;\n }\n \n+static void release_revisions_mailmap(struct string_list *mailmap)\n+{\n+\tif (!mailmap)\n+\t\treturn;\n+\tclear_mailmap(mailmap);\n+\tfree(mailmap);\n+}\n+\n void release_revisions(struct rev_info *revs)\n {\n \tif (!revs)\n \t\treturn;\n \trelease_revisions_commit_list(revs);\n \tobject_array_clear(&revs->pending);\n+\trelease_revisions_mailmap(revs->mailmap);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\ndiff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh\nindex 2630e756dab..752aa8c9454 100755\n--- a/t/t0056-git-C.sh\n+++ b/t/t0056-git-C.sh\n@@ -2,6 +2,7 @@\n \n test_description='\"-C <path>\" option and its effects on other path-related options'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success '\"git -C <path>\" runs git from the directory <path>' '\ndiff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh\nindex 741e0803c1a..73048d0a526 100755\n--- a/t/t4055-diff-context.sh\n+++ b/t/t4055-diff-context.sh\n@@ -5,6 +5,7 @@\n \n test_description='diff.context configuration'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t4066-diff-emit-delay.sh b/t/t4066-diff-emit-delay.sh\nindex a1de63b77f8..0ecb3915412 100755\n--- a/t/t4066-diff-emit-delay.sh\n+++ b/t/t4066-diff-emit-delay.sh\n@@ -4,6 +4,7 @@ test_description='test combined/stat/moved interaction'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # This test covers a weird 3-way interaction between \"--cc -p\", which will run\ndiff --git a/t/t7008-filter-branch-null-sha1.sh b/t/t7008-filter-branch-null-sha1.sh\nindex 9ba9f24ad2f..0ce8fd2c895 100755\n--- a/t/t7008-filter-branch-null-sha1.sh\n+++ b/t/t7008-filter-branch-null-sha1.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='filter-branch removal of trees with null sha1'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup: base commits' '\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450859","messageId":"patch-12.24-8811a5ca840-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 12/24] revisions API users: use release_revisions() in submodule.c edge case","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:42Z","receivedAt":"2022-03-09T13:18:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use release_revisions() on the the \"struct rev_info\" in\nshow_submodule_diff_summary() where we need to pre-initialize with \"{\n0 }\" to make sure that we won't segfault when calling\nrelease_revisions().\n\nMost functions that use \"struct rev_info\" will call\nrepo_init_revisions() early, which will do an equivalent memset(). In\nthis case we might \"goto out\" before doing the equivalent of that\noperation, see the \"goto\" on \"(!left || !right || !sub)\" added in\n8e6df65015f (submodule: refactor show_submodule_summary with helper\nfunction, 2016-08-31).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n submodule.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/submodule.c b/submodule.c\nindex 0510cb193b6..285c1896c9e 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -638,7 +638,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,\n \t\tstruct object_id *one, struct object_id *two,\n \t\tunsigned dirty_submodule)\n {\n-\tstruct rev_info rev;\n+\tstruct rev_info rev = { 0 };\n \tstruct commit *left = NULL, *right = NULL;\n \tstruct commit_list *merge_bases = NULL;\n \tstruct repository *sub;\n@@ -664,6 +664,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,\n \tprint_submodule_diff_summary(sub, &rev, o);\n \n out:\n+\trelease_revisions(&rev);\n \tif (merge_bases)\n \t\tfree_commit_list(merge_bases);\n \tclear_commit_marks(left, ~0);\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450860","messageId":"patch-13.24-d18bfa5691f-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 13/24] revisions API users: use release_revisions() in wt-status.c edge case","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:43Z","receivedAt":"2022-03-09T13:18:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use release_revisions() for the \"struct rev_info\" in\nhas_unstaged_changes() which didn't require the addition of a \"goto\"\npattern, but needed us to refactor another \"return fn(...)\" pattern.\n\nThis change is split from preceding changes to make similar\nconversions in those commits easier to review, as they are all\nrepetitions of the same pattern.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n wt-status.c | 4 +++-\n 1 file changed, 3 insertions(+), 1 deletion(-)\n\ndiff --git a/wt-status.c b/wt-status.c\nindex a68731ea0f7..89289e52603 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -2537,7 +2537,9 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)\n \trev_info.diffopt.flags.quick = 1;\n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_files(&rev_info, 0);\n-\treturn diff_result_code(&rev_info.diffopt, result);\n+\tresult = diff_result_code(&rev_info.diffopt, result);\n+\trelease_revisions(&rev_info);\n+\treturn result;\n }\n \n /**\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450861","messageId":"patch-22.24-f387b9e744a-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 22/24] revisions API: call diff_free(&revs->pruning) in revisions_release()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:52Z","receivedAt":"2022-03-09T13:18:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Call diff_free() on the \"pruning\" member of \"struct rev_info\". We'll\nhandle the \"diffopt\" member of the same type some other time, doing so\nwill require us to untangle the \"no_free\" conditions I added in\ne900d494dcf (diff: add an API for deferred freeing, 2021-02-11).\n\nEven without \"diffopt\" addressing leaks in \"pruning\" makes several\ntests pass under SANITIZE=leak.\n\nThis was also the last missing piece that allows us to remove the\nUNLEAK() in \"cmd_diff\" and \"cmd_diff_index\", which allows us to use\nthose commands as a canary for general leaks in the revisions API. See\n[1] for further rationale, and 886e1084d78 (builtin/: add UNLEAKs,\n2017-10-01) for the commit that added the UNLEAK() there.\n\n1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/diff-index.c                | 1 -\n builtin/diff.c                      | 1 -\n revision.c                          | 2 ++\n t/t2016-checkout-patch.sh           | 1 +\n t/t4039-diff-assume-unchanged.sh    | 1 +\n t/t4206-log-follow-harder-copies.sh | 2 ++\n t/t6131-pathspec-icase.sh           | 2 ++\n 7 files changed, 8 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/diff-index.c b/builtin/diff-index.c\nindex 3a83183c312..7d158af6b6d 100644\n--- a/builtin/diff-index.c\n+++ b/builtin/diff-index.c\n@@ -70,7 +70,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)\n \t\treturn -1;\n \t}\n \tresult = run_diff_index(&rev, option);\n-\tUNLEAK(rev);\n \tresult = diff_result_code(&rev.diffopt, result);\n \trelease_revisions(&rev);\n \treturn result;\ndiff --git a/builtin/diff.c b/builtin/diff.c\nindex dd48336da56..f539132ac68 100644\n--- a/builtin/diff.c\n+++ b/builtin/diff.c\n@@ -594,7 +594,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \tresult = diff_result_code(&rev.diffopt, result);\n \tif (1 < rev.diffopt.skip_stat_unmatch)\n \t\trefresh_index_quietly();\n-\tUNLEAK(rev);\n \trelease_revisions(&rev);\n \tUNLEAK(ent);\n \tUNLEAK(blob);\ndiff --git a/revision.c b/revision.c\nindex ec6cd662467..81f4a36fc9d 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2956,6 +2956,8 @@ void release_revisions(struct rev_info *revs)\n \tclear_pathspec(&revs->prune_data);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n+\t/* TODO (need to handle \"no_free\"): diff_free(&revs->diffopt) */\n+\tdiff_free(&revs->pruning);\n \treflog_walk_info_release(revs->reflog_info);\n }\n \ndiff --git a/t/t2016-checkout-patch.sh b/t/t2016-checkout-patch.sh\nindex abfd586c32b..83c246ab0a5 100755\n--- a/t/t2016-checkout-patch.sh\n+++ b/t/t2016-checkout-patch.sh\n@@ -2,6 +2,7 @@\n \n test_description='git checkout --patch'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./lib-patch-mode.sh\n \n test_expect_success PERL 'setup' '\ndiff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh\nindex 0eb0314a8b3..78090e6852d 100755\n--- a/t/t4039-diff-assume-unchanged.sh\n+++ b/t/t4039-diff-assume-unchanged.sh\n@@ -2,6 +2,7 @@\n \n test_description='diff with assume-unchanged entries'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # external diff has been tested in t4020-diff-external.sh\ndiff --git a/t/t4206-log-follow-harder-copies.sh b/t/t4206-log-follow-harder-copies.sh\nindex 4871a5dc92f..33ecf82c7f9 100755\n--- a/t/t4206-log-follow-harder-copies.sh\n+++ b/t/t4206-log-follow-harder-copies.sh\n@@ -6,6 +6,8 @@\n test_description='Test --follow should always find copies hard in git log.\n \n '\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-diff.sh\n \ndiff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh\nindex 39fc3f6769b..770cce026cb 100755\n--- a/t/t6131-pathspec-icase.sh\n+++ b/t/t6131-pathspec-icase.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='test case insensitive pathspec limiting'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n if test_have_prereq CASE_INSENSITIVE_FS\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450862","messageId":"patch-23.24-fd457f0d85c-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 23/24] revisions API: have release_revisions() release \"date_mode\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:53Z","receivedAt":"2022-03-09T13:18:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"date_mode\" in the \"struct ref_info\".\n\nThis uses the date_mode_release() function added in 974c919d36d (date\nAPI: add and use a date_mode_release(), 2022-02-16). As that commit\nnotes \"t7004-tag.sh\" tests for the leaks that are being fixed\nhere. That test now fails \"only\" 44 tests, instead of the 46 it failed\nbefore this change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/revision.c b/revision.c\nindex 81f4a36fc9d..9d03e7805ff 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2954,6 +2954,7 @@ void release_revisions(struct rev_info *revs)\n \trelease_revisions_cmdline(&revs->cmdline);\n \tobject_array_clear(&revs->boundary_commits);\n \tclear_pathspec(&revs->prune_data);\n+\tdate_mode_release(&revs->date_mode);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n \t/* TODO (need to handle \"no_free\"): diff_free(&revs->diffopt) */\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450863","messageId":"patch-24.24-e02a097b884-20220309T123321Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH 24/24] revisions API: have release_revisions() release \"topo_walk_info\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T13:16:54Z","receivedAt":"2022-03-09T13:18:55Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor the existing reset_topo_walk() into a thin wrapper for a\nrelease_revisions_topo_walk_info() + resetting the member to \"NULL\",\nand call release_revisions_topo_walk_info() from release_revisions().\n\nThis fixes memory leaks that have been with us ever since\n\"topo_walk_info\" was added to revision.[ch] in\nf0d9cc4196a (revision.c: begin refactoring --topo-order logic,\n2018-11-01).\n\nDue to various other leaks this makes no tests pass in their entirety,\nbut e.g. before this running this on git.git:\n\n    ./git -P log --pretty=tformat:\"%P   %H | %s\" --parents --full-history --topo-order -3 -- README.md\n\nWould report under SANITIZE=leak:\n\n    SUMMARY: LeakSanitizer: 531064 byte(s) leaked in 6 allocation(s).\n\nNow we'll free all of that memory.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 18 ++++++++++++++----\n 1 file changed, 14 insertions(+), 4 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex 9d03e7805ff..5a9400667df 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2945,6 +2945,8 @@ static void release_revisions_mailmap(struct string_list *mailmap)\n \tfree(mailmap);\n }\n \n+static void release_revisions_topo_walk_info(struct topo_walk_info *info);\n+\n void release_revisions(struct rev_info *revs)\n {\n \tif (!revs)\n@@ -2960,6 +2962,7 @@ void release_revisions(struct rev_info *revs)\n \t/* TODO (need to handle \"no_free\"): diff_free(&revs->diffopt) */\n \tdiff_free(&revs->pruning);\n \treflog_walk_info_release(revs->reflog_info);\n+\trelease_revisions_topo_walk_info(revs->topo_walk_info);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n@@ -3472,17 +3475,24 @@ static void compute_indegrees_to_depth(struct rev_info *revs,\n \t\tindegree_walk_step(revs);\n }\n \n-static void reset_topo_walk(struct rev_info *revs)\n+static void release_revisions_topo_walk_info(struct topo_walk_info *info)\n {\n-\tstruct topo_walk_info *info = revs->topo_walk_info;\n-\n+\tif (!info)\n+\t\treturn;\n \tclear_prio_queue(&info->explore_queue);\n \tclear_prio_queue(&info->indegree_queue);\n \tclear_prio_queue(&info->topo_queue);\n \tclear_indegree_slab(&info->indegree);\n \tclear_author_date_slab(&info->author_date);\n+\tfree(info);\n+}\n+\n+static void reset_topo_walk(struct rev_info *revs)\n+{\n+\tstruct topo_walk_info *info = revs->topo_walk_info;\n \n-\tFREE_AND_NULL(revs->topo_walk_info);\n+\trelease_revisions_topo_walk_info(info);\n+\trevs->topo_walk_info = NULL;\n }\n \n static void init_topo_walk(struct rev_info *revs)\n-- \n2.35.1.1295.g6b025d3e231\n\n"},{"id":"450873","messageId":"77c8ef4b-5dce-401b-e703-cfa32e18c853@github.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"Re: [PATCH 00/24] revision.[ch]: add and use release_revisions()","fromName":"Derrick Stolee","fromEmail":"derrickstolee@github.com","sentAt":"2022-03-09T14:34:55Z","receivedAt":"2022-03-09T14:35:02Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:\n> == For Junio ==\n> \n> This has a trivially resolved conflict with Derrick Stolee's\n> aaf251cdc5c (revision: put object filter into struct rev_info,\n> 2022-03-08) currently in \"seen\" in builtin/rev-list.c.\n> \n> The resolution is to just keep the \"goto cleanup\" in place of \"return\n> 0\" in the conflicting lines, but to otherwise keep Derrick's version.\n> \n> It will pass with/without SANITIZE=leak when applied to both \"master\"\n> and \"seen\". I omitted one test change (described in a relevant commit\n> message) due to the latter not being true (no fault of \"seen\", just a\n> new leaking command being added to a test).\n\nSince ds/partial-bundles will soon be updated in v4 to change the\npointer added to struct rev_info, it is even more likely that there\nwill be more important things to do with regards to clearing the\nmemory of rev_infos based on that change. It might be better to wait\nfor that update (coming soon) and then rebase directly on top.\n\nThanks,\n-Stolee\n\n"},{"id":"450907","messageId":"3a89df25-8bfe-ba17-415c-3e2f14e17d61@github.com","threadId":"57525","inReplyTo":"patch-08.24-0f9d313b338-20220309T123321Z-avarab@gmail.com","subject":"Re: [PATCH 08/24] revisions API users: add \"goto cleanup\" for release_revisions()","fromName":"Derrick Stolee","fromEmail":"derrickstolee@github.com","sentAt":"2022-03-09T19:08:54Z","receivedAt":"2022-03-09T19:09:01Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:\n> Add a release_revisions() to various users of \"struct rev_list\" which\n> requires a minor refactoring to a \"goto cleanup\" pattern to use that\n> function.\n\nA straight-forward refactor. I have a couple nits on this one.\n\n>  static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,\n> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\n> index 09db2620829..19393da4e31 100644\n> --- a/builtin/submodule--helper.c\n> +++ b/builtin/submodule--helper.c\n> @@ -1222,6 +1222,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n>  \tstruct strvec diff_args = STRVEC_INIT;\n>  \tstruct rev_info rev;\n>  \tstruct module_cb_list list = MODULE_CB_LIST_INIT;\n> +\tint ret = 0;\n\nSince we initialize ret = 0 here...\n\n> @@ -1259,9 +1262,11 @@ static int compute_summary_module_list(struct object_id *head_oid,\n>  \telse\n>  \t\trun_diff_files(&rev, 0);\n>  \tprepare_submodule_summary(info, &list);\n> +\tret = 0;\n\n... this is extraneous.\n\n>  \t\tdie(_(\"unable to write %s\"), get_index_file());\n> -\n> +\tret = (result.clean == 0);\n> +cleanup:\n\nHere (and other places) I feel the loss of this empty line\nabove the goto label. It just seems helpful to help see\nthese labels a little more clearly if they have an empty\nline first, instead of immediately following a statement.\n\nThanks,\n-Stolee\n"},{"id":"450908","messageId":"50873925-a5bc-7df8-1407-371eb904e38e@github.com","threadId":"57525","inReplyTo":"patch-10.24-a89f0da4fd7-20220309T123321Z-avarab@gmail.com","subject":"Re: [PATCH 10/24] revisions API users: use release_revisions() in builtin/log.c","fromName":"Derrick Stolee","fromEmail":"derrickstolee@github.com","sentAt":"2022-03-09T19:12:15Z","receivedAt":"2022-03-09T19:12:21Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:\n\n> +static int cmd_log_deinit(int ret, struct rev_info *rev)\n> +{\n> +\trelease_revisions(rev);\n> +\treturn ret;\n> +}\n\nThis pattern of passing a return value through the helper\nfunction is a clever way to get around adding \"int ret = ...;\nrelease(); return ret;\" lines.\n\nThanks,\n-Stolee\n"},{"id":"450911","messageId":"bed6917e-105e-e19f-2fd9-201507fc8a26@github.com","threadId":"57525","inReplyTo":"patch-16.24-1a988c96371-20220309T123321Z-avarab@gmail.com","subject":"Re: [PATCH 16/24] revisions API: have release_revisions() release \"mailmap\"","fromName":"Derrick Stolee","fromEmail":"derrickstolee@github.com","sentAt":"2022-03-09T19:17:01Z","receivedAt":"2022-03-09T19:18:58Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:\n\n> @@ -1097,7 +1097,7 @@ static const char *find_author_by_nickname(const char *name)\n>  \tstruct rev_info revs;\n>  \tstruct commit *commit;\n>  \tstruct strbuf buf = STRBUF_INIT;\n> -\tstruct string_list mailmap = STRING_LIST_INIT_NODUP;\n> +\tstruct string_list *mailmap = xmalloc(sizeof(struct string_list));\n>  \tconst char *av[20];\n>  \tint ac = 0;\n>  \n> @@ -1108,7 +1108,8 @@ static const char *find_author_by_nickname(const char *name)\n>  \tav[++ac] = buf.buf;\n>  \tav[++ac] = NULL;\n>  \tsetup_revisions(ac, av, &revs, NULL);\n> -\trevs.mailmap = &mailmap;\n> +\tstring_list_init_nodup(mailmap);\n> +\trevs.mailmap = mailmap;\n>  \tread_mailmap(revs.mailmap);\n>  \n>  \tif (prepare_revision_walk(&revs))\n> @@ -1119,7 +1120,6 @@ static const char *find_author_by_nickname(const char *name)\n>  \t\tctx.date_mode.type = DATE_NORMAL;\n>  \t\tstrbuf_release(&buf);\n>  \t\tformat_commit_message(commit, \"%aN <%aE>\", &buf, &ctx);\n> -\t\tclear_mailmap(&mailmap);\n\nIt seems like you can completely remove the 'mailmap' variable and\ninstead malloc and init revs.mailmap directly.\n\nThanks,\n-Stolee\n"},{"id":"450912","messageId":"4e564691-0ef6-a867-647e-c26064b0bb36@github.com","threadId":"57525","inReplyTo":"patch-17.24-b5687ed1355-20220309T123321Z-avarab@gmail.com","subject":"Re: [PATCH 17/24] revisions API: have release_revisions() release \"cmdline\"","fromName":"Derrick Stolee","fromEmail":"derrickstolee@github.com","sentAt":"2022-03-09T19:19:24Z","receivedAt":"2022-03-09T19:19:36Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:\n> Extend the the release_revisions() function so that it frees the\n> \"cmdline\" in the \"struct ref_info\". This in combination with a\n> preceding change to free \"commits\" and \"mailmap\" means that we can\n> whitelist another test under \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nCould you consider changing your vocabulary to use \"allowlist\"\n(with the opposite term being \"denylist\")?\n\nThanks,\n-Stolee\n"},{"id":"450914","messageId":"625c3868-60c9-009b-b01b-f186710d4d08@github.com","threadId":"57525","inReplyTo":"patch-20.24-fa53e81c7c0-20220309T123321Z-avarab@gmail.com","subject":"Re: [PATCH 20/24] revisions API: clear \"boundary_commits\" in release_revisions()","fromName":"Derrick Stolee","fromEmail":"derrickstolee@github.com","sentAt":"2022-03-09T19:21:07Z","receivedAt":"2022-03-09T19:21:49Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:\n> Clear the \"boundary_commits\" object_array in release_revisions(). This\n> makes a *lot* of tests pass under SANITIZE=leak, including most of the\n> t/t[0-9]*git-svn*.sh tests.\n> \n> This includes the tests we had false-positive passes on before my\n> 6798b08e848 (perl Git.pm: don't ignore signalled failure in\n> _cmd_close(), 2022-02-01), now they pass for real.\n> \n> Since there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to\n> list those that don't pass than to touch most of those 66. So let's\n> introduce a \"TEST_FAILS_SANITIZE_LEAK=true\", which if set in the tests\n> won't cause lib-git-svn.sh to set \"TEST_PASSES_SANITIZE_LEAK=true.\n\nThis paragraph perhaps belongs a few patches earlier in \"revisions\nAPI: have release_revisions() release \"cmdline\"\", or else there was\nsome swap of order here.\n\nThanks,\n-Stolee\n"},{"id":"450915","messageId":"99c429f7-af5d-78ac-9888-9f4f58d01b62@github.com","threadId":"57525","inReplyTo":"patch-21.24-ccf276641d6-20220309T123321Z-avarab@gmail.com","subject":"Re: [PATCH 21/24] revisions API: release \"reflog_info\" in release revisions()","fromName":"Derrick Stolee","fromEmail":"derrickstolee@github.com","sentAt":"2022-03-09T19:24:33Z","receivedAt":"2022-03-09T19:25:01Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:\n> Add a missing reflog_walk_info_release() to \"reflog-walk.c\" and use it\n> in release_revisions().\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  reflog-walk.c            | 26 ++++++++++++++++++++++++--\n>  reflog-walk.h            |  1 +\n>  revision.c               |  1 +\n>  t/t0100-previous.sh      |  1 +\n>  t/t1401-symbolic-ref.sh  |  2 ++\n>  t/t1411-reflog-show.sh   |  1 +\n>  t/t1412-reflog-loop.sh   |  2 ++\n>  t/t1415-worktree-refs.sh |  1 +\n>  8 files changed, 33 insertions(+), 2 deletions(-)\n> \n> diff --git a/reflog-walk.c b/reflog-walk.c\n> index 8ac4b284b6b..4322228d122 100644\n> --- a/reflog-walk.c\n> +++ b/reflog-walk.c\n> @@ -7,7 +7,7 @@\n>  #include \"reflog-walk.h\"\n>  \n>  struct complete_reflogs {\n> -\tchar *ref;\n> +\tconst char *ref;\n>  \tconst char *short_ref;\n\nThis seems like the opposite change from what I would\nexpect, because the 'const' implies non-ownership.\n\n> -\tfree(array->ref);\n> +\tfree((char *)array->ref);\n> +\tfree((char *)array->short_ref);\n\nThis further makes the point that we should be keeping\nnon-const versions so we can clearly document ownership.\n\n> +static void complete_reflogs_clear(void *util, const char *str)\n> +{\n> +\tstruct complete_reflogs *array = util;\n> +\tfree_complete_reflog(array);\n> +}\n\nIs there a reason we don't do the cast inside?\n\n\tfree_complete_reflog((struct complete_reflogs *)util);\n\nThanks,\n-Stolee\n"},{"id":"450916","messageId":"ea05408a-bf77-e03d-c367-54d4c6c3c3c6@github.com","threadId":"57525","inReplyTo":"patch-24.24-e02a097b884-20220309T123321Z-avarab@gmail.com","subject":"Re: [PATCH 24/24] revisions API: have release_revisions() release \"topo_walk_info\"","fromName":"Derrick Stolee","fromEmail":"derrickstolee@github.com","sentAt":"2022-03-09T19:27:54Z","receivedAt":"2022-03-09T19:28:34Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:\n> Refactor the existing reset_topo_walk() into a thin wrapper for a\n> release_revisions_topo_walk_info() + resetting the member to \"NULL\",\n> and call release_revisions_topo_walk_info() from release_revisions().\n> \n> This fixes memory leaks that have been with us ever since\n> \"topo_walk_info\" was added to revision.[ch] in\n> f0d9cc4196a (revision.c: begin refactoring --topo-order logic,\n> 2018-11-01).\n\nExcellent.\n\n> +static void reset_topo_walk(struct rev_info *revs)\n> +{\n> +\tstruct topo_walk_info *info = revs->topo_walk_info;\n\nWhy add this variable?\n  \n> -\tFREE_AND_NULL(revs->topo_walk_info);\n> +\trelease_revisions_topo_walk_info(info);\n\nFor only this use?\n\n\trelease_revisions_topo_walk_info(revs->topo_walk_info);\n\nseems to work just fine. Is the line too long?\n\nThanks,\n-Stolee\n"},{"id":"450918","messageId":"94242ef8-2e68-1b77-a108-2ed3604741fa@github.com","threadId":"57525","inReplyTo":"77c8ef4b-5dce-401b-e703-cfa32e18c853@github.com","subject":"Re: [PATCH 00/24] revision.[ch]: add and use release_revisions()","fromName":"Derrick Stolee","fromEmail":"derrickstolee@github.com","sentAt":"2022-03-09T19:30:26Z","receivedAt":"2022-03-09T19:30:34Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 3/9/2022 9:34 AM, Derrick Stolee wrote:\n> On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:\n>> == For Junio ==\n>>\n>> This has a trivially resolved conflict with Derrick Stolee's\n>> aaf251cdc5c (revision: put object filter into struct rev_info,\n>> 2022-03-08) currently in \"seen\" in builtin/rev-list.c.\n>>\n>> The resolution is to just keep the \"goto cleanup\" in place of \"return\n>> 0\" in the conflicting lines, but to otherwise keep Derrick's version.\n>>\n>> It will pass with/without SANITIZE=leak when applied to both \"master\"\n>> and \"seen\". I omitted one test change (described in a relevant commit\n>> message) due to the latter not being true (no fault of \"seen\", just a\n>> new leaking command being added to a test).\n> \n> Since ds/partial-bundles will soon be updated in v4 to change the\n> pointer added to struct rev_info, it is even more likely that there\n> will be more important things to do with regards to clearing the\n> memory of rev_infos based on that change. It might be better to wait\n> for that update (coming soon) and then rebase directly on top.\n\nI took a look at the series as it stands now and have a few nits\nhere and there. Generally, things are pretty standard in this kind\nof series you've been working diligently on for a while.\n\nThe only thing I can recommend is to check that your leak-check\nstatements are still true when reaching the end of the series, now\nthat the filter member exists. Likely the tests that you are marking\nas leak-free do nothing with object filters, so they will still be\ntrue. Just something to keep in mind and maybe add a patch that\nrecursively frees the contents of 'revs->filter' at the end.\n\nThanks,\n-Stolee\n"},{"id":"450921","messageId":"220309.86y21ilxox.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"94242ef8-2e68-1b77-a108-2ed3604741fa@github.com","subject":"Re: [PATCH 00/24] revision.[ch]: add and use release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-09T19:39:14Z","receivedAt":"2022-03-09T19:42:45Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Mar 09 2022, Derrick Stolee wrote:\n\n> On 3/9/2022 9:34 AM, Derrick Stolee wrote:\n>> On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:\n>>> == For Junio ==\n>>>\n>>> This has a trivially resolved conflict with Derrick Stolee's\n>>> aaf251cdc5c (revision: put object filter into struct rev_info,\n>>> 2022-03-08) currently in \"seen\" in builtin/rev-list.c.\n>>>\n>>> The resolution is to just keep the \"goto cleanup\" in place of \"return\n>>> 0\" in the conflicting lines, but to otherwise keep Derrick's version.\n>>>\n>>> It will pass with/without SANITIZE=leak when applied to both \"master\"\n>>> and \"seen\". I omitted one test change (described in a relevant commit\n>>> message) due to the latter not being true (no fault of \"seen\", just a\n>>> new leaking command being added to a test).\n>> \n>> Since ds/partial-bundles will soon be updated in v4 to change the\n>> pointer added to struct rev_info, it is even more likely that there\n>> will be more important things to do with regards to clearing the\n>> memory of rev_infos based on that change. It might be better to wait\n>> for that update (coming soon) and then rebase directly on top.\n>\n> I took a look at the series as it stands now and have a few nits\n> here and there. Generally, things are pretty standard in this kind\n> of series you've been working diligently on for a while.\n\nThanks for the review. From a quick skim I didn't see anything I needed\nto address other than with an eventual re-roll, so I'll incorporate it\ninto a v2, pending some further feedback.\n\n> The only thing I can recommend is to check that your leak-check\n> statements are still true when reaching the end of the series, now\n> that the filter member exists. Likely the tests that you are marking\n> as leak-free do nothing with object filters, so they will still be\n> true. Just something to keep in mind and maybe add a patch that\n> recursively frees the contents of 'revs->filter' at the end.\n\n*nod*. For what it's worth I tested this with 'git rebase -x' and a\nlocal patch I have that allows you to run the tests in a mode where it\nasserts that there's a 1=1 mapping between tests marked for the\nlinux-leaks job, and those that currently pass. I.e. a passing test that\nisn't opted-in via TEST_PASSES_SANITIZE_LEAK=true will fail if it passes\nwith SANITIZE=leak.\n\nI'll run the v2 with that again, and will lean on the side of just\nejecting any changes that step on the toes of other in-flight topics.\n\nI.e. as noted in the cover letter this isn't all of the revisions.[ch]\nleaks, just most of them, so I can always eject a few and leave those\nfor a follow-up series (which I already have patches for, but those are\nall a bit more complex, so I left them out for now).\n\n"},{"id":"450927","messageId":"xmqqlexiki2k.fsf@gitster.g","threadId":"57525","inReplyTo":"77c8ef4b-5dce-401b-e703-cfa32e18c853@github.com","subject":"Re: [PATCH 00/24] revision.[ch]: add and use release_revisions()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-09T20:05:23Z","receivedAt":"2022-03-09T20:05:29Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Derrick Stolee <derrickstolee@github.com> writes:\n\n> On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:\n>> == For Junio ==\n>> \n>> This has a trivially resolved conflict with Derrick Stolee's\n>> aaf251cdc5c (revision: put object filter into struct rev_info,\n>> 2022-03-08) currently in \"seen\" in builtin/rev-list.c.\n>> \n>> The resolution is to just keep the \"goto cleanup\" in place of \"return\n>> 0\" in the conflicting lines, but to otherwise keep Derrick's version.\n>> \n>> It will pass with/without SANITIZE=leak when applied to both \"master\"\n>> and \"seen\". I omitted one test change (described in a relevant commit\n>> message) due to the latter not being true (no fault of \"seen\", just a\n>> new leaking command being added to a test).\n>\n> Since ds/partial-bundles will soon be updated in v4 to change the\n> pointer added to struct rev_info, it is even more likely that there\n> will be more important things to do with regards to clearing the\n> memory of rev_infos based on that change. It might be better to wait\n> for that update (coming soon) and then rebase directly on top.\n\n;-)\n\nI just saw both of these topics involved, and indeed the\ninteractions need to be cleaned up.\n\nThanks, both, for working on these.\n\n"},{"id":"450930","messageId":"YikPXN5xobn+Y/Jn@nand.local","threadId":"57525","inReplyTo":"patch-03.24-ab1afe6c0c8-20220309T123321Z-avarab@gmail.com","subject":"Re: [PATCH 03/24] format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2022-03-09T20:34:36Z","receivedAt":"2022-03-09T20:34:41Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Wed, Mar 09, 2022 at 02:16:33PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> @@ -1946,7 +1947,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n>  \t\tstrbuf_addch(&buf, '\\n');\n>  \t}\n>\n> -\trev.extra_headers = strbuf_detach(&buf, NULL);\n> +\textra_headers = strbuf_detach(&buf, NULL);\n> +\trev.extra_headers = extra_headers;\n\nSmall nit, these two assignments could be combined on the same line\nwithout wrapping. But obviously not a big deal.\n\n> @@ -2173,8 +2175,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n>  \t\tprepare_bases(&bases, base, list, nr);\n>  \t}\n>\n> -\tif (in_reply_to || thread || cover_letter)\n> -\t\trev.ref_message_ids = xcalloc(1, sizeof(struct string_list));\n> +\tif (in_reply_to || thread || cover_letter) {\n> +\t\trev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));\n> +\t\tstring_list_init_nodup(rev.ref_message_ids);\n> +\t}\n\nOK, and this is the \"while we're at it...\" part of your commit message.\nI did find this a little confusing to read at first, so wouldn't have\nminded to see this and the rest of the change split across two patches,\nbut I think this is fine, too.\n\n> @@ -2281,6 +2285,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n>  \tstrbuf_release(&rdiff1);\n>  \tstrbuf_release(&rdiff2);\n>  \tstrbuf_release(&rdiff_title);\n> +\tfree(extra_headers);\n> +\tif (rev.ref_message_ids) {\n> +\t\tstring_list_clear(rev.ref_message_ids, 0);\n\nI was surprised to learn that string_list_clear() is not a noop when its\nfirst argument is NULL.\n\nLooking around, it probably wouldn't help that much. E.g., running\nsomething like:\n\n    git grep -B1 'string_list_clear(' | grep if -A1\n\ndoesn't turn up many results like the above. So maybe some good\n#leftoverbits there, although maybe not.\n\n> +\t\tfree(rev.ref_message_ids);\n\nThis could go in or out of the if-statement.\n\nThanks,\nTaylor\n"},{"id":"450932","messageId":"YikdBfMh++b8jPvu@nand.local","threadId":"57525","inReplyTo":"patch-05.24-b89dcadcc22-20220309T123321Z-avarab@gmail.com","subject":"Re: [PATCH 05/24] revision.[ch]: provide and start using a release_revisions()","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2022-03-09T21:32:53Z","receivedAt":"2022-03-09T21:33:00Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Wed, Mar 09, 2022 at 02:16:35PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> Let's provide a release_revisions() function for these users, and\n> start migrating them over per the plan outlined in [1]. Right now this\n> only handles the \"pending\" member of the struct, but more will be\n> added in subsequent commits.\n\nI could not be more excited about this change! The lack of easy\nmechanism to free everything in the rev_info structure has been a major\nimpediment for me to get much value out of the SANITIZE=leak builds, and\nthis is a huge step towards making those more useful.\n\nThis plan looks good to me, as does this first patch to introduce the\nnew API.\n\nShould it be documented anywhere in e.g.,\nDocumentation/MyFirstObjectWalk.txt or elsewhere?\n\nThanks,\nTaylor\n"},{"id":"450935","messageId":"Yikh0Cu3IJt16pIZ@nand.local","threadId":"57525","inReplyTo":"patch-10.24-a89f0da4fd7-20220309T123321Z-avarab@gmail.com","subject":"Re: [PATCH 10/24] revisions API users: use release_revisions() in builtin/log.c","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2022-03-09T21:53:20Z","receivedAt":"2022-03-09T21:53:27Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Wed, Mar 09, 2022 at 02:16:40PM +0100, Ævar Arnfjörð Bjarmason wrote:\n> In the case of \"format-patch\" revert the addition of UNLEAK() in\n> dee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16), and\n> which will cause several tests that previously passed under\n> \"TEST_PASSES_SANITIZE_LEAK=true\" to start failing.\n\nThanks for calling this out. When I skimmed the diff first, I wasn't\nsure whether the change in t4126 was intentional or not. But this is\nhelpful context to indicate that it was.\n\n> @@ -731,8 +737,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n>  \t\t\tret = error(_(\"unknown type: %d\"), o->type);\n>  \t\t}\n>  \t}\n> -\tfree(objects);\n> -\treturn ret;\n> +\treturn cmd_log_deinit(ret, &rev);\n>  }\n\nHere `objects` points at `rev.pending.objects`, and that is now freed\nalong the cmd_log_deinit() -> release_revision() -> object_array_clear()\npath.\n\nMakes sense, and the rest looks good, too.\n\nThanks,\nTaylor\n"},{"id":"450938","messageId":"Yiki7+BSi5sNamRf@nand.local","threadId":"57525","inReplyTo":"50873925-a5bc-7df8-1407-371eb904e38e@github.com","subject":"Re: [PATCH 10/24] revisions API users: use release_revisions() in builtin/log.c","fromName":"Taylor Blau","fromEmail":"me@ttaylorr.com","sentAt":"2022-03-09T21:58:07Z","receivedAt":"2022-03-09T21:58:12Z","isPatch":true,"sender":{"key":"me@ttaylorr.com","avatar":"https://avatars.githubusercontent.com/u/301000140?v=4"},"body":"On Wed, Mar 09, 2022 at 02:12:15PM -0500, Derrick Stolee wrote:\n> On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:\n>\n> > +static int cmd_log_deinit(int ret, struct rev_info *rev)\n> > +{\n> > +\trelease_revisions(rev);\n> > +\treturn ret;\n> > +}\n>\n> This pattern of passing a return value through the helper\n> function is a clever way to get around adding \"int ret = ...;\n> release(); return ret;\" lines.\n\nClever indeed. I originally thought that cmd_log_deinit() was going to\nbe a wrapper around release_revisions() that also freed any log-specific\nbits of the rev_info structure that are used exclusively by this file.\n\nBut that is not a great pattern, since having to track which file(s) use\nwhich field(s) of the rev_info structure sounds cumbersome, error-prone,\nand fragile. So I'm glad to see that it's really just used to\ntemporarily hold a return value.\n\nI wouldn't be sad to see this as a macro, either, maybe something like:\n\n    #define LOG_TEARDOWN(ret, rev) \\\n        do { release_revisions(rev); return ret; } while (0);\n\nto make it clear(er) that this has a pretty narrow single purpose. But\neven that doesn't seem like a great idea, since many of the uses have\nthe result of a function call as the first argument, which obviously\nwould not work with this macro.\n\nSo it's probably fine as-is ;).\n\nThanks,\nTaylor\n"},{"id":"450946","messageId":"xmqqmthylonj.fsf@gitster.g","threadId":"57525","inReplyTo":"patch-05.24-b89dcadcc22-20220309T123321Z-avarab@gmail.com","subject":"Re: [PATCH 05/24] revision.[ch]: provide and start using a release_revisions()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-09T22:57:52Z","receivedAt":"2022-03-09T22:58:05Z","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> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index 9244827ca02..ed993383531 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -629,7 +629,7 @@ static void show_local_changes(struct object *head,\n>  \tdiff_setup_done(&rev.diffopt);\n>  \tadd_pending_object(&rev, head, NULL);\n>  \trun_diff_index(&rev, 0);\n> -\tobject_array_clear(&rev.pending);\n> +\trelease_revisions(&rev);\n>  }\n\nI very much like this.\n\n> -\tobject_array_clear(&rev.pending);\n>  \tclear_pathspec(&rev.prune_data);\n> +\trelease_revisions(&rev);\n\nBut this makes readers wonder why .prune_data still needs a separate\ncall to clear.  We are making it unnecessary to clear .pending\nseparately, which was what made me say I very much like this in the\nfirst place.\n\nAt least surviving clear_pathspec() now deserves an in-code comment?\nWe'll know when we see what release_revisions() actually does.\n\n>  \trun_diff_index(&rev, 1);\n> -\tobject_array_clear(&rev.pending);\n> -\treturn (rev.diffopt.flags.has_changes != 0);\n> +\thas_changes = rev.diffopt.flags.has_changes;\n> +\trelease_revisions(&rev);\n> +\treturn (has_changes != 0);\n\nThis is necessary because release_revisions(&rev) is a way to\nrelease all resources held by rev, and .has_changes is stored\nas a part of a resource that will be cleared.\n\n> diff --git a/revision.c b/revision.c\n> index 5824fdeddfd..831f2cf977b 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -2926,6 +2926,13 @@ static void release_revisions_commit_list(struct rev_info *revs)\n>  \trevs->commits = NULL;\n>  }\n>  \n> +void release_revisions(struct rev_info *revs)\n> +{\n> +\tif (!revs)\n> +\t\treturn;\n> +\tobject_array_clear(&revs->pending);\n> +}\n\nWhoa.  \n\nEarlier, we saw a code that indicates that a call to this function\nwill invalidate the revs->diffopt.flags.has_changes but that is not\nthe case, at least at this point in the series.  Is this a result of\nan incorrect \"rebase -i\"?\n\nRegarding the clear_pathspec() earlier we saw, it is OK to leave the\ncall there without any extra comment, if the plan is to first start\nby introducing release_revisions() that does nothing but .pending\nfield.  But then the diffopt.flags.has_changes thing we saw earlier\nshould be postponed to a step where release_revisions() clears the\ndiffopt structure that is embedded in rev_info.\n\n> @@ -2568,8 +2568,9 @@ int has_uncommitted_changes(struct repository *r,\n>  \n>  \tdiff_setup_done(&rev_info.diffopt);\n>  \tresult = run_diff_index(&rev_info, 1);\n> -\tobject_array_clear(&rev_info.pending);\n> -\treturn diff_result_code(&rev_info.diffopt, result);\n> +\tresult = diff_result_code(&rev_info.diffopt, result);\n> +\trelease_revisions(&rev_info);\n> +\treturn result;\n\nDitto.\n"},{"id":"450961","messageId":"220310.868rtilksy.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"99c429f7-af5d-78ac-9888-9f4f58d01b62@github.com","subject":"Re: [PATCH 21/24] revisions API: release \"reflog_info\" in release revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-10T00:13:31Z","receivedAt":"2022-03-10T00:21:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Mar 09 2022, Derrick Stolee wrote:\n\n> On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:\n>> Add a missing reflog_walk_info_release() to \"reflog-walk.c\" and use it\n>> in release_revisions().\n>> \n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>>  reflog-walk.c            | 26 ++++++++++++++++++++++++--\n>>  reflog-walk.h            |  1 +\n>>  revision.c               |  1 +\n>>  t/t0100-previous.sh      |  1 +\n>>  t/t1401-symbolic-ref.sh  |  2 ++\n>>  t/t1411-reflog-show.sh   |  1 +\n>>  t/t1412-reflog-loop.sh   |  2 ++\n>>  t/t1415-worktree-refs.sh |  1 +\n>>  8 files changed, 33 insertions(+), 2 deletions(-)\n>> \n>> diff --git a/reflog-walk.c b/reflog-walk.c\n>> index 8ac4b284b6b..4322228d122 100644\n>> --- a/reflog-walk.c\n>> +++ b/reflog-walk.c\n>> @@ -7,7 +7,7 @@\n>>  #include \"reflog-walk.h\"\n>>  \n>>  struct complete_reflogs {\n>> -\tchar *ref;\n>> +\tconst char *ref;\n>>  \tconst char *short_ref;\n>\n> This seems like the opposite change from what I would\n> expect, because the 'const' implies non-ownership.\n\nYes, I'll change it.\n\nFWIW we've had recent discussions on this point & it's a bit\ncontext-dependant. See\nhttps://lore.kernel.org/git/patch-v2-1.1-e2a166a9733-20211021T201541Z-avarab@gmail.com/\nand https://lore.kernel.org/git/xmqqlf2vbbl8.fsf@gitster.g/\n\nI.e. the preferred pattern is:\n\n * Make it \"char *\" if it's your own private struct, because it's non-const\n * If it's a \"public API\" and it's not really \"const char *\", but we\n   don't want the API user to think they can fiddle with it, make it\n   \"const char *\" and cast it in the free().\n\nIn this case I think I just mixed those two up, or maybe I initially\nwrote it before that was clarified. In this case it's our own private\nstruct within this file.\n\n>> -\tfree(array->ref);\n>> +\tfree((char *)array->ref);\n>> +\tfree((char *)array->short_ref);\n>\n> This further makes the point that we should be keeping\n> non-const versions so we can clearly document ownership.\n\n*nod*\n\n>> +static void complete_reflogs_clear(void *util, const char *str)\n>> +{\n>> +\tstruct complete_reflogs *array = util;\n>> +\tfree_complete_reflog(array);\n>> +}\n>\n> Is there a reason we don't do the cast inside?\n>\n> \tfree_complete_reflog((struct complete_reflogs *)util);\n\nI think we generally do that more often that not (although I should add\nan extra \\n) there. I.e. to immediately cast the void* into a variable.\n\nI also find it handy when e.g. stepping through in a debugger, because\nyou'll have a variable you can inspect without de-referencing it every\ntime, and if the function uses \"array\" for something else in the future\nit'll be less verbosity on the second use...\n"},{"id":"450964","messageId":"220310.864k46lk7x.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"xmqqmthylonj.fsf@gitster.g","subject":"Re: [PATCH 05/24] revision.[ch]: provide and start using a release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-10T00:28:04Z","receivedAt":"2022-03-10T00:33:46Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Mar 09 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> diff --git a/builtin/checkout.c b/builtin/checkout.c\n>> index 9244827ca02..ed993383531 100644\n>> --- a/builtin/checkout.c\n>> +++ b/builtin/checkout.c\n>> @@ -629,7 +629,7 @@ static void show_local_changes(struct object *head,\n>>  \tdiff_setup_done(&rev.diffopt);\n>>  \tadd_pending_object(&rev, head, NULL);\n>>  \trun_diff_index(&rev, 0);\n>> -\tobject_array_clear(&rev.pending);\n>> +\trelease_revisions(&rev);\n>>  }\n>\n> I very much like this.\n>\n>> -\tobject_array_clear(&rev.pending);\n>>  \tclear_pathspec(&rev.prune_data);\n>> +\trelease_revisions(&rev);\n>\n> But this makes readers wonder why .prune_data still needs a separate\n> call to clear.  We are making it unnecessary to clear .pending\n> separately, which was what made me say I very much like this in the\n> first place.\n>\n> At least surviving clear_pathspec() now deserves an in-code comment?\n> We'll know when we see what release_revisions() actually does.\n\nI'll note it in a commit message. Generally in this series I then loop\nback and remove the clear_pathspec() (or similar) once I start clearing\n\"prune_data\", but in this case I seem to have missed this specific case\nlater in the series...\n\n>>  \trun_diff_index(&rev, 1);\n>> -\tobject_array_clear(&rev.pending);\n>> -\treturn (rev.diffopt.flags.has_changes != 0);\n>> +\thas_changes = rev.diffopt.flags.has_changes;\n>> +\trelease_revisions(&rev);\n>> +\treturn (has_changes != 0);\n>\n> This is necessary because release_revisions(&rev) is a way to\n> release all resources held by rev, and .has_changes is stored\n> as a part of a resource that will be cleared.\n\nYes, but...\n\n>> diff --git a/revision.c b/revision.c\n>> index 5824fdeddfd..831f2cf977b 100644\n>> --- a/revision.c\n>> +++ b/revision.c\n>> @@ -2926,6 +2926,13 @@ static void release_revisions_commit_list(struct rev_info *revs)\n>>  \trevs->commits = NULL;\n>>  }\n>>  \n>> +void release_revisions(struct rev_info *revs)\n>> +{\n>> +\tif (!revs)\n>> +\t\treturn;\n>> +\tobject_array_clear(&revs->pending);\n>> +}\n>\n> Whoa.  \n>\n> Earlier, we saw a code that indicates that a call to this function\n> will invalidate the revs->diffopt.flags.has_changes but that is not\n> the case, at least at this point in the series.  Is this a result of\n> an incorrect \"rebase -i\"?\n>\n> Regarding the clear_pathspec() earlier we saw, it is OK to leave the\n> call there without any extra comment, if the plan is to first start\n> by introducing release_revisions() that does nothing but .pending\n> field.  But then the diffopt.flags.has_changes thing we saw earlier\n> should be postponed to a step where release_revisions() clears the\n> diffopt structure that is embedded in rev_info.\n>\n>> @@ -2568,8 +2568,9 @@ int has_uncommitted_changes(struct repository *r,\n>>  \n>>  \tdiff_setup_done(&rev_info.diffopt);\n>>  \tresult = run_diff_index(&rev_info, 1);\n>> -\tobject_array_clear(&rev_info.pending);\n>> -\treturn diff_result_code(&rev_info.diffopt, result);\n>> +\tresult = diff_result_code(&rev_info.diffopt, result);\n>> +\trelease_revisions(&rev_info);\n>> +\treturn result;\n>\n> Ditto.\n\n...indeed, those don't do anything yet.\n\nI thought it better (and will explain so in a re-rolled commit message)\nto start adding release_revisions() to existing API users, and not to\nhave those individidual users assume anything about what is and isn't\ncleared yet.\n\nSome users that end up with the release_revisions() don't need it at\nall, because in those cases we just so happen not to actually allocate\nanything.\n\nBut just like with strbuf_release() et al we generally just add it at\nthe end, and just have the destructor worry about the NOOP-ing.\n\nSo I'd prefer to keep this part of the general structure as-is,\ni.e. even if we do nothing with \"diffopt\" *yet* we can assert that the\nmere addition of the nascent release_revisions() isn't breaking anything\nby merely existing as we add it to all \"struct rev_info\" users.\n\nThen as we add more things to release_revisions() we get a nicely tested\nbefore/after where we can see if/how adding new free()'s there breaks or\ndoesn't break.\n"},{"id":"450970","messageId":"xmqqee3aipbz.fsf@gitster.g","threadId":"57525","inReplyTo":"220310.864k46lk7x.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH 05/24] revision.[ch]: provide and start using a release_revisions()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-10T01:11:28Z","receivedAt":"2022-03-10T01:11:34Z","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> So I'd prefer to keep this part of the general structure as-is,\n> i.e. even if we do nothing with \"diffopt\" *yet* we can assert ...\n\nPlease don't.\n\nIt will become hard to tell during the patch progression if \"we can\ndo so even though we do not need to do so *yet*\" is correct\n(e.g. diffopt---which does not have a separate allocation to be\nreleased), or if \"pretending that the field is cleared by _release()\nfunction is premature and will lead to a new leak\" (e.g. if you lost\nseparate clearing of .prune_data at this step, that would be an\nincorrect change because it does hold on to an allocated resource).\n"},{"id":"451026","messageId":"220310.865yolkgbo.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"625c3868-60c9-009b-b01b-f186710d4d08@github.com","subject":"Re: [PATCH 20/24] revisions API: clear \"boundary_commits\" in release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-10T14:55:10Z","receivedAt":"2022-03-10T15:04:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Mar 09 2022, Derrick Stolee wrote:\n\n> On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:\n>> Clear the \"boundary_commits\" object_array in release_revisions(). This\n>> makes a *lot* of tests pass under SANITIZE=leak, including most of the\n>> t/t[0-9]*git-svn*.sh tests.\n>> \n>> This includes the tests we had false-positive passes on before my\n>> 6798b08e848 (perl Git.pm: don't ignore signalled failure in\n>> _cmd_close(), 2022-02-01), now they pass for real.\n>> \n>> Since there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to\n>> list those that don't pass than to touch most of those 66. So let's\n>> introduce a \"TEST_FAILS_SANITIZE_LEAK=true\", which if set in the tests\n>> won't cause lib-git-svn.sh to set \"TEST_PASSES_SANITIZE_LEAK=true.\n>\n> This paragraph perhaps belongs a few patches earlier in \"revisions\n> API: have release_revisions() release \"cmdline\"\", or else there was\n> some swap of order here.\n\nIndeed, this was a bad case of a commit message being duplicated, will\nfix. Sorry!\n"},{"id":"451027","messageId":"220310.861qz9kg8m.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"220310.864k46lk7x.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH 05/24] revision.[ch]: provide and start using a release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-10T14:56:32Z","receivedAt":"2022-03-10T15:08:59Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Mar 10 2022, Ævar Arnfjörð Bjarmason wrote:\n\n> On Wed, Mar 09 2022, Junio C Hamano wrote:\n>\n>> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>>\n>>> diff --git a/builtin/checkout.c b/builtin/checkout.c\n>>> index 9244827ca02..ed993383531 100644\n>>> --- a/builtin/checkout.c\n>>> +++ b/builtin/checkout.c\n>>> @@ -629,7 +629,7 @@ static void show_local_changes(struct object *head,\n>>>  \tdiff_setup_done(&rev.diffopt);\n>>>  \tadd_pending_object(&rev, head, NULL);\n>>>  \trun_diff_index(&rev, 0);\n>>> -\tobject_array_clear(&rev.pending);\n>>> +\trelease_revisions(&rev);\n>>>  }\n>>\n>> I very much like this.\n>>\n>>> -\tobject_array_clear(&rev.pending);\n>>>  \tclear_pathspec(&rev.prune_data);\n>>> +\trelease_revisions(&rev);\n>>\n>> But this makes readers wonder why .prune_data still needs a separate\n>> call to clear.  We are making it unnecessary to clear .pending\n>> separately, which was what made me say I very much like this in the\n>> first place.\n>>\n>> At least surviving clear_pathspec() now deserves an in-code comment?\n>> We'll know when we see what release_revisions() actually does.\n>\n> I'll note it in a commit message. Generally in this series I then loop\n> back and remove the clear_pathspec() (or similar) once I start clearing\n> \"prune_data\", but in this case I seem to have missed this specific case\n> later in the series...\n\nJust for completeness, I didn't miss a case here, but thought I did\nbecause of the truncated diff mentioning \"builtin/checkout.c\",\nnevermind... (but will fix the other raised issue).\n"},{"id":"451028","messageId":"220310.86wnh1j1e6.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"xmqqee3aipbz.fsf@gitster.g","subject":"Re: [PATCH 05/24] revision.[ch]: provide and start using a release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-10T14:57:28Z","receivedAt":"2022-03-10T15:09:33Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Mar 09 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>> So I'd prefer to keep this part of the general structure as-is,\n>> i.e. even if we do nothing with \"diffopt\" *yet* we can assert ...\n>\n> Please don't.\n>\n> It will become hard to tell during the patch progression if \"we can\n> do so even though we do not need to do so *yet*\" is correct\n> (e.g. diffopt---which does not have a separate allocation to be\n> released), or if \"pretending that the field is cleared by _release()\n> function is premature and will lead to a new leak\" (e.g. if you lost\n> separate clearing of .prune_data at this step, that would be an\n> incorrect change because it does hold on to an allocated resource).\n\nFor a v2 I've rewritten those sorts of changes out. I've bundled them up\nto a commit for \"diffopt\" at the end, even though whe don't free() it\nyet.\n\nDepending on what you think of that change we can either drop it or keep\nit, but in either case it should be obvious what is and isn't in\nanticipation of any future free() we aren't yet doing.\n"},{"id":"451030","messageId":"220310.86sfrpj0t0.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"4e564691-0ef6-a867-647e-c26064b0bb36@github.com","subject":"Re: [PATCH 17/24] revisions API: have release_revisions() release \"cmdline\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-10T15:10:36Z","receivedAt":"2022-03-10T15:16:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Mar 09 2022, Derrick Stolee wrote:\n\n> On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:\n>> Extend the the release_revisions() function so that it frees the\n>> \"cmdline\" in the \"struct ref_info\". This in combination with a\n>> preceding change to free \"commits\" and \"mailmap\" means that we can\n>> whitelist another test under \"TEST_PASSES_SANITIZE_LEAK=true\".\n>\n> Could you consider changing your vocabulary to use \"allowlist\"\n> (with the opposite term being \"denylist\")?\n\nI looked at some of the wording with past changes like that, which in\nsome cases was entirely different. See some of:\n\n    git log --grep=SANITIZE\n\nBut I figred for the nascent v2 re-roll that avoiding the churn was\nbetter, particularly since there's various similar phrasing on \"master\"\nalready.\n"},{"id":"452031","messageId":"patch-v2-01.27-8fbafe61053-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 01/27] t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:31:51Z","receivedAt":"2022-03-23T20:32:33Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak that's been with us since f9500261e0a (fast-rebase:\nwrite conflict state to working tree, index, and HEAD, 2021-05-20)\nchanged this code to move these strbuf_release() into an if/else\nblock.\n\nWe'll also add to \"reflog_msg\" in the \"else\" arm of the \"if\" block\nbeing modified here, and we'll append to \"branch_msg\" in both\ncases. But after f9500261e0a only the \"if\" block would free these two\n\"struct strbuf\".\n\nThis change is needed so that a subsequent addition of a \"goto\ncleanup\" pattern when adding release_revisions() doesn't solve this\nunrelated bug while it's at it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/helper/test-fast-rebase.c | 5 +++--\n 1 file changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c\nindex fc2d4609043..993b90eaedd 100644\n--- a/t/helper/test-fast-rebase.c\n+++ b/t/helper/test-fast-rebase.c\n@@ -201,8 +201,6 @@ int cmd__fast_rebase(int argc, const char **argv)\n \t\t}\n \t\tif (create_symref(\"HEAD\", branch_name.buf, reflog_msg.buf) < 0)\n \t\t\tdie(_(\"unable to update HEAD\"));\n-\t\tstrbuf_release(&reflog_msg);\n-\t\tstrbuf_release(&branch_name);\n \n \t\tprime_cache_tree(the_repository, the_repository->index,\n \t\t\t\t result.tree);\n@@ -221,5 +219,8 @@ int cmd__fast_rebase(int argc, const char **argv)\n \tif (write_locked_index(&the_index, &lock,\n \t\t\t       COMMIT_LOCK | SKIP_IF_UNCHANGED))\n \t\tdie(_(\"unable to write %s\"), get_index_file());\n+\n+\tstrbuf_release(&reflog_msg);\n+\tstrbuf_release(&branch_name);\n \treturn (result.clean == 0);\n }\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452032","messageId":"patch-v2-02.27-77e7c2c321e-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 02/27] blame: use \"goto cleanup\" for cleanup_scoreboard()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:31:52Z","receivedAt":"2022-03-23T20:32:36Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Amend a freeing pattern added in 0906ac2b54b (blame: use changed-path\nBloom filters, 2020-04-16) to use a \"goto cleanup\", so that we can be\nsure that we call cleanup_scoreboard().\n\nThis change is needed so that a subsequent addition of a\nrelease_revisions() doesn't solve this unrelated issue while it's at\nit.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/blame.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 8d15b68afc9..885b381ab83 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1167,7 +1167,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tif (!incremental)\n \t\tsetup_pager();\n \telse\n-\t\treturn 0;\n+\t\tgoto cleanup;\n \n \tblame_sort_final(&sb);\n \n@@ -1201,6 +1201,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\tprintf(\"num commits: %d\\n\", sb.num_commits);\n \t}\n \n+cleanup:\n \tcleanup_scoreboard(&sb);\n \treturn 0;\n }\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452033","messageId":"patch-v2-03.27-151d1175905-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 03/27] string_list API users: use string_list_init_{no,}dup","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:31:53Z","receivedAt":"2022-03-23T20:32:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Follow-up on the introduction of string_list_init_nodup() and\nstring_list_init_dup() in the series merged in bd4232fac33 (Merge\nbranch 'ab/struct-init', 2021-07-16) and convert code that implicitly\nrelied on xcalloc() being equivalent to the initializer to use\nxmalloc() and string_list_init_{no,}dup() instead.\n\nIn the case of get_unmerged() in merge-recursive.c we used the\ncombination of xcalloc() and assigning \"1\" to \"strdup_strings\" to get\nwhat we'd get via string_list_init_dup(), let's use that instead.\n\nAdjacent code in cmd_format_patch() will be changed in a subsequent\ncommit, since we're changing that let's change the other in-tree\npatterns that do the same. Let's also convert a \"x == NULL\" to \"!x\"\nper our CodingGuidelines, as we need to change the \"if\" line anyway.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/log.c      | 9 ++++++---\n builtin/shortlog.c | 6 ++++--\n merge-recursive.c  | 4 ++--\n 3 files changed, 12 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex c211d66d1d0..634dc782cce 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -231,7 +231,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,\n \t}\n \n \tif (mailmap) {\n-\t\trev->mailmap = xcalloc(1, sizeof(struct string_list));\n+\t\trev->mailmap = xmalloc(sizeof(struct string_list));\n+\t\tstring_list_init_nodup(rev->mailmap);\n \t\tread_mailmap(rev->mailmap);\n \t}\n \n@@ -2173,8 +2174,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tprepare_bases(&bases, base, list, nr);\n \t}\n \n-\tif (in_reply_to || thread || cover_letter)\n-\t\trev.ref_message_ids = xcalloc(1, sizeof(struct string_list));\n+\tif (in_reply_to || thread || cover_letter) {\n+\t\trev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));\n+\t\tstring_list_init_nodup(rev.ref_message_ids);\n+\t}\n \tif (in_reply_to) {\n \t\tconst char *msgid = clean_message_id(in_reply_to);\n \t\tstring_list_append(rev.ref_message_ids, msgid);\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex 26c5c0cf935..fcde07c9367 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -81,8 +81,10 @@ static void insert_one_record(struct shortlog *log,\n \t\tformat_subject(&subject, oneline, \" \");\n \t\tbuffer = strbuf_detach(&subject, NULL);\n \n-\t\tif (item->util == NULL)\n-\t\t\titem->util = xcalloc(1, sizeof(struct string_list));\n+\t\tif (!item->util) {\n+\t\t\titem->util = xmalloc(sizeof(struct string_list));\n+\t\t\tstring_list_init_nodup(item->util);\n+\t\t}\n \t\tstring_list_append(item->util, buffer);\n \t}\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 1ee6364e8b1..32bbba5fbb1 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -522,10 +522,10 @@ static struct stage_data *insert_stage_data(struct repository *r,\n  */\n static struct string_list *get_unmerged(struct index_state *istate)\n {\n-\tstruct string_list *unmerged = xcalloc(1, sizeof(struct string_list));\n+\tstruct string_list *unmerged = xmalloc(sizeof(struct string_list));\n \tint i;\n \n-\tunmerged->strdup_strings = 1;\n+\tstring_list_init_dup(unmerged);\n \n \t/* TODO: audit for interaction with sparse-index. */\n \tensure_full_index(istate);\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452035","messageId":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com","subject":"[PATCH v2 00/27] revision.[ch]: add and use release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:31:50Z","receivedAt":"2022-03-23T20:32:43Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This is a re-roll of the v1 of [1] now that 7391ecd338e (Merge branch\n'ds/partial-bundles', 2022-03-21) has landed, which it had a conflict\nwith. I believe this v2 addresses all the feedback brought up on v1.\n\nAs the range-diff shows the v2 is a bit larger, I split up some\ncommits as suggested in review.\n\nI've also rebased this locally on unsubmitted patches I have which\nallow for testing that the tests we mark with SANITIZE=leak have a 1=1\ncorrespondance with what we're marking as passing with\nTEST_PASSES_SANITIZE_LEAK=true.\n\nAll of the individual commits here pass under that, so all the t/*.sh\nchanges are correct as far as exhaustively marking newly passing tests\nwith \"TEST_PASSES_SANITIZE_LEAK=true\" goes.\n\nNow, as I was just about to submit this I noticed that Derrick has\nsubmitted a follow-up series to 7391ecd338e[2]. It textually conflicts\nwith this one in a minor way, and the resolution is the following:\n\n\tdiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\n\tindex 5f6197a37bb..0c1d5586ed4 100644\n\t--- a/builtin/pack-objects.c\n\t+++ b/builtin/pack-objects.c\n\t@@ -3796,7 +3796,6 @@ static void get_object_list(struct rev_info *revs, int ac, const char **av)\n\t \tif (unpack_unreachable)\n\t \t\tloosen_unused_packed_objects();\n\t \n\t-\trelease_revisions(&revs);\n\t \toid_array_clear(&recent_objects);\n\t }\n\t \n\t@@ -4189,6 +4188,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)\n\t \n\t cleanup:\n\t \tstrvec_clear(&rp);\n\t+\tUNLEAK(revs.diffopt);\n\t+\trelease_revisions(&revs);\n\t \n\t \treturn 0;\n\t }\n\nThe UNLEAK() there is new. At the tip of this series we pass (among\nmany others) t5313-pack-bounds-checks.sh, t5316-pack-delta-depth.sh,\nt6114-keep-packs.sh under SANITIZE=leak. When I rebase Derrick's [2]\non top of this series it requires the addition of that UNLEAK().\n\nI didn't look in any detail (didn't have time just now) into why that\nnow fails exactly, we do the relevant revs.diffopt allocation before.\n\nThere's also a trivial conflict here with brian m. carlson's\nbc/stash-export series. This series moves the free_stash_info()\nfunction around, so the conflict is the following:\n\t\n\t<<<<<<< HEAD\n\t\t\t\tfprintf_ln(stderr, _(\"No stash entries found.\"));\n\t=======\n\t\t\t\tfree_stash_info(info);\n\t\t\t\tif (!quiet)\n\t\t\t\t\tfprintf_ln(stderr, _(\"No stash entries found.\"));\n\t>>>>>>> origin/seen\n\t[...]\n\t<<<<<<< HEAD\n\t\tif (get_oid(revision, &info->w_commit))\n\t\t\treturn error(_(\"%s is not a valid reference\"), revision);\n\t=======\n\t\tif (get_oid_with_context(the_repository, revision,\n\t\t\t\t\t GET_OID_QUIETLY | GET_OID_RETURN_FAILURE,\n\t\t\t\t\t &info->w_commit, &unused)) {\n\t\t\tif (!quiet)\n\t\t\t\terror(_(\"%s is not a valid reference\"), revision);\n\t\t\tfree_stash_info(info);\n\t\t\treturn -1;\n\t\t}\n\t>>>>>>> origin/seen\n\nIn both cases the solution is triavial: Keep brian's side, but delete\nthe two free_stash_info() calls, as we now call it in the invoking\nfunction.\n\t\n1. https://lore.kernel.org/git/cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com/\n2. https://lore.kernel.org/git/pull.1186.git.1647970119.gitgitgadget@gmail.com/\n\nÆvar Arnfjörð Bjarmason (27):\n  t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"\n  blame: use \"goto cleanup\" for cleanup_scoreboard()\n  string_list API users: use string_list_init_{no,}dup\n  format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"\n  revision.[ch]: split freeing of revs->commit into a function\n  revision.[ch]: provide and start using a release_revisions()\n  revisions API users: add straightforward release_revisions()\n  revisions API users: use release_revisions() needing \"{ 0 }\" init\n  stash: always have the owner of \"stash_info\" free it\n  revisions API users: add \"goto cleanup\" for release_revisions()\n  revisions API users: use release_revisions() in http-push.c\n  revisions API users: use release_revisions() in builtin/log.c\n  revisions API users: use release_revisions() with UNLEAK()\n  revisions API users: use release_revisions() in submodule.c edge case\n  revisions API users: use release_revisions() for \"prune_data\" users\n  revisions API: have release_revisions() release \"commits\"\n  revisions API: have release_revisions() release \"mailmap\"\n  revisions API: have release_revisions() release \"cmdline\"\n  revisions API: have release_revisions() release \"filter\"\n  revisions API: have release_revisions() release \"grep_filter\"\n  revisions API: have release_revisions() release \"prune_data\"\n  revisions API: clear \"boundary_commits\" in release_revisions()\n  revisions API: release \"reflog_info\" in release revisions()\n  revisions API: call diff_free(&revs->pruning) in revisions_release()\n  revisions API: have release_revisions() release \"date_mode\"\n  revisions API: have release_revisions() release \"topo_walk_info\"\n  revisions API: add a TODO for diff_free(&revs->diffopt)\n\n add-interactive.c                          |  3 +-\n bisect.c                                   | 20 ++++--\n builtin/add.c                              |  3 +-\n builtin/am.c                               |  3 +\n builtin/bisect--helper.c                   |  2 +\n builtin/blame.c                            |  4 +-\n builtin/checkout.c                         |  3 +-\n builtin/commit.c                           |  6 +-\n builtin/describe.c                         |  2 +\n builtin/diff-files.c                       |  8 ++-\n builtin/diff-index.c                       |  5 +-\n builtin/diff.c                             |  2 +-\n builtin/fast-export.c                      |  1 +\n builtin/log.c                              | 36 ++++++----\n builtin/merge.c                            |  2 +\n builtin/pack-objects.c                     |  2 +\n builtin/prune.c                            |  1 +\n builtin/reflog.c                           |  1 +\n builtin/rev-list.c                         | 19 ++++--\n builtin/shortlog.c                         |  8 ++-\n builtin/stash.c                            | 77 +++++++++++-----------\n builtin/submodule--helper.c                | 14 ++--\n bundle.c                                   | 12 ++--\n diff-lib.c                                 |  8 ++-\n fmt-merge-msg.c                            |  1 +\n http-push.c                                |  3 +-\n merge-ort.c                                |  1 +\n merge-recursive.c                          |  5 +-\n midx.c                                     |  1 +\n pack-bitmap-write.c                        |  1 +\n range-diff.c                               |  2 +-\n ref-filter.c                               |  1 +\n reflog-walk.c                              | 24 ++++++-\n reflog-walk.h                              |  1 +\n remote.c                                   |  1 +\n revision.c                                 | 68 ++++++++++++++++---\n revision.h                                 |  6 ++\n sequencer.c                                | 26 ++++++--\n shallow.c                                  |  1 +\n submodule.c                                |  5 +-\n t/helper/test-fast-rebase.c                | 23 +++++--\n t/helper/test-revision-walking.c           |  1 +\n t/lib-git-svn.sh                           |  4 ++\n t/t0056-git-C.sh                           |  1 +\n t/t0062-revision-walking.sh                |  1 +\n t/t0100-previous.sh                        |  1 +\n t/t0101-at-syntax.sh                       |  2 +\n t/t1001-read-tree-m-2way.sh                |  1 +\n t/t1002-read-tree-m-u-2way.sh              |  1 +\n t/t1060-object-corruption.sh               |  2 +\n t/t1401-symbolic-ref.sh                    |  2 +\n t/t1411-reflog-show.sh                     |  1 +\n t/t1412-reflog-loop.sh                     |  2 +\n t/t1415-worktree-refs.sh                   |  1 +\n t/t2015-checkout-unborn.sh                 |  1 +\n t/t2200-add-update.sh                      |  1 +\n t/t3302-notes-index-expensive.sh           |  1 +\n t/t3303-notes-subtrees.sh                  |  1 +\n t/t3305-notes-fanout.sh                    |  1 +\n t/t3408-rebase-multi-line.sh               |  1 +\n t/t4021-format-patch-numbered.sh           |  1 +\n t/t4027-diff-submodule.sh                  |  1 +\n t/t4028-format-patch-mime-headers.sh       |  2 +\n t/t4036-format-patch-signer-mime.sh        |  1 +\n t/t4039-diff-assume-unchanged.sh           |  1 +\n t/t4055-diff-context.sh                    |  1 +\n t/t4066-diff-emit-delay.sh                 |  1 +\n t/t4122-apply-symlink-inside.sh            |  1 +\n t/t4126-apply-empty.sh                     |  1 -\n t/t4128-apply-root.sh                      |  1 +\n t/t4206-log-follow-harder-copies.sh        |  2 +\n t/t4207-log-decoration-colors.sh           |  1 +\n t/t4212-log-corrupt.sh                     |  1 +\n t/t5301-sliding-window.sh                  |  2 +\n t/t5313-pack-bounds-checks.sh              |  2 +\n t/t5316-pack-delta-depth.sh                |  2 +\n t/t5320-delta-islands.sh                   |  2 +\n t/t5322-pack-objects-sparse.sh             |  1 +\n t/t5506-remote-groups.sh                   |  1 +\n t/t5513-fetch-track.sh                     |  1 +\n t/t5515-fetch-merge-logic.sh               |  1 +\n t/t5518-fetch-exit-status.sh               |  1 +\n t/t5532-fetch-proxy.sh                     |  2 +\n t/t5600-clone-fail-cleanup.sh              |  1 +\n t/t5900-repo-selection.sh                  |  2 +\n t/t6002-rev-list-bisect.sh                 |  1 +\n t/t6003-rev-list-topo-order.sh             |  1 +\n t/t6005-rev-list-count.sh                  |  1 +\n t/t6018-rev-list-glob.sh                   |  1 +\n t/t6100-rev-list-in-order.sh               |  1 +\n t/t6101-rev-parse-parents.sh               |  1 +\n t/t6110-rev-list-sparse.sh                 |  1 +\n t/t6114-keep-packs.sh                      |  2 +\n t/t6131-pathspec-icase.sh                  |  2 +\n t/t7008-filter-branch-null-sha1.sh         |  1 +\n t/t7702-repack-cyclic-alternate.sh         |  2 +\n t/t9001-send-email.sh                      |  1 +\n t/t9100-git-svn-basic.sh                   |  1 +\n t/t9101-git-svn-props.sh                   |  2 +\n t/t9104-git-svn-follow-parent.sh           |  2 +\n t/t9106-git-svn-commit-diff-clobber.sh     |  2 +\n t/t9115-git-svn-dcommit-funky-renames.sh   |  1 +\n t/t9116-git-svn-log.sh                     |  1 +\n t/t9122-git-svn-author.sh                  |  2 +\n t/t9127-git-svn-partial-rebuild.sh         |  1 +\n t/t9129-git-svn-i18n-commitencoding.sh     |  1 +\n t/t9132-git-svn-broken-symlink.sh          |  1 +\n t/t9139-git-svn-non-utf8-commitencoding.sh |  1 +\n t/t9146-git-svn-empty-dirs.sh              |  2 +\n t/t9148-git-svn-propset.sh                 |  1 +\n t/t9160-git-svn-preserve-empty-dirs.sh     |  1 +\n t/t9162-git-svn-dcommit-interactive.sh     |  2 +\n t/t9164-git-svn-dcommit-concurrent.sh      |  2 +\n t/t9501-gitweb-standalone-http-status.sh   |  1 +\n wt-status.c                                | 15 +++--\n 115 files changed, 402 insertions(+), 123 deletions(-)\n\nRange-diff against v1:\n 1:  017c67c7540 =  1:  8fbafe61053 t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"\n 2:  db0b04e53a9 =  2:  77e7c2c321e blame: use \"goto cleanup\" for cleanup_scoreboard()\n -:  ----------- >  3:  151d1175905 string_list API users: use string_list_init_{no,}dup\n 3:  ab1afe6c0c8 !  4:  64a76629e23 format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"\n    @@ Commit message\n         variable to hold it, to avoid the eventual cast from \"const char *\"\n         when we free() it.\n     \n    -    While we're at it let's also move to using string_list_init_nodup()\n    -    instead of relying on calloc() implicitly coming up with the same\n    -    result. See 770fedaf9fb (string-list.[ch]: add a\n    -    string_list_init_{nodup,dup}(), 2021-07-01) for the introduction of\n    -    the string_list_init_nodup() helper. The string_list_clear() here is\n    -    redundant due to the *_nodup() initialization, but let's add it anyway\n    -    for consistency with other API use.\n    -\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/log.c ##\n    @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *pre\n      \tstruct commit *commit;\n      \tstruct commit **list = NULL;\n      \tstruct rev_info rev;\n    -+\tchar *extra_headers = NULL;\n    ++\tchar *to_free = NULL;\n      \tstruct setup_revision_opt s_r_opt;\n      \tint nr = 0, total, i;\n      \tint use_stdout = 0;\n    @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *pre\n      \t}\n      \n     -\trev.extra_headers = strbuf_detach(&buf, NULL);\n    -+\textra_headers = strbuf_detach(&buf, NULL);\n    -+\trev.extra_headers = extra_headers;\n    ++\trev.extra_headers = to_free = strbuf_detach(&buf, NULL);\n      \n      \tif (from) {\n      \t\tif (split_ident_line(&rev.from_ident, from, strlen(from)))\n    -@@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)\n    - \t\tprepare_bases(&bases, base, list, nr);\n    - \t}\n    - \n    --\tif (in_reply_to || thread || cover_letter)\n    --\t\trev.ref_message_ids = xcalloc(1, sizeof(struct string_list));\n    -+\tif (in_reply_to || thread || cover_letter) {\n    -+\t\trev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));\n    -+\t\tstring_list_init_nodup(rev.ref_message_ids);\n    -+\t}\n    - \tif (in_reply_to) {\n    - \t\tconst char *msgid = clean_message_id(in_reply_to);\n    - \t\tstring_list_append(rev.ref_message_ids, msgid);\n     @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)\n      \tstrbuf_release(&rdiff1);\n      \tstrbuf_release(&rdiff2);\n      \tstrbuf_release(&rdiff_title);\n    -+\tfree(extra_headers);\n    -+\tif (rev.ref_message_ids) {\n    ++\tfree(to_free);\n    ++\tif (rev.ref_message_ids)\n     +\t\tstring_list_clear(rev.ref_message_ids, 0);\n    -+\t\tfree(rev.ref_message_ids);\n    -+\t}\n    ++\tfree(rev.ref_message_ids);\n      \tUNLEAK(rev);\n      \treturn 0;\n      }\n 4:  763e0ab3425 =  5:  4c0718b43d7 revision.[ch]: split freeing of revs->commit into a function\n 5:  b89dcadcc22 !  6:  7194aac88e1 revision.[ch]: provide and start using a release_revisions()\n    @@ Commit message\n         to clear its \"diffopt\". I.e. we need to call release_revisions() after\n         we've inspected any state in \"struct rev_info\".\n     \n    +    This leaves in place e.g. clear_pathspec(&rev.prune_data) in\n    +    stash_working_tree() in builtin/stash.c, subsequent commits will teach\n    +    release_revisions() to free \"prune_data\" and other members that in\n    +    some cases are individually cleared by users of \"struct rev_info\" by\n    +    reaching into its members. Those subsequent commits will remove the\n    +    relevant calls to e.g. clear_pathspec().\n    +\n    +    We avoid amending code in index_differs_from() in diff-lib.c as well\n    +    as wt_status_collect_changes_index(), has_unstaged_changes() and\n    +    has_uncommitted_changes() in wt-status.c in a way that assumes that we\n    +    are already clearing the \"diffopt\" member. That will be handled in a\n    +    subsequent commit.\n    +\n         1. https://lore.kernel.org/git/87a6k8daeu.fsf@evledraar.gmail.com/\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n    @@ builtin/stash.c: static int stash_working_tree(struct stash_info *info, const st\n      \treturn ret;\n     \n      ## diff-lib.c ##\n    -@@ diff-lib.c: int index_differs_from(struct repository *r,\n    - {\n    - \tstruct rev_info rev;\n    - \tstruct setup_revision_opt opt;\n    -+\tunsigned int has_changes;\n    - \n    - \trepo_init_revisions(r, &rev, NULL);\n    - \tmemset(&opt, 0, sizeof(opt));\n     @@ diff-lib.c: int index_differs_from(struct repository *r,\n      \t\tdiff_flags_or(&rev.diffopt.flags, flags);\n      \trev.diffopt.ita_invisible_in_index = ita_invisible_in_index;\n      \trun_diff_index(&rev, 1);\n     -\tobject_array_clear(&rev.pending);\n    --\treturn (rev.diffopt.flags.has_changes != 0);\n    -+\thas_changes = rev.diffopt.flags.has_changes;\n     +\trelease_revisions(&rev);\n    -+\treturn (has_changes != 0);\n    + \treturn (rev.diffopt.flags.has_changes != 0);\n      }\n      \n    - static struct strbuf *idiff_prefix_cb(struct diff_options *opt, void *data)\n     \n      ## range-diff.c ##\n     @@ range-diff.c: int is_range_diff_range(const char *arg)\n    @@ revision.h: void repo_init_revisions(struct repository *r,\n     \n      ## wt-status.c ##\n     @@ wt-status.c: static void wt_status_collect_changes_index(struct wt_status *s)\n    - \trev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;\n    + \n      \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n      \trun_diff_index(&rev, 1);\n     -\tobject_array_clear(&rev.pending);\n    @@ wt-status.c: static void wt_status_collect_changes_index(struct wt_status *s)\n      \tclear_pathspec(&rev.prune_data);\n      }\n      \n    +@@ wt-status.c: int has_unstaged_changes(struct repository *r, int ignore_submodules)\n    + \trev_info.diffopt.flags.quick = 1;\n    + \tdiff_setup_done(&rev_info.diffopt);\n    + \tresult = run_diff_files(&rev_info, 0);\n    ++\trelease_revisions(&rev_info);\n    + \treturn diff_result_code(&rev_info.diffopt, result);\n    + }\n    + \n     @@ wt-status.c: int has_uncommitted_changes(struct repository *r,\n      \n      \tdiff_setup_done(&rev_info.diffopt);\n      \tresult = run_diff_index(&rev_info, 1);\n     -\tobject_array_clear(&rev_info.pending);\n    --\treturn diff_result_code(&rev_info.diffopt, result);\n    -+\tresult = diff_result_code(&rev_info.diffopt, result);\n     +\trelease_revisions(&rev_info);\n    -+\treturn result;\n    + \treturn diff_result_code(&rev_info.diffopt, result);\n      }\n      \n    - /**\n 6:  b5d55539b6d =  7:  3cb36d358d6 revisions API users: add straightforward release_revisions()\n 7:  295fa35174f =  8:  42ad1208934 revisions API users: use release_revisions() needing \"{ 0 }\" init\n -:  ----------- >  9:  88289157533 stash: always have the owner of \"stash_info\" free it\n 8:  0f9d313b338 ! 10:  eafe57663fc revisions API users: add \"goto cleanup\" for release_revisions()\n    @@ builtin/diff-files.c: int cmd_diff_files(int argc, const char **argv, const char\n     +\t\tresult = -1;\n     +\t\tgoto cleanup;\n      \t}\n    ++cleanup:\n      \tresult = run_diff_files(&rev, options);\n     -\treturn diff_result_code(&rev.diffopt, result);\n     +\tresult = diff_result_code(&rev.diffopt, result);\n    -+cleanup:\n     +\trelease_revisions(&rev);\n     +\treturn result;\n      }\n    @@ builtin/rev-list.c: int cmd_rev_list(int argc, const char **argv, const char *pr\n     @@ builtin/rev-list.c: int cmd_rev_list(int argc, const char **argv, const char *prefix)\n      \n      \tif (use_bitmap_index) {\n    - \t\tif (!try_bitmap_count(&revs, &filter_options, filter_provided_objects))\n    + \t\tif (!try_bitmap_count(&revs, filter_provided_objects))\n     -\t\t\treturn 0;\n     +\t\t\tgoto cleanup;\n    - \t\tif (!try_bitmap_disk_usage(&revs, &filter_options, filter_provided_objects))\n    + \t\tif (!try_bitmap_disk_usage(&revs, filter_provided_objects))\n     -\t\t\treturn 0;\n     +\t\t\tgoto cleanup;\n    - \t\tif (!try_bitmap_traversal(&revs, &filter_options, filter_provided_objects))\n    + \t\tif (!try_bitmap_traversal(&revs, filter_provided_objects))\n     -\t\t\treturn 0;\n     +\t\t\tgoto cleanup;\n      \t}\n    @@ builtin/rev-list.c: int cmd_rev_list(int argc, const char **argv, const char *pr\n     \n      ## builtin/stash.c ##\n     @@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *prefix)\n    - \n    - \tret = get_stash_info(&info, stash_args.nr, stash_args.v);\n    + cleanup:\n      \tstrvec_clear(&stash_args);\n    --\tif (ret)\n    --\t\treturn -1;\n    -+\tif (ret) {\n    -+\t\tret = -1;\n    -+\t\tgoto cleanup;\n    -+\t}\n    - \n    - \t/*\n    - \t * The config settings are applied only if there are not passed\n    -@@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *prefix)\n    - \n    - \t\tif (!show_stat && !show_patch) {\n    - \t\t\tfree_stash_info(&info);\n    --\t\t\treturn 0;\n    -+\t\t\tret = 0;\n    -+\t\t\tgoto cleanup;\n    - \t\t}\n    - \t}\n    - \n    -@@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *prefix)\n    - \tlog_tree_diff_flush(&rev);\n    - \n      \tfree_stash_info(&info);\n    --\treturn diff_result_code(&rev.diffopt, 0);\n    -+\tret = diff_result_code(&rev.diffopt, 0);\n    -+cleanup:\n     +\trelease_revisions(&rev);\n    -+\treturn ret;\n    + \treturn ret;\n      }\n      \n    - static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,\n     \n      ## builtin/submodule--helper.c ##\n     @@ builtin/submodule--helper.c: static int compute_summary_module_list(struct object_id *head_oid,\n    @@ builtin/submodule--helper.c: static int compute_summary_module_list(struct objec\n      \telse\n      \t\trun_diff_files(&rev, 0);\n      \tprepare_submodule_summary(info, &list);\n    -+\tret = 0;\n     +cleanup:\n      \tstrvec_clear(&diff_args);\n      \trelease_revisions(&rev);\n    @@ t/helper/test-fast-rebase.c: int cmd__fast_rebase(int argc, const char **argv)\n     -\tif (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1)\n     -\t\treturn error(_(\"unhandled options\"));\n     +\tif (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) {\n    -+\t\tret =  error(_(\"unhandled options\"));\n    ++\t\tret = error(_(\"unhandled options\"));\n     +\t\tgoto cleanup;\n     +\t}\n      \n    @@ t/helper/test-fast-rebase.c: int cmd__fast_rebase(int argc, const char **argv)\n      \tinit_merge_options(&merge_opt, the_repository);\n      \tmemset(&result, 0, sizeof(result));\n     @@ t/helper/test-fast-rebase.c: int cmd__fast_rebase(int argc, const char **argv)\n    - \tif (write_locked_index(&the_index, &lock,\n      \t\t\t       COMMIT_LOCK | SKIP_IF_UNCHANGED))\n      \t\tdie(_(\"unable to write %s\"), get_index_file());\n    --\n    + \n     +\tret = (result.clean == 0);\n     +cleanup:\n      \tstrbuf_release(&reflog_msg);\n 9:  8f93b87f369 = 11:  6fb2caf4d17 revisions API users: use release_revisions() in http-push.c\n10:  a89f0da4fd7 ! 12:  74818bc372f revisions API users: use release_revisions() in builtin/log.c\n    @@ builtin/log.c: int cmd_log(int argc, const char **argv, const char *prefix)\n      \n      /* format-patch */\n     @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)\n    + \tif (rev.ref_message_ids)\n      \t\tstring_list_clear(rev.ref_message_ids, 0);\n    - \t\tfree(rev.ref_message_ids);\n    - \t}\n    + \tfree(rev.ref_message_ids);\n     -\tUNLEAK(rev);\n     -\treturn 0;\n     +\treturn cmd_log_deinit(0, &rev);\n11:  a93c8838257 = 13:  8717e6e8471 revisions API users: use release_revisions() with UNLEAK()\n12:  8811a5ca840 = 14:  38f4f1c4ecb revisions API users: use release_revisions() in submodule.c edge case\n14:  761bbd1763c = 15:  566b739ac6c revisions API users: use release_revisions() for \"prune_data\" users\n15:  4a0adf6fd00 = 16:  bc36810e56e revisions API: have release_revisions() release \"commits\"\n16:  1a988c96371 ! 17:  a93251edf85 revisions API: have release_revisions() release \"mailmap\"\n    @@ builtin/commit.c: static const char *find_author_by_nickname(const char *name)\n      \tstruct commit *commit;\n      \tstruct strbuf buf = STRBUF_INIT;\n     -\tstruct string_list mailmap = STRING_LIST_INIT_NODUP;\n    -+\tstruct string_list *mailmap = xmalloc(sizeof(struct string_list));\n      \tconst char *av[20];\n      \tint ac = 0;\n      \n    @@ builtin/commit.c: static const char *find_author_by_nickname(const char *name)\n      \tav[++ac] = NULL;\n      \tsetup_revisions(ac, av, &revs, NULL);\n     -\trevs.mailmap = &mailmap;\n    -+\tstring_list_init_nodup(mailmap);\n    -+\trevs.mailmap = mailmap;\n    ++\trevs.mailmap = xmalloc(sizeof(struct string_list));\n    ++\tstring_list_init_nodup(revs.mailmap);\n      \tread_mailmap(revs.mailmap);\n      \n      \tif (prepare_revision_walk(&revs))\n    @@ t/t0056-git-C.sh\n      \n      test_expect_success '\"git -C <path>\" runs git from the directory <path>' '\n     \n    + ## t/t3302-notes-index-expensive.sh ##\n    +@@ t/t3302-notes-index-expensive.sh: test_description='Test commit notes index (expensive!)'\n    + GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n    + export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n    + \n    ++TEST_PASSES_SANITIZE_LEAK=true\n    + . ./test-lib.sh\n    + \n    + create_repo () {\n    +\n      ## t/t4055-diff-context.sh ##\n     @@\n      \n    @@ t/t7008-filter-branch-null-sha1.sh\n      \n      test_description='filter-branch removal of trees with null sha1'\n     +\n    -+TEST_PASSES_SANITIZE_LEAK=true\n      . ./test-lib.sh\n      \n      test_expect_success 'setup: base commits' '\n17:  b5687ed1355 ! 18:  424fac7ab95 revisions API: have release_revisions() release \"cmdline\"\n    @@ t/t1060-object-corruption.sh\n      \n      test_description='see how we handle various forms of corruption'\n     +\n    -+TEST_PASSES_SANITIZE_LEAK=true\n      . ./test-lib.sh\n      \n      # convert \"1234abcd\" to \".git/objects/12/34abcd\"\n     \n    - ## t/t2015-checkout-unborn.sh ##\n    -@@ t/t2015-checkout-unborn.sh: test_description='checkout from unborn branch'\n    + ## t/t3303-notes-subtrees.sh ##\n    +@@ t/t3303-notes-subtrees.sh: test_description='Test commit notes organized in subtrees'\n      GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n      export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n      \n     +TEST_PASSES_SANITIZE_LEAK=true\n      . ./test-lib.sh\n      \n    - test_expect_success 'setup' '\n    + number_of_commits=100\n     \n    - ## t/t3408-rebase-multi-line.sh ##\n    -@@ t/t3408-rebase-multi-line.sh: test_description='rebasing a commit with multi-line first paragraph.'\n    - GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n    - export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n    + ## t/t3305-notes-fanout.sh ##\n    +@@\n    + \n    + test_description='Test that adding/removing many notes triggers automatic fanout restructuring'\n      \n     +TEST_PASSES_SANITIZE_LEAK=true\n      . ./test-lib.sh\n      \n    - test_expect_success setup '\n    + path_has_fanout() {\n     \n    - ## t/t4207-log-decoration-colors.sh ##\n    -@@ t/t4207-log-decoration-colors.sh: test_description='Test for \"git log --decorate\" colors'\n    + ## t/t3408-rebase-multi-line.sh ##\n    +@@ t/t3408-rebase-multi-line.sh: test_description='rebasing a commit with multi-line first paragraph.'\n      GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n      export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n      \n    @@ t/t4207-log-decoration-colors.sh: test_description='Test for \"git log --decorate\n      \n      test_expect_success setup '\n     \n    - ## t/t4212-log-corrupt.sh ##\n    + ## t/t4027-diff-submodule.sh ##\n     @@\n      \n    - test_description='git log with invalid commit headers'\n    + test_description='difference in submodules'\n      \n     +TEST_PASSES_SANITIZE_LEAK=true\n      . ./test-lib.sh\n    + . \"$TEST_DIRECTORY\"/lib-diff.sh\n      \n    - test_expect_success 'setup' '\n     \n    - ## t/t5313-pack-bounds-checks.sh ##\n    + ## t/t4128-apply-root.sh ##\n     @@\n    - #!/bin/sh\n      \n    - test_description='bounds-checking of access to mmapped on-disk file formats'\n    -+\n    -+TEST_PASSES_SANITIZE_LEAK=true\n    - . ./test-lib.sh\n    - \n    - clear_base () {\n    -\n    - ## t/t5322-pack-objects-sparse.sh ##\n    -@@ t/t5322-pack-objects-sparse.sh: test_description='pack-objects object selection using sparse algorithm'\n    - GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n    - export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n    - \n    -+TEST_PASSES_SANITIZE_LEAK=true\n    - . ./test-lib.sh\n    - \n    - test_expect_success 'setup repo' '\n    -\n    - ## t/t5506-remote-groups.sh ##\n    -@@ t/t5506-remote-groups.sh: test_description='git remote group handling'\n    - GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n    - export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n    + test_description='apply same filename'\n      \n     +TEST_PASSES_SANITIZE_LEAK=true\n      . ./test-lib.sh\n      \n    - mark() {\n    + test_expect_success 'setup' '\n     \n    - ## t/t5513-fetch-track.sh ##\n    + ## t/t4212-log-corrupt.sh ##\n     @@\n      \n    - test_description='fetch follows remote-tracking branches correctly'\n    + test_description='git log with invalid commit headers'\n      \n     +TEST_PASSES_SANITIZE_LEAK=true\n      . ./test-lib.sh\n      \n    - test_expect_success setup '\n    + test_expect_success 'setup' '\n     \n      ## t/t5515-fetch-merge-logic.sh ##\n     @@ t/t5515-fetch-merge-logic.sh: export GIT_TEST_PROTOCOL_VERSION\n    @@ t/t5518-fetch-exit-status.sh: test_description='fetch exit status test'\n      \n      test_expect_success setup '\n     \n    - ## t/t5532-fetch-proxy.sh ##\n    -@@\n    - #!/bin/sh\n    - \n    - test_description='fetching via git:// using core.gitproxy'\n    -+\n    -+TEST_PASSES_SANITIZE_LEAK=true\n    - . ./test-lib.sh\n    - \n    - test_expect_success 'setup remote repo' '\n    -\n    - ## t/t5900-repo-selection.sh ##\n    -@@\n    - #!/bin/sh\n    - \n    - test_description='selecting remote repo in ambiguous cases'\n    -+\n    -+TEST_PASSES_SANITIZE_LEAK=true\n    - . ./test-lib.sh\n    - \n    - reset() {\n    -\n      ## t/t6002-rev-list-bisect.sh ##\n     @@\n      #\n    @@ t/t6003-rev-list-topo-order.sh\n      . \"$TEST_DIRECTORY\"/lib-t6000.sh # t6xxx specific functions\n      \n     \n    - ## t/t6018-rev-list-glob.sh ##\n    -@@ t/t6018-rev-list-glob.sh: test_description='rev-list/rev-parse --glob'\n    - GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n    - export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n    + ## t/t6005-rev-list-count.sh ##\n    +@@\n    + \n    + test_description='git rev-list --max-count and --skip test'\n      \n     +TEST_PASSES_SANITIZE_LEAK=true\n      . ./test-lib.sh\n      \n    - commit () {\n    + test_expect_success 'setup' '\n     \n    - ## t/t6019-rev-list-ancestry-path.sh ##\n    -@@ t/t6019-rev-list-ancestry-path.sh: test_description='--ancestry-path'\n    + ## t/t6018-rev-list-glob.sh ##\n    +@@ t/t6018-rev-list-glob.sh: test_description='rev-list/rev-parse --glob'\n      GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n      export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n      \n     +TEST_PASSES_SANITIZE_LEAK=true\n      . ./test-lib.sh\n      \n    - test_merge () {\n    + commit () {\n     \n      ## t/t6100-rev-list-in-order.sh ##\n     @@\n    @@ t/t6100-rev-list-in-order.sh\n      \n      test_expect_success 'setup a commit history with trees, blobs' '\n     \n    - ## t/t6101-rev-parse-parents.sh ##\n    -@@ t/t6101-rev-parse-parents.sh: test_description='Test git rev-parse with different parent options'\n    - GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n    - export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n    - \n    -+TEST_PASSES_SANITIZE_LEAK=true\n    - . ./test-lib.sh\n    - \n    - test_cmp_rev_output () {\n    -\n    - ## t/t6114-keep-packs.sh ##\n    -@@\n    - #!/bin/sh\n    - \n    - test_description='rev-list with .keep packs'\n    -+\n    -+TEST_PASSES_SANITIZE_LEAK=true\n    - . ./test-lib.sh\n    - \n    - test_expect_success 'setup' '\n    -\n    - ## t/t7702-repack-cyclic-alternate.sh ##\n    -@@\n    - #\n    - \n    - test_description='repack involving cyclic alternate'\n    -+\n    -+TEST_PASSES_SANITIZE_LEAK=true\n    - . ./test-lib.sh\n    - \n    - test_expect_success setup '\n    -\n      ## t/t9100-git-svn-basic.sh ##\n     @@ t/t9100-git-svn-basic.sh: test_description='git svn basic tests'\n      GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n    @@ t/t9106-git-svn-commit-diff-clobber.sh\n      \n      test_expect_success 'initialize repo' '\n     \n    + ## t/t9115-git-svn-dcommit-funky-renames.sh ##\n    +@@\n    + \n    + test_description='git svn dcommit can commit renames of files with ugly names'\n    + \n    ++TEST_FAILS_SANITIZE_LEAK=true\n    + . ./lib-git-svn.sh\n    + \n    + test_expect_success 'load repository with strange names' '\n    +\n    + ## t/t9116-git-svn-log.sh ##\n    +@@\n    + #\n    + \n    + test_description='git svn log tests'\n    ++\n    ++TEST_FAILS_SANITIZE_LEAK=true\n    + . ./lib-git-svn.sh\n    + \n    + test_expect_success 'setup repository and import' '\n    +\n      ## t/t9122-git-svn-author.sh ##\n     @@\n      #!/bin/sh\n    @@ t/t9122-git-svn-author.sh\n      \n      test_expect_success 'setup svn repository' '\n     \n    - ## t/t9129-git-svn-i18n-commitencoding.sh ##\n    + ## t/t9127-git-svn-partial-rebuild.sh ##\n     @@\n    + #\n      \n    - test_description='git svn honors i18n.commitEncoding in config'\n    - \n    + test_description='git svn partial-rebuild tests'\n    ++\n     +TEST_FAILS_SANITIZE_LEAK=true\n      . ./lib-git-svn.sh\n      \n    - compare_git_head_with () {\n    + test_expect_success 'initialize svnrepo' '\n     \n    - ## t/t9139-git-svn-non-utf8-commitencoding.sh ##\n    + ## t/t9129-git-svn-i18n-commitencoding.sh ##\n     @@\n      \n    - test_description='git svn refuses to dcommit non-UTF8 messages'\n    + test_description='git svn honors i18n.commitEncoding in config'\n      \n     +TEST_FAILS_SANITIZE_LEAK=true\n      . ./lib-git-svn.sh\n      \n    - # ISO-2022-JP can pass for valid UTF-8, so skipping that in this test\n    + compare_git_head_with () {\n     \n    - ## t/t9148-git-svn-propset.sh ##\n    + ## t/t9132-git-svn-broken-symlink.sh ##\n     @@\n      \n    - test_description='git svn propset tests'\n    + test_description='test that git handles an svn repository with empty symlinks'\n      \n     +TEST_FAILS_SANITIZE_LEAK=true\n      . ./lib-git-svn.sh\n    - \n    - test_expect_success 'setup propset via import' '\n    + test_expect_success 'load svn dumpfile' '\n    + \tsvnadmin load \"$rawsvnrepo\" <<EOF\n     \n    - ## t/t9151-svn-mergeinfo.sh ##\n    + ## t/t9139-git-svn-non-utf8-commitencoding.sh ##\n     @@\n      \n    - test_description='git-svn svn mergeinfo properties'\n    + test_description='git svn refuses to dcommit non-UTF8 messages'\n      \n     +TEST_FAILS_SANITIZE_LEAK=true\n      . ./lib-git-svn.sh\n      \n    - test_expect_success 'load svn dump' \"\n    + # ISO-2022-JP can pass for valid UTF-8, so skipping that in this test\n     \n    - ## t/t9157-git-svn-fetch-merge.sh ##\n    + ## t/t9146-git-svn-empty-dirs.sh ##\n     @@\n    - #\n    + # Copyright (c) 2009 Eric Wong\n      \n    - test_description='git svn merge detection'\n    + test_description='git svn creates empty directories'\n     +\n     +TEST_FAILS_SANITIZE_LEAK=true\n      . ./lib-git-svn.sh\n      \n    - svn_ver=\"$(svn --version --quiet)\"\n    + test_expect_success 'initialize repo' '\n     \n    - ## t/t9159-git-svn-no-parent-mergeinfo.sh ##\n    + ## t/t9148-git-svn-propset.sh ##\n     @@\n    - #!/bin/sh\n    - test_description='git svn handling of root commits in merge ranges'\n    -+\n    + \n    + test_description='git svn propset tests'\n    + \n     +TEST_FAILS_SANITIZE_LEAK=true\n      . ./lib-git-svn.sh\n      \n    - svn_ver=\"$(svn --version --quiet)\"\n    + test_expect_success 'setup propset via import' '\n     \n    - ## t/t9161-git-svn-mergeinfo-push.sh ##\n    + ## t/t9151-svn-mergeinfo.sh ##\n     @@\n      \n    - test_description='git-svn svn mergeinfo propagation'\n    + test_description='git-svn svn mergeinfo properties'\n      \n     +TEST_FAILS_SANITIZE_LEAK=true\n      . ./lib-git-svn.sh\n      \n      test_expect_success 'load svn dump' \"\n     \n    + ## t/t9160-git-svn-preserve-empty-dirs.sh ##\n    +@@ t/t9160-git-svn-preserve-empty-dirs.sh: This test uses git to clone a Subversion repository that contains empty\n    + directories, and checks that corresponding directories are created in the\n    + local Git repository with placeholder files.'\n    + \n    ++TEST_FAILS_SANITIZE_LEAK=true\n    + . ./lib-git-svn.sh\n    + \n    + GIT_REPO=git-svn-repo\n    +\n      ## t/t9162-git-svn-dcommit-interactive.sh ##\n     @@\n      # Copyright (c) 2011 Frédéric Heitzmann\n    @@ t/t9162-git-svn-dcommit-interactive.sh\n      \n      test_expect_success 'initialize repo' '\n     \n    - ## t/t9163-git-svn-reset-clears-caches.sh ##\n    -@@ t/t9163-git-svn-reset-clears-caches.sh: test_description='git svn reset clears memoized caches'\n    - GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n    - export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n    - \n    -+TEST_FAILS_SANITIZE_LEAK=true\n    - . ./lib-git-svn.sh\n    - \n    - svn_ver=\"$(svn --version --quiet)\"\n    -\n    - ## t/t9165-git-svn-fetch-merge-branch-of-branch.sh ##\n    + ## t/t9164-git-svn-dcommit-concurrent.sh ##\n     @@\n      #\n      \n    - test_description='git svn merge detection'\n    + test_description='concurrent git svn dcommit'\n     +\n     +TEST_FAILS_SANITIZE_LEAK=true\n      . ./lib-git-svn.sh\n      \n    - svn_ver=\"$(svn --version --quiet)\"\n    + \n     \n    - ## t/t9166-git-svn-fetch-merge-branch-of-branch2.sh ##\n    -@@\n    - #\n    + ## t/t9501-gitweb-standalone-http-status.sh ##\n    +@@ t/t9501-gitweb-standalone-http-status.sh: code and message.'\n    + GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n    + export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n      \n    - test_description='git svn merge detection'\n    -+\n    -+TEST_FAILS_SANITIZE_LEAK=true\n    - . ./lib-git-svn.sh\n    ++TEST_PASSES_SANITIZE_LEAK=true\n    + . ./lib-gitweb.sh\n      \n    - svn_ver=\"$(svn --version --quiet)\"\n    + #\n -:  ----------- > 19:  d5b7a8a690e revisions API: have release_revisions() release \"filter\"\n18:  fc486e1bc46 ! 20:  857880bb4b6 revisions API: have release_revisions() release \"grep_filter\"\n    @@ Commit message\n         revisions API: have release_revisions() release \"grep_filter\"\n     \n         Extend the the release_revisions() function so that it frees the\n    -    \"grep_filter\" in the \"struct ref_info\".\n    +    \"grep_filter\" in the \"struct ref_info\".This allows us to mark a test\n    +    as passing under \"TEST_PASSES_SANITIZE_LEAK=true\".\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## revision.c ##\n     @@ revision.c: void release_revisions(struct rev_info *revs)\n    - \tobject_array_clear(&revs->pending);\n      \trelease_revisions_cmdline(&revs->cmdline);\n    + \tlist_objects_filter_release(&revs->filter);\n      \trelease_revisions_mailmap(revs->mailmap);\n     +\tfree_grep_patterns(&revs->grep_filter);\n      }\n      \n      static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n    +\n    + ## t/t9151-svn-mergeinfo.sh ##\n    +@@\n    + \n    + test_description='git-svn svn mergeinfo properties'\n    + \n    +-TEST_FAILS_SANITIZE_LEAK=true\n    + . ./lib-git-svn.sh\n    + \n    + test_expect_success 'load svn dump' \"\n19:  b5948be0d4e ! 21:  f98d473f08e revisions API: have release_revisions() release \"prune_data\"\n    @@ diff-lib.c: int do_diff_cache(const struct object_id *tree_oid, struct diff_opti\n     \n      ## revision.c ##\n     @@ revision.c: void release_revisions(struct rev_info *revs)\n    - \trelease_revisions_commit_list(revs);\n      \tobject_array_clear(&revs->pending);\n      \trelease_revisions_cmdline(&revs->cmdline);\n    + \tlist_objects_filter_release(&revs->filter);\n     +\tclear_pathspec(&revs->prune_data);\n      \trelease_revisions_mailmap(revs->mailmap);\n      \tfree_grep_patterns(&revs->grep_filter);\n20:  fa53e81c7c0 ! 22:  827430315fa revisions API: clear \"boundary_commits\" in release_revisions()\n    @@ Commit message\n         revisions API: clear \"boundary_commits\" in release_revisions()\n     \n         Clear the \"boundary_commits\" object_array in release_revisions(). This\n    -    makes a *lot* of tests pass under SANITIZE=leak, including most of the\n    -    t/t[0-9]*git-svn*.sh tests.\n    +    makes a few more tests pass under SANITIZE=leak, including\n    +    \"t/t4126-apply-empty.sh\" which started failed as an UNLEAK() in\n    +    cmd_format_patch() was removed in a preceding commit.\n     \n    -    This includes the tests we had false-positive passes on before my\n    -    6798b08e848 (perl Git.pm: don't ignore signalled failure in\n    -    _cmd_close(), 2022-02-01), now they pass for real.\n    -\n    -    Since there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to\n    -    list those that don't pass than to touch most of those 66. So let's\n    -    introduce a \"TEST_FAILS_SANITIZE_LEAK=true\", which if set in the tests\n    -    won't cause lib-git-svn.sh to set \"TEST_PASSES_SANITIZE_LEAK=true.\n    -\n    -    This change also marks all the tests that we removed\n    -    \"TEST_FAILS_SANITIZE_LEAK=true\" from in an earlier commit due to\n    -    removing the UNLEAK() from cmd_format_patch(), we can now assert that\n    -    its API use doesn't leak any \"struct rev_info\" memory.\n    +    This also re-marks the various tests relying on \"git format-patch\" as\n    +    passing under \"SANITIZE=leak\", in the preceding \"revisions API users:\n    +    use release_revisions() in builtin/log.c\" commit those were marked as\n    +    failing as we removed the UNLEAK(rev) from cmd_format_patch() in\n    +    \"builtin/log.c\".\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## revision.c ##\n     @@ revision.c: void release_revisions(struct rev_info *revs)\n    + \t\treturn;\n      \trelease_revisions_commit_list(revs);\n      \tobject_array_clear(&revs->pending);\n    - \trelease_revisions_cmdline(&revs->cmdline);\n     +\tobject_array_clear(&revs->boundary_commits);\n    + \trelease_revisions_cmdline(&revs->cmdline);\n    + \tlist_objects_filter_release(&revs->filter);\n      \tclear_pathspec(&revs->prune_data);\n    - \trelease_revisions_mailmap(revs->mailmap);\n    - \tfree_grep_patterns(&revs->grep_filter);\n     \n      ## t/t4021-format-patch-numbered.sh ##\n     @@\n    @@ t/t9001-send-email.sh: test_description='git send-email'\n      . ./test-lib.sh\n      \n      # May be altered later in the test\n    +\n    + ## t/t9116-git-svn-log.sh ##\n    +@@\n    + \n    + test_description='git svn log tests'\n    + \n    +-TEST_FAILS_SANITIZE_LEAK=true\n    + . ./lib-git-svn.sh\n    + \n    + test_expect_success 'setup repository and import' '\n21:  ccf276641d6 ! 23:  c559b7d538b revisions API: release \"reflog_info\" in release revisions()\n    @@ Commit message\n     \n      ## reflog-walk.c ##\n     @@\n    - #include \"reflog-walk.h\"\n      \n      struct complete_reflogs {\n    --\tchar *ref;\n    -+\tconst char *ref;\n    - \tconst char *short_ref;\n    + \tchar *ref;\n    +-\tconst char *short_ref;\n    ++\tchar *short_ref;\n      \tstruct reflog_info {\n      \t\tstruct object_id ooid, noid;\n    + \t\tchar *email;\n     @@ reflog-walk.c: static void free_complete_reflog(struct complete_reflogs *array)\n    - \t\tfree(array->items[i].message);\n      \t}\n      \tfree(array->items);\n    --\tfree(array->ref);\n    -+\tfree((char *)array->ref);\n    -+\tfree((char *)array->short_ref);\n    + \tfree(array->ref);\n    ++\tfree(array->short_ref);\n      \tfree(array);\n      }\n      \n22:  f387b9e744a ! 24:  c3a22f5b474 revisions API: call diff_free(&revs->pruning) in revisions_release()\n    @@ Metadata\n      ## Commit message ##\n         revisions API: call diff_free(&revs->pruning) in revisions_release()\n     \n    -    Call diff_free() on the \"pruning\" member of \"struct rev_info\". We'll\n    -    handle the \"diffopt\" member of the same type some other time, doing so\n    -    will require us to untangle the \"no_free\" conditions I added in\n    -    e900d494dcf (diff: add an API for deferred freeing, 2021-02-11).\n    -\n    -    Even without \"diffopt\" addressing leaks in \"pruning\" makes several\n    -    tests pass under SANITIZE=leak.\n    +    Call diff_free() on the \"pruning\" member of \"struct rev_info\".  Doing\n    +    so makes several tests pass under SANITIZE=leak.\n     \n         This was also the last missing piece that allows us to remove the\n         UNLEAK() in \"cmd_diff\" and \"cmd_diff_index\", which allows us to use\n    @@ revision.c: void release_revisions(struct rev_info *revs)\n      \tclear_pathspec(&revs->prune_data);\n      \trelease_revisions_mailmap(revs->mailmap);\n      \tfree_grep_patterns(&revs->grep_filter);\n    -+\t/* TODO (need to handle \"no_free\"): diff_free(&revs->diffopt) */\n     +\tdiff_free(&revs->pruning);\n      \treflog_walk_info_release(revs->reflog_info);\n      }\n      \n     \n    - ## t/t2016-checkout-patch.sh ##\n    -@@\n    + ## t/t1001-read-tree-m-2way.sh ##\n    +@@ t/t1001-read-tree-m-2way.sh: In the test, these paths are used:\n    + \tyomin   - not in H or M\n    + '\n    + \n    ++TEST_PASSES_SANITIZE_LEAK=true\n    + . ./test-lib.sh\n    + . \"$TEST_DIRECTORY\"/lib-read-tree.sh\n      \n    - test_description='git checkout --patch'\n    +\n    + ## t/t1002-read-tree-m-u-2way.sh ##\n    +@@ t/t1002-read-tree-m-u-2way.sh: This is identical to t1001, but uses -u to update the work tree as well.\n    + \n    + '\n      \n     +TEST_PASSES_SANITIZE_LEAK=true\n    - . ./lib-patch-mode.sh\n    + . ./test-lib.sh\n    + . \"$TEST_DIRECTORY\"/lib-read-tree.sh\n    + \n    +\n    + ## t/t2200-add-update.sh ##\n    +@@ t/t2200-add-update.sh: only the updates to dir/sub.\n    + Also tested are \"git add -u\" without limiting, and \"git add -u\"\n    + without contents changes, and other conditions'\n    + \n    ++TEST_PASSES_SANITIZE_LEAK=true\n    + . ./test-lib.sh\n      \n    - test_expect_success PERL 'setup' '\n    + test_expect_success setup '\n     \n      ## t/t4039-diff-assume-unchanged.sh ##\n     @@\n23:  fd457f0d85c ! 25:  dabbb430ece revisions API: have release_revisions() release \"date_mode\"\n    @@ Commit message\n      ## revision.c ##\n     @@ revision.c: void release_revisions(struct rev_info *revs)\n      \trelease_revisions_cmdline(&revs->cmdline);\n    - \tobject_array_clear(&revs->boundary_commits);\n    + \tlist_objects_filter_release(&revs->filter);\n      \tclear_pathspec(&revs->prune_data);\n     +\tdate_mode_release(&revs->date_mode);\n      \trelease_revisions_mailmap(revs->mailmap);\n      \tfree_grep_patterns(&revs->grep_filter);\n    - \t/* TODO (need to handle \"no_free\"): diff_free(&revs->diffopt) */\n    + \tdiff_free(&revs->pruning);\n24:  e02a097b884 ! 26:  644d1b54afe revisions API: have release_revisions() release \"topo_walk_info\"\n    @@ revision.c: static void release_revisions_mailmap(struct string_list *mailmap)\n      {\n      \tif (!revs)\n     @@ revision.c: void release_revisions(struct rev_info *revs)\n    - \t/* TODO (need to handle \"no_free\"): diff_free(&revs->diffopt) */\n    + \tfree_grep_patterns(&revs->grep_filter);\n      \tdiff_free(&revs->pruning);\n      \treflog_walk_info_release(revs->reflog_info);\n     +\trelease_revisions_topo_walk_info(revs->topo_walk_info);\n    @@ revision.c: static void compute_indegrees_to_depth(struct rev_info *revs,\n      \tclear_author_date_slab(&info->author_date);\n     +\tfree(info);\n     +}\n    -+\n    -+static void reset_topo_walk(struct rev_info *revs)\n    -+{\n    -+\tstruct topo_walk_info *info = revs->topo_walk_info;\n      \n     -\tFREE_AND_NULL(revs->topo_walk_info);\n    -+\trelease_revisions_topo_walk_info(info);\n    ++static void reset_topo_walk(struct rev_info *revs)\n    ++{\n    ++\trelease_revisions_topo_walk_info(revs->topo_walk_info);\n     +\trevs->topo_walk_info = NULL;\n      }\n      \n13:  d18bfa5691f ! 27:  2a4d50a0ec8 revisions API users: use release_revisions() in wt-status.c edge case\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    revisions API users: use release_revisions() in wt-status.c edge case\n    +    revisions API: add a TODO for diff_free(&revs->diffopt)\n     \n    -    Use release_revisions() for the \"struct rev_info\" in\n    -    has_unstaged_changes() which didn't require the addition of a \"goto\"\n    -    pattern, but needed us to refactor another \"return fn(...)\" pattern.\n    +    Add a TODO comment indicating that we should release \"diffopt\" in\n    +    release_revisions(). In a preceding commit we started releasing the\n    +    \"pruning\" member of the same type, but handling \"diffopt\" will require\n    +    us to untangle the \"no_free\" conditions I added in e900d494dcf (diff:\n    +    add an API for deferred freeing, 2021-02-11).\n     \n    -    This change is split from preceding changes to make similar\n    -    conversions in those commits easier to review, as they are all\n    -    repetitions of the same pattern.\n    +    Let's leave a TODO comment to that effect, and so that we don't forget\n    +    refactor code that was changed to use release_revisions() in earlier\n    +    commits to stop using the \"diffopt\" member after a call to\n    +    release_revisions(). This works currently, but would become a logic\n    +    error as soon as we started freeing \"diffopt\". Doing that change now\n    +    doesn't harm anything, and future-proofs us against a later change to\n    +    release_revisions().\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    + ## diff-lib.c ##\n    +@@ diff-lib.c: int index_differs_from(struct repository *r,\n    + {\n    + \tstruct rev_info rev;\n    + \tstruct setup_revision_opt opt;\n    ++\tunsigned has_changes;\n    + \n    + \trepo_init_revisions(r, &rev, NULL);\n    + \tmemset(&opt, 0, sizeof(opt));\n    +@@ diff-lib.c: int index_differs_from(struct repository *r,\n    + \t\tdiff_flags_or(&rev.diffopt.flags, flags);\n    + \trev.diffopt.ita_invisible_in_index = ita_invisible_in_index;\n    + \trun_diff_index(&rev, 1);\n    ++\thas_changes = rev.diffopt.flags.has_changes;\n    + \trelease_revisions(&rev);\n    +-\treturn (rev.diffopt.flags.has_changes != 0);\n    ++\treturn (has_changes != 0);\n    + }\n    + \n    + static struct strbuf *idiff_prefix_cb(struct diff_options *opt, void *data)\n    +\n    + ## revision.c ##\n    +@@ revision.c: void release_revisions(struct rev_info *revs)\n    + \tdate_mode_release(&revs->date_mode);\n    + \trelease_revisions_mailmap(revs->mailmap);\n    + \tfree_grep_patterns(&revs->grep_filter);\n    ++\t/* TODO (need to handle \"no_free\"): diff_free(&revs->diffopt) */\n    + \tdiff_free(&revs->pruning);\n    + \treflog_walk_info_release(revs->reflog_info);\n    + \trelease_revisions_topo_walk_info(revs->topo_walk_info);\n    +\n      ## wt-status.c ##\n     @@ wt-status.c: int has_unstaged_changes(struct repository *r, int ignore_submodules)\n      \trev_info.diffopt.flags.quick = 1;\n      \tdiff_setup_done(&rev_info.diffopt);\n      \tresult = run_diff_files(&rev_info, 0);\n    ++\tresult = diff_result_code(&rev_info.diffopt, result);\n    + \trelease_revisions(&rev_info);\n     -\treturn diff_result_code(&rev_info.diffopt, result);\n    ++\treturn result;\n    + }\n    + \n    + /**\n    +@@ wt-status.c: int has_uncommitted_changes(struct repository *r,\n    + \n    + \tdiff_setup_done(&rev_info.diffopt);\n    + \tresult = run_diff_index(&rev_info, 1);\n     +\tresult = diff_result_code(&rev_info.diffopt, result);\n    -+\trelease_revisions(&rev_info);\n    + \trelease_revisions(&rev_info);\n    +-\treturn diff_result_code(&rev_info.diffopt, result);\n     +\treturn result;\n      }\n      \n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452034","messageId":"patch-v2-05.27-4c0718b43d7-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 05/27] revision.[ch]: split freeing of revs->commit into a function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:31:55Z","receivedAt":"2022-03-23T20:32:44Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Move the existing code for invoking free_commit_list() and setting\nrevs->commits to NULL into a new release_revisions_commit_list()\nfunction. This will be used as part of a general free()-ing mechanism\nfor \"struct rev_info\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 15 +++++++++++----\n 1 file changed, 11 insertions(+), 4 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex 2646b78990e..303d1188207 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2923,6 +2923,16 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \treturn left;\n }\n \n+static void release_revisions_commit_list(struct rev_info *revs)\n+{\n+\tstruct commit_list *commits = revs->commits;\n+\n+\tif (!commits)\n+\t\treturn;\n+\tfree_commit_list(commits);\n+\trevs->commits = NULL;\n+}\n+\n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n {\n \tstruct commit_list *l = xcalloc(1, sizeof(*l));\n@@ -4080,10 +4090,7 @@ static void create_boundary_commit_list(struct rev_info *revs)\n \t * boundary commits anyway.  (This is what the code has always\n \t * done.)\n \t */\n-\tif (revs->commits) {\n-\t\tfree_commit_list(revs->commits);\n-\t\trevs->commits = NULL;\n-\t}\n+\trelease_revisions_commit_list(revs);\n \n \t/*\n \t * Put all of the actual boundary commits from revs->boundary_commits\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452036","messageId":"patch-v2-06.27-7194aac88e1-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 06/27] revision.[ch]: provide and start using a release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:31:56Z","receivedAt":"2022-03-23T20:32:44Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The users of the revision.[ch] API's \"struct rev_info\" are a major\nsource of memory leaks in the test suite under SANITIZE=leak, which in\nturn adds a lot of noise when trying to mark up tests with\n\"TEST_PASSES_SANITIZE_LEAK=true\".\n\nThe users of that API are largely one-shot, e.g. \"git log\", so\narguably freeing the memory is a waste of time, but in many cases\nthey've actually been trying to free the memory, and just doing that\nin a buggy manner.\n\nLet's provide a release_revisions() function for these users, and\nstart migrating them over per the plan outlined in [1]. Right now this\nonly handles the \"pending\" member of the struct, but more will be\nadded in subsequent commits.\n\nEven though we only clear the \"pending\" member now, let's not leave a\ntrap in code like the pre-image of index_differs_from(), where we'd\nstart doing the wrong thing as soon as the release_revisions() learned\nto clear its \"diffopt\". I.e. we need to call release_revisions() after\nwe've inspected any state in \"struct rev_info\".\n\nThis leaves in place e.g. clear_pathspec(&rev.prune_data) in\nstash_working_tree() in builtin/stash.c, subsequent commits will teach\nrelease_revisions() to free \"prune_data\" and other members that in\nsome cases are individually cleared by users of \"struct rev_info\" by\nreaching into its members. Those subsequent commits will remove the\nrelevant calls to e.g. clear_pathspec().\n\nWe avoid amending code in index_differs_from() in diff-lib.c as well\nas wt_status_collect_changes_index(), has_unstaged_changes() and\nhas_uncommitted_changes() in wt-status.c in a way that assumes that we\nare already clearing the \"diffopt\" member. That will be handled in a\nsubsequent commit.\n\n1. https://lore.kernel.org/git/87a6k8daeu.fsf@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/checkout.c | 2 +-\n builtin/stash.c    | 3 +--\n diff-lib.c         | 2 +-\n range-diff.c       | 2 +-\n revision.c         | 7 +++++++\n revision.h         | 6 ++++++\n wt-status.c        | 5 +++--\n 7 files changed, 20 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 797681481d1..4d9e0bd3ac1 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -629,7 +629,7 @@ static void show_local_changes(struct object *head,\n \tdiff_setup_done(&rev.diffopt);\n \tadd_pending_object(&rev, head, NULL);\n \trun_diff_index(&rev, 0);\n-\tobject_array_clear(&rev.pending);\n+\trelease_revisions(&rev);\n }\n \n static void describe_detached_head(const char *msg, struct commit *commit)\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 242e73cbb09..8a8093c5eea 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1267,9 +1267,8 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps\n \n done:\n \tdiscard_index(&istate);\n-\tUNLEAK(rev);\n-\tobject_array_clear(&rev.pending);\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n \tstrbuf_release(&diff_output);\n \tremove_path(stash_index_path.buf);\n \treturn ret;\ndiff --git a/diff-lib.c b/diff-lib.c\nindex ca085a03efc..d6800274bd5 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -662,7 +662,7 @@ int index_differs_from(struct repository *r,\n \t\tdiff_flags_or(&rev.diffopt.flags, flags);\n \trev.diffopt.ita_invisible_in_index = ita_invisible_in_index;\n \trun_diff_index(&rev, 1);\n-\tobject_array_clear(&rev.pending);\n+\trelease_revisions(&rev);\n \treturn (rev.diffopt.flags.has_changes != 0);\n }\n \ndiff --git a/range-diff.c b/range-diff.c\nindex b72eb9fdbee..39cc010c628 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -596,6 +596,6 @@ int is_range_diff_range(const char *arg)\n \t}\n \n \tfree(copy);\n-\tobject_array_clear(&revs.pending);\n+\trelease_revisions(&revs);\n \treturn negative > 0 && positive > 0;\n }\ndiff --git a/revision.c b/revision.c\nindex 303d1188207..90bac9ada03 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2933,6 +2933,13 @@ static void release_revisions_commit_list(struct rev_info *revs)\n \trevs->commits = NULL;\n }\n \n+void release_revisions(struct rev_info *revs)\n+{\n+\tif (!revs)\n+\t\treturn;\n+\tobject_array_clear(&revs->pending);\n+}\n+\n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n {\n \tstruct commit_list *l = xcalloc(1, sizeof(*l));\ndiff --git a/revision.h b/revision.h\nindex 5bc59c7bfe1..ad325a30453 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -377,6 +377,12 @@ void repo_init_revisions(struct repository *r,\n int setup_revisions(int argc, const char **argv, struct rev_info *revs,\n \t\t    struct setup_revision_opt *);\n \n+/**\n+ * Free data allocated in a \"struct rev_info\" after it's been setup\n+ * with repo_init_revisions() and setup_revisions().\n+ */\n+void release_revisions(struct rev_info *revs);\n+\n void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,\n \t\t\tconst struct option *options,\n \t\t\tconst char * const usagestr[]);\ndiff --git a/wt-status.c b/wt-status.c\nindex d33f9272b72..922cf787f95 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -662,7 +662,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)\n \n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_index(&rev, 1);\n-\tobject_array_clear(&rev.pending);\n+\trelease_revisions(&rev);\n \tclear_pathspec(&rev.prune_data);\n }\n \n@@ -2545,6 +2545,7 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)\n \trev_info.diffopt.flags.quick = 1;\n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_files(&rev_info, 0);\n+\trelease_revisions(&rev_info);\n \treturn diff_result_code(&rev_info.diffopt, result);\n }\n \n@@ -2577,7 +2578,7 @@ int has_uncommitted_changes(struct repository *r,\n \n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_index(&rev_info, 1);\n-\tobject_array_clear(&rev_info.pending);\n+\trelease_revisions(&rev_info);\n \treturn diff_result_code(&rev_info.diffopt, result);\n }\n \n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452037","messageId":"patch-v2-04.27-64a76629e23-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 04/27] format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:31:54Z","receivedAt":"2022-03-23T20:32:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix two memory leaks in \"struct rev_info\" by freeing that memory in\ncmd_format_patch(). These two are unusual special-cases in being in\nthe \"struct rev_info\", but not being \"owned\" by the code in\nrevision.c. I.e. they're members of the struct so that this code in\n\"builtin/log.c\" can pass information code in log-tree.c.\n\nSee 20ff06805c6 (format-patch: resurrect extra headers from config,\n2006-06-02) and d1566f7883f (git-format-patch: Make the second and\nsubsequent mails replies to the first, 2006-07-14) for the initial\nintroduction of \"extra_headers\" and \"ref_message_ids\".\n\nWe can count on repo_init_revisions() memset()-ing this data to 0\nhowever, so we can count on it being either NULL or something we\nallocated. In the case of \"extra_headers\" let's add a local \"char *\"\nvariable to hold it, to avoid the eventual cast from \"const char *\"\nwhen we free() it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/log.c | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 634dc782cce..6f9928fabfe 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1747,6 +1747,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tstruct commit *commit;\n \tstruct commit **list = NULL;\n \tstruct rev_info rev;\n+\tchar *to_free = NULL;\n \tstruct setup_revision_opt s_r_opt;\n \tint nr = 0, total, i;\n \tint use_stdout = 0;\n@@ -1947,7 +1948,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tstrbuf_addch(&buf, '\\n');\n \t}\n \n-\trev.extra_headers = strbuf_detach(&buf, NULL);\n+\trev.extra_headers = to_free = strbuf_detach(&buf, NULL);\n \n \tif (from) {\n \t\tif (split_ident_line(&rev.from_ident, from, strlen(from)))\n@@ -2284,6 +2285,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tstrbuf_release(&rdiff1);\n \tstrbuf_release(&rdiff2);\n \tstrbuf_release(&rdiff_title);\n+\tfree(to_free);\n+\tif (rev.ref_message_ids)\n+\t\tstring_list_clear(rev.ref_message_ids, 0);\n+\tfree(rev.ref_message_ids);\n \tUNLEAK(rev);\n \treturn 0;\n }\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452038","messageId":"patch-v2-07.27-3cb36d358d6-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 07/27] revisions API users: add straightforward release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:31:57Z","receivedAt":"2022-03-23T20:32:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a release_revisions() to various users of \"struct rev_list\" in\nthose straightforward cases where we only need to add the\nrelease_revisions() call to the end of a block, and don't need to\ne.g. refactor anything to use a \"goto cleanup\" pattern.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n add-interactive.c                | 1 +\n bisect.c                         | 2 ++\n builtin/add.c                    | 1 +\n builtin/am.c                     | 3 +++\n builtin/bisect--helper.c         | 2 ++\n builtin/blame.c                  | 1 +\n builtin/checkout.c               | 1 +\n builtin/commit.c                 | 1 +\n builtin/describe.c               | 2 ++\n builtin/fast-export.c            | 1 +\n builtin/merge.c                  | 2 ++\n builtin/pack-objects.c           | 2 ++\n builtin/prune.c                  | 1 +\n builtin/reflog.c                 | 1 +\n builtin/shortlog.c               | 2 ++\n builtin/submodule--helper.c      | 1 +\n fmt-merge-msg.c                  | 1 +\n merge-ort.c                      | 1 +\n merge-recursive.c                | 1 +\n midx.c                           | 1 +\n pack-bitmap-write.c              | 1 +\n ref-filter.c                     | 1 +\n remote.c                         | 1 +\n sequencer.c                      | 3 +++\n shallow.c                        | 1 +\n submodule.c                      | 2 ++\n t/helper/test-revision-walking.c | 1 +\n wt-status.c                      | 1 +\n 28 files changed, 39 insertions(+)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex 72472103017..54cdfc82017 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -570,6 +570,7 @@ static int get_modified_files(struct repository *r,\n \n \t\tif (ps)\n \t\t\tclear_pathspec(&rev.prune_data);\n+\t\trelease_revisions(&rev);\n \t}\n \thashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);\n \tif (unmerged_count)\ndiff --git a/bisect.c b/bisect.c\nindex 9e6a2b7f201..cc6b8b6230d 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -884,6 +884,7 @@ static int check_ancestors(struct repository *r, int rev_nr,\n \t/* Clean up objects used, as they will be reused. */\n \tclear_commit_marks_many(rev_nr, rev, ALL_REV_FLAGS);\n \n+\trelease_revisions(&revs);\n \treturn res;\n }\n \n@@ -964,6 +965,7 @@ static void show_diff_tree(struct repository *r,\n \n \tsetup_revisions(ARRAY_SIZE(argv) - 1, argv, &opt, NULL);\n \tlog_tree_commit(&opt, commit);\n+\trelease_revisions(&opt);\n }\n \n /*\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 3ffb86a4338..f507d2191cd 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -340,6 +340,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)\n \n \tunlink(file);\n \tfree(file);\n+\trelease_revisions(&rev);\n \treturn 0;\n }\n \ndiff --git a/builtin/am.c b/builtin/am.c\nindex 0f4111bafa0..93bec62afa9 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -1397,6 +1397,7 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm\n \tadd_pending_object(&rev_info, &commit->object, \"\");\n \tdiff_setup_done(&rev_info.diffopt);\n \tlog_tree_commit(&rev_info, commit);\n+\trelease_revisions(&rev_info);\n }\n \n /**\n@@ -1429,6 +1430,7 @@ static void write_index_patch(const struct am_state *state)\n \tadd_pending_object(&rev_info, &tree->object, \"\");\n \tdiff_setup_done(&rev_info.diffopt);\n \trun_diff_index(&rev_info, 1);\n+\trelease_revisions(&rev_info);\n }\n \n /**\n@@ -1582,6 +1584,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \t\tadd_pending_oid(&rev_info, \"HEAD\", &our_tree, 0);\n \t\tdiff_setup_done(&rev_info.diffopt);\n \t\trun_diff_index(&rev_info, 1);\n+\t\trelease_revisions(&rev_info);\n \t}\n \n \tif (run_apply(state, index_path))\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex 8b2b259ff0d..e4d7b6779ae 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -555,6 +555,7 @@ static int bisect_skipped_commits(struct bisect_terms *terms)\n \treset_revision_walk();\n \n \tstrbuf_release(&commit_name);\n+\trelease_revisions(&revs);\n \tfclose(fp);\n \treturn 0;\n }\n@@ -1041,6 +1042,7 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar\n \t\t\t\t\t\toid_to_hex(&commit->object.oid));\n \n \t\t\treset_revision_walk();\n+\t\t\trelease_revisions(&revs);\n \t\t} else {\n \t\t\tstrvec_push(&argv_state, argv[i]);\n \t\t}\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 885b381ab83..24bac822c56 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1203,5 +1203,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \n cleanup:\n \tcleanup_scoreboard(&sb);\n+\trelease_revisions(&revs);\n \treturn 0;\n }\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 4d9e0bd3ac1..7ad4a7113c9 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1082,6 +1082,7 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne\n \n \t/* Clean up objects used, as they will be reused. */\n \trepo_clear_commit_marks(the_repository, ALL_REV_FLAGS);\n+\trelease_revisions(&revs);\n }\n \n static int switch_branches(const struct checkout_opts *opts,\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 009a1de0a3d..c7eda9bbb72 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1123,6 +1123,7 @@ static const char *find_author_by_nickname(const char *name)\n \t\tstrbuf_release(&buf);\n \t\tformat_commit_message(commit, \"%aN <%aE>\", &buf, &ctx);\n \t\tclear_mailmap(&mailmap);\n+\t\trelease_revisions(&revs);\n \t\treturn strbuf_detach(&buf, NULL);\n \t}\n \tdie(_(\"--author '%s' is not 'Name <email>' and matches no existing author\"), name);\ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex 42159cd26bd..a76f1a1a7a7 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -517,6 +517,7 @@ static void describe_blob(struct object_id oid, struct strbuf *dst)\n \n \ttraverse_commit_list(&revs, process_commit, process_object, &pcd);\n \treset_revision_walk();\n+\trelease_revisions(&revs);\n }\n \n static void describe(const char *arg, int last_one)\n@@ -667,6 +668,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)\n \t\t\t\tsuffix = NULL;\n \t\t\telse\n \t\t\t\tsuffix = dirty;\n+\t\t\trelease_revisions(&revs);\n \t\t}\n \t\tdescribe(\"HEAD\", 1);\n \t} else if (dirty) {\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex a7d72697fba..f34ae451ee3 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -1275,6 +1275,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)\n \t\tprintf(\"done\\n\");\n \n \trefspec_clear(&refspecs);\n+\trelease_revisions(&revs);\n \n \treturn 0;\n }\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex f178f5a3ee1..d9784d4891c 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -443,6 +443,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead\n \t}\n \twrite_file_buf(git_path_squash_msg(the_repository), out.buf, out.len);\n \tstrbuf_release(&out);\n+\trelease_revisions(&rev);\n }\n \n static void finish(struct commit *head_commit,\n@@ -998,6 +999,7 @@ static int evaluate_result(void)\n \t */\n \tcnt += count_unmerged_entries();\n \n+\trelease_revisions(&rev);\n \treturn cnt;\n }\n \ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 829ca359cf9..0aa194f68dc 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -3384,6 +3384,7 @@ static void read_packs_list_from_stdin(void)\n \tstrbuf_release(&buf);\n \tstring_list_clear(&include_packs, 0);\n \tstring_list_clear(&exclude_packs, 0);\n+\trelease_revisions(&revs);\n }\n \n static void read_object_list_from_stdin(void)\n@@ -3800,6 +3801,7 @@ static void get_object_list(int ac, const char **av)\n \tif (unpack_unreachable)\n \t\tloosen_unused_packed_objects();\n \n+\trelease_revisions(&revs);\n \toid_array_clear(&recent_objects);\n }\n \ndiff --git a/builtin/prune.c b/builtin/prune.c\nindex c2bcdc07db4..df376b2ed1e 100644\n--- a/builtin/prune.c\n+++ b/builtin/prune.c\n@@ -196,5 +196,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix)\n \t\tprune_shallow(show_only ? PRUNE_SHOW_ONLY : 0);\n \t}\n \n+\trelease_revisions(&revs);\n \treturn 0;\n }\ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex 9407f835cb6..592d5d33442 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -248,6 +248,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)\n \t\tif (verbose)\n \t\t\tprintf(_(\"Marking reachable objects...\"));\n \t\tmark_reachable_objects(&revs, 0, 0, NULL);\n+\t\trelease_revisions(&revs);\n \t\tif (verbose)\n \t\t\tputchar('\\n');\n \t}\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex fcde07c9367..35825f075e3 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -422,6 +422,8 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)\n \telse\n \t\tget_from_rev(&rev, &log);\n \n+\trelease_revisions(&rev);\n+\n \tshortlog_output(&log);\n \tif (log.file != stdout)\n \t\tfclose(log.file);\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 2f7c58362b2..0eeced8a10d 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1259,6 +1259,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \t\trun_diff_files(&rev, 0);\n \tprepare_submodule_summary(info, &list);\n \tstrvec_clear(&diff_args);\n+\trelease_revisions(&rev);\n \treturn 0;\n }\n \ndiff --git a/fmt-merge-msg.c b/fmt-merge-msg.c\nindex baca57d5b64..f48f44f9cd1 100644\n--- a/fmt-merge-msg.c\n+++ b/fmt-merge-msg.c\n@@ -699,6 +699,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,\n \t\t\tshortlog(origins.items[i].string,\n \t\t\t\t origins.items[i].util,\n \t\t\t\t head, &rev, opts, out);\n+\t\trelease_revisions(&rev);\n \t}\n \n \tstrbuf_complete_line(out);\ndiff --git a/merge-ort.c b/merge-ort.c\nindex 8545354dafd..4c0d4eba19b 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -1594,6 +1594,7 @@ static int find_first_merges(struct repository *repo,\n \t}\n \n \tobject_array_clear(&merges);\n+\trelease_revisions(&revs);\n \treturn result->nr;\n }\n \ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 32bbba5fbb1..acd13b2b069 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1160,6 +1160,7 @@ static int find_first_merges(struct repository *repo,\n \t}\n \n \tobject_array_clear(&merges);\n+\trelease_revisions(&revs);\n \treturn result->nr;\n }\n \ndiff --git a/midx.c b/midx.c\nindex 865170bad05..702c8a9b178 100644\n--- a/midx.c\n+++ b/midx.c\n@@ -1061,6 +1061,7 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr\n \tif (indexed_commits_nr_p)\n \t\t*indexed_commits_nr_p = cb.commits_nr;\n \n+\trelease_revisions(&revs);\n \treturn cb.commits;\n }\n \ndiff --git a/pack-bitmap-write.c b/pack-bitmap-write.c\nindex cab3eaa2acd..ea8e0b51cdf 100644\n--- a/pack-bitmap-write.c\n+++ b/pack-bitmap-write.c\n@@ -326,6 +326,7 @@ static void bitmap_builder_init(struct bitmap_builder *bb,\n \ttrace2_data_intmax(\"pack-bitmap-write\", the_repository,\n \t\t\t   \"num_maximal_commits\", num_maximal);\n \n+\trelease_revisions(&revs);\n \tfree_commit_list(reusable);\n }\n \ndiff --git a/ref-filter.c b/ref-filter.c\nindex 7838bd22b8d..a91688bbf17 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -2392,6 +2392,7 @@ static void reach_filter(struct ref_array *array,\n \t\tclear_commit_marks(merge_commit, ALL_REV_FLAGS);\n \t}\n \n+\trelease_revisions(&revs);\n \tfree(to_clear);\n }\n \ndiff --git a/remote.c b/remote.c\nindex 42a4e7106e1..fa3152a5d52 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -2172,6 +2172,7 @@ static int stat_branch_pair(const char *branch_name, const char *base,\n \tclear_commit_marks(theirs, ALL_REV_FLAGS);\n \n \tstrvec_clear(&argv);\n+\trelease_revisions(&revs);\n \treturn 1;\n }\n \ndiff --git a/sequencer.c b/sequencer.c\nindex 84eed9e96bc..29ec201af64 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1347,6 +1347,7 @@ void print_commit_summary(struct repository *r,\n \t\tlog_tree_commit(&rev, commit);\n \t}\n \n+\trelease_revisions(&rev);\n \tstrbuf_release(&format);\n }\n \n@@ -3415,6 +3416,7 @@ static int make_patch(struct repository *r,\n \t\tunuse_commit_buffer(commit, commit_buffer);\n \t}\n \tstrbuf_release(&buf);\n+\trelease_revisions(&log_tree_opt);\n \n \treturn res;\n }\n@@ -4527,6 +4529,7 @@ static int pick_commits(struct repository *r,\n \t\t\t\t\t      &log_tree_opt.diffopt);\n \t\t\t\tlog_tree_diff_flush(&log_tree_opt);\n \t\t\t}\n+\t\t\trelease_revisions(&log_tree_opt);\n \t\t}\n \t\tflush_rewritten_pending();\n \t\tif (!stat(rebase_path_rewritten_list(), &st) &&\ndiff --git a/shallow.c b/shallow.c\nindex 71e5876f377..2552f139f61 100644\n--- a/shallow.c\n+++ b/shallow.c\n@@ -261,6 +261,7 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,\n \t\tif ((o->flags & both_flags) == both_flags)\n \t\t\to->flags &= ~not_shallow_flag;\n \t}\n+\trelease_revisions(&revs);\n \treturn result;\n }\n \ndiff --git a/submodule.c b/submodule.c\nindex 5ace18a7d94..0510cb193b6 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -902,9 +902,11 @@ static void collect_changed_submodules(struct repository *r,\n \t\tdiff_rev.diffopt.format_callback_data = &data;\n \t\tdiff_rev.dense_combined_merges = 1;\n \t\tdiff_tree_combined_merge(commit, &diff_rev);\n+\t\trelease_revisions(&diff_rev);\n \t}\n \n \treset_revision_walk();\n+\trelease_revisions(&rev);\n }\n \n static void free_submodules_oids(struct string_list *submodules)\ndiff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c\nindex 625b2dbf822..4a45d5bac2a 100644\n--- a/t/helper/test-revision-walking.c\n+++ b/t/helper/test-revision-walking.c\n@@ -43,6 +43,7 @@ static int run_revision_walk(void)\n \t}\n \n \treset_revision_walk();\n+\trelease_revisions(&rev);\n \treturn got_revision;\n }\n \ndiff --git a/wt-status.c b/wt-status.c\nindex 922cf787f95..f9100621375 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1152,6 +1152,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)\n \t\trev.diffopt.b_prefix = \"w/\";\n \t\trun_diff_files(&rev, 0);\n \t}\n+\trelease_revisions(&rev);\n }\n \n static void wt_longstatus_print_tracking(struct wt_status *s)\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452039","messageId":"patch-v2-08.27-42ad1208934-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 08/27] revisions API users: use release_revisions() needing \"{ 0 }\" init","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:31:58Z","receivedAt":"2022-03-23T20:32:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use release_revisions() to various users of \"struct rev_list\" which\nneed to have their \"struct rev_info\" zero-initialized before we can\nstart using it. In all of these cases we might \"goto cleanup\" (or equivalent),\n\nFor the bundle.c code see the early exit case added in\n3bbbe467f29 (bundle verify: error out if called without an object\ndatabase, 2019-05-27).\n\nFor the relevant bisect.c code see 45b6370812c (bisect: libify\n`check_good_are_ancestors_of_bad` and its dependents, 2020-02-17).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n bisect.c                    | 18 ++++++++++++------\n builtin/submodule--helper.c |  3 ++-\n bundle.c                    | 12 ++++++++----\n 3 files changed, 22 insertions(+), 11 deletions(-)\n\ndiff --git a/bisect.c b/bisect.c\nindex cc6b8b6230d..159a4b644df 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -1010,7 +1010,7 @@ void read_bisect_terms(const char **read_bad, const char **read_good)\n  */\n enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n {\n-\tstruct rev_info revs;\n+\tstruct rev_info revs = { 0 };\n \tstruct commit_list *tried;\n \tint reaches = 0, all = 0, nr, steps;\n \tenum bisect_error res = BISECT_OK;\n@@ -1035,7 +1035,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \n \tres = check_good_are_ancestors_of_bad(r, prefix, no_checkout);\n \tif (res)\n-\t\treturn res;\n+\t\tgoto cleanup;\n \n \tbisect_rev_setup(r, &revs, prefix, \"%s\", \"^%s\", 1);\n \n@@ -1060,14 +1060,16 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \t\t       term_good,\n \t\t       term_bad);\n \n-\t\treturn BISECT_FAILED;\n+\t\tres = BISECT_FAILED;\n+\t\tgoto cleanup;\n \t}\n \n \tif (!all) {\n \t\tfprintf(stderr, _(\"No testable commit found.\\n\"\n \t\t\t\"Maybe you started with bad path arguments?\\n\"));\n \n-\t\treturn BISECT_NO_TESTABLE_COMMIT;\n+\t\tres = BISECT_NO_TESTABLE_COMMIT;\n+\t\tgoto cleanup;\n \t}\n \n \tbisect_rev = &revs.commits->item->object.oid;\n@@ -1087,7 +1089,8 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \t\t * for negative return values for early returns up\n \t\t * until the cmd_bisect__helper() caller.\n \t\t */\n-\t\treturn BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;\n+\t\tres = BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;\n+\t\tgoto cleanup;\n \t}\n \n \tnr = all - reaches - 1;\n@@ -1106,7 +1109,10 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \t/* Clean up objects used, as they will be reused. */\n \trepo_clear_commit_marks(r, ALL_REV_FLAGS);\n \n-\treturn bisect_checkout(bisect_rev, no_checkout);\n+\tres = bisect_checkout(bisect_rev, no_checkout);\n+cleanup:\n+\trelease_revisions(&revs);\n+\treturn res;\n }\n \n static inline int log2i(int n)\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 0eeced8a10d..e8e20cdcc53 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -756,7 +756,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,\n {\n \tchar *displaypath;\n \tstruct strvec diff_files_args = STRVEC_INIT;\n-\tstruct rev_info rev;\n+\tstruct rev_info rev = { 0 };\n \tint diff_files_result;\n \tstruct strbuf buf = STRBUF_INIT;\n \tconst char *git_dir;\n@@ -843,6 +843,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,\n cleanup:\n \tstrvec_clear(&diff_files_args);\n \tfree(displaypath);\n+\trelease_revisions(&rev);\n }\n \n static void status_submodule_cb(const struct cache_entry *list_item,\ndiff --git a/bundle.c b/bundle.c\nindex e359370cfcd..a83d253a815 100644\n--- a/bundle.c\n+++ b/bundle.c\n@@ -196,14 +196,16 @@ int verify_bundle(struct repository *r,\n \t * to be verbose about the errors\n \t */\n \tstruct string_list *p = &header->prerequisites;\n-\tstruct rev_info revs;\n+\tstruct rev_info revs = { 0 };\n \tconst char *argv[] = {NULL, \"--all\", NULL};\n \tstruct commit *commit;\n \tint i, ret = 0, req_nr;\n \tconst char *message = _(\"Repository lacks these prerequisite commits:\");\n \n-\tif (!r || !r->objects || !r->objects->odb)\n-\t\treturn error(_(\"need a repository to verify a bundle\"));\n+\tif (!r || !r->objects || !r->objects->odb) {\n+\t\tret = error(_(\"need a repository to verify a bundle\"));\n+\t\tgoto cleanup;\n+\t}\n \n \trepo_init_revisions(r, &revs, NULL);\n \tfor (i = 0; i < p->nr; i++) {\n@@ -221,7 +223,7 @@ int verify_bundle(struct repository *r,\n \t\terror(\"%s %s\", oid_to_hex(oid), name);\n \t}\n \tif (revs.pending.nr != p->nr)\n-\t\treturn ret;\n+\t\tgoto cleanup;\n \treq_nr = revs.pending.nr;\n \tsetup_revisions(2, argv, &revs, NULL);\n \n@@ -283,6 +285,8 @@ int verify_bundle(struct repository *r,\n \t\t\tlist_refs(r, 0, NULL);\n \t\t}\n \t}\n+cleanup:\n+\trelease_revisions(&revs);\n \treturn ret;\n }\n \n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452040","messageId":"patch-v2-12.27-74818bc372f-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 12/27] revisions API users: use release_revisions() in builtin/log.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:32:02Z","receivedAt":"2022-03-23T20:32:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In preparation for having the \"log\" family of functions make wider use\nof release_revisions() let's have them call it just before\nexiting. This changes the \"log\", \"whatchanged\", \"show\",\n\"format-patch\", etc. commands, all of which live in this file.\n\nThe release_revisions() API still only frees the \"pending\" member, but\nwill learn to more members of \"struct rev_info\" in subsequent commits.\n\nIn the case of \"format-patch\" revert the addition of UNLEAK() in\ndee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16), and\nwhich will cause several tests that previously passed under\n\"TEST_PASSES_SANITIZE_LEAK=true\" to start failing.\n\nIn subsequent commits we'll now be able to use those tests to check\nwhether that part of the API is really leaking memory, and will fix\nall of those memory leaks. Removing the UNLEAK() allows us to make\nincremental progress in that direction. See [1] for further details\nabout this approach.\n\n1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/log.c          | 20 ++++++++++++--------\n t/t4126-apply-empty.sh |  2 --\n 2 files changed, 12 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 6f9928fabfe..c40ebe1c3f4 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -295,6 +295,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,\n \tcmd_log_init_finish(argc, argv, prefix, rev, opt);\n }\n \n+static int cmd_log_deinit(int ret, struct rev_info *rev)\n+{\n+\trelease_revisions(rev);\n+\treturn ret;\n+}\n+\n /*\n  * This gives a rough estimate for how many commits we\n  * will print out in the list.\n@@ -558,7 +564,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)\n \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n \tif (!rev.diffopt.output_format)\n \t\trev.diffopt.output_format = DIFF_FORMAT_RAW;\n-\treturn cmd_log_walk(&rev);\n+\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n }\n \n static void show_tagger(const char *buf, struct rev_info *rev)\n@@ -677,7 +683,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n \n \tif (!rev.no_walk)\n-\t\treturn cmd_log_walk(&rev);\n+\t\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n \n \tcount = rev.pending.nr;\n \tobjects = rev.pending.objects;\n@@ -732,8 +738,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \t\t\tret = error(_(\"unknown type: %d\"), o->type);\n \t\t}\n \t}\n-\tfree(objects);\n-\treturn ret;\n+\treturn cmd_log_deinit(ret, &rev);\n }\n \n /*\n@@ -761,7 +766,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)\n \trev.always_show_header = 1;\n \tcmd_log_init_finish(argc, argv, prefix, &rev, &opt);\n \n-\treturn cmd_log_walk(&rev);\n+\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n }\n \n static void log_setup_revisions_tweak(struct rev_info *rev,\n@@ -792,7 +797,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)\n \topt.revarg_opt = REVARG_COMMITTISH;\n \topt.tweak = log_setup_revisions_tweak;\n \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n-\treturn cmd_log_walk(&rev);\n+\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n }\n \n /* format-patch */\n@@ -2289,8 +2294,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tif (rev.ref_message_ids)\n \t\tstring_list_clear(rev.ref_message_ids, 0);\n \tfree(rev.ref_message_ids);\n-\tUNLEAK(rev);\n-\treturn 0;\n+\treturn cmd_log_deinit(0, &rev);\n }\n \n static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)\ndiff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh\nindex 33860d38290..66a7ba8ab8f 100755\n--- a/t/t4126-apply-empty.sh\n+++ b/t/t4126-apply-empty.sh\n@@ -2,8 +2,6 @@\n \n test_description='apply empty'\n \n-\n-TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452041","messageId":"patch-v2-13.27-8717e6e8471-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 13/27] revisions API users: use release_revisions() with UNLEAK()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:32:03Z","receivedAt":"2022-03-23T20:32:54Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use a release_revisions() with those \"struct rev_list\" users which\nalready \"UNLEAK\" the struct. It may seem odd to simultaneously attempt\nto free() memory, but also to explicitly ignore whether we have memory\nleaks in the same.\n\nAs explained in preceding commits this is being done to use the\nbuilt-in commands as a guinea pig for whether the release_revisions()\nfunction works as expected, we'd like to test e.g. whether we segfault\nas we change it. In subsequent commits we'll then remove these\nUNLEAK() as the function is made to free the memory that caused us to\nadd them in the first place.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/diff-index.c | 4 +++-\n builtin/diff.c       | 1 +\n 2 files changed, 4 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/diff-index.c b/builtin/diff-index.c\nindex 5fd23ab5b6c..3a83183c312 100644\n--- a/builtin/diff-index.c\n+++ b/builtin/diff-index.c\n@@ -71,5 +71,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)\n \t}\n \tresult = run_diff_index(&rev, option);\n \tUNLEAK(rev);\n-\treturn diff_result_code(&rev.diffopt, result);\n+\tresult = diff_result_code(&rev.diffopt, result);\n+\trelease_revisions(&rev);\n+\treturn result;\n }\ndiff --git a/builtin/diff.c b/builtin/diff.c\nindex bb7fafca618..dd48336da56 100644\n--- a/builtin/diff.c\n+++ b/builtin/diff.c\n@@ -595,6 +595,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \tif (1 < rev.diffopt.skip_stat_unmatch)\n \t\trefresh_index_quietly();\n \tUNLEAK(rev);\n+\trelease_revisions(&rev);\n \tUNLEAK(ent);\n \tUNLEAK(blob);\n \treturn result;\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452042","messageId":"patch-v2-10.27-eafe57663fc-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 10/27] revisions API users: add \"goto cleanup\" for release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:32:00Z","receivedAt":"2022-03-23T20:32:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a release_revisions() to various users of \"struct rev_list\" which\nrequires a minor refactoring to a \"goto cleanup\" pattern to use that\nfunction.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/diff-files.c        |  8 ++++++--\n builtin/rev-list.c          | 19 ++++++++++++-------\n builtin/stash.c             |  1 +\n builtin/submodule--helper.c | 10 +++++++---\n sequencer.c                 | 23 ++++++++++++++++-------\n t/helper/test-fast-rebase.c | 18 +++++++++++++-----\n 6 files changed, 55 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin/diff-files.c b/builtin/diff-files.c\nindex 70103c40952..2bfaf9ba7ae 100644\n--- a/builtin/diff-files.c\n+++ b/builtin/diff-files.c\n@@ -77,8 +77,12 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)\n \n \tif (read_cache_preload(&rev.diffopt.pathspec) < 0) {\n \t\tperror(\"read_cache_preload\");\n-\t\treturn -1;\n+\t\tresult = -1;\n+\t\tgoto cleanup;\n \t}\n+cleanup:\n \tresult = run_diff_files(&rev, options);\n-\treturn diff_result_code(&rev.diffopt, result);\n+\tresult = diff_result_code(&rev.diffopt, result);\n+\trelease_revisions(&rev);\n+\treturn result;\n }\ndiff --git a/builtin/rev-list.c b/builtin/rev-list.c\nindex 572da1472e5..2f0b6c759e4 100644\n--- a/builtin/rev-list.c\n+++ b/builtin/rev-list.c\n@@ -502,6 +502,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \tint use_bitmap_index = 0;\n \tint filter_provided_objects = 0;\n \tconst char *show_progress = NULL;\n+\tint ret = 0;\n \n \tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n \t\tusage(rev_list_usage);\n@@ -585,7 +586,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \t\t}\n \t\tif (!strcmp(arg, \"--test-bitmap\")) {\n \t\t\ttest_bitmap_walk(&revs);\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t\t}\n \t\tif (skip_prefix(arg, \"--progress=\", &arg)) {\n \t\t\tshow_progress = arg;\n@@ -674,11 +675,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \n \tif (use_bitmap_index) {\n \t\tif (!try_bitmap_count(&revs, filter_provided_objects))\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t\tif (!try_bitmap_disk_usage(&revs, filter_provided_objects))\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t\tif (!try_bitmap_traversal(&revs, filter_provided_objects))\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t}\n \n \tif (prepare_revision_walk(&revs))\n@@ -698,8 +699,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \n \t\tfind_bisection(&revs.commits, &reaches, &all, bisect_flags);\n \n-\t\tif (bisect_show_vars)\n-\t\t\treturn show_bisect_vars(&info, reaches, all);\n+\t\tif (bisect_show_vars) {\n+\t\t\tret = show_bisect_vars(&info, reaches, all);\n+\t\t\tgoto cleanup;\n+\t\t}\n \t}\n \n \tif (filter_provided_objects) {\n@@ -754,5 +757,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \tif (show_disk_usage)\n \t\tprintf(\"%\"PRIuMAX\"\\n\", (uintmax_t)total_disk_usage);\n \n-\treturn 0;\n+cleanup:\n+\trelease_revisions(&revs);\n+\treturn ret;\n }\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 4c8c8af8a09..4fc717d5578 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -917,6 +917,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n cleanup:\n \tstrvec_clear(&stash_args);\n \tfree_stash_info(&info);\n+\trelease_revisions(&rev);\n \treturn ret;\n }\n \ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex e8e20cdcc53..78887779d8b 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1222,6 +1222,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \tstruct strvec diff_args = STRVEC_INIT;\n \tstruct rev_info rev;\n \tstruct module_cb_list list = MODULE_CB_LIST_INIT;\n+\tint ret = 0;\n \n \tstrvec_push(&diff_args, get_diff_cmd(diff_cmd));\n \tif (info->cached)\n@@ -1247,11 +1248,13 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \t\t\tsetup_work_tree();\n \t\tif (read_cache_preload(&rev.diffopt.pathspec) < 0) {\n \t\t\tperror(\"read_cache_preload\");\n-\t\t\treturn -1;\n+\t\t\tret = -1;\n+\t\t\tgoto cleanup;\n \t\t}\n \t} else if (read_cache() < 0) {\n \t\tperror(\"read_cache\");\n-\t\treturn -1;\n+\t\tret = -1;\n+\t\tgoto cleanup;\n \t}\n \n \tif (diff_cmd == DIFF_INDEX)\n@@ -1259,9 +1262,10 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \telse\n \t\trun_diff_files(&rev, 0);\n \tprepare_submodule_summary(info, &list);\n+cleanup:\n \tstrvec_clear(&diff_args);\n \trelease_revisions(&rev);\n-\treturn 0;\n+\treturn ret;\n }\n \n static int module_summary(int argc, const char **argv, const char *prefix)\ndiff --git a/sequencer.c b/sequencer.c\nindex 29ec201af64..efea320fb9b 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -5356,6 +5356,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \tint rebase_merges = flags & TODO_LIST_REBASE_MERGES;\n \tint reapply_cherry_picks = flags & TODO_LIST_REAPPLY_CHERRY_PICKS;\n \tint skipped_commit = 0;\n+\tint ret = 0;\n \n \trepo_init_revisions(r, &revs, NULL);\n \trevs.verbose_header = 1;\n@@ -5379,14 +5380,20 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \tpp.fmt = revs.commit_format;\n \tpp.output_encoding = get_log_output_encoding();\n \n-\tif (setup_revisions(argc, argv, &revs, NULL) > 1)\n-\t\treturn error(_(\"make_script: unhandled options\"));\n+\tif (setup_revisions(argc, argv, &revs, NULL) > 1) {\n+\t\tret = error(_(\"make_script: unhandled options\"));\n+\t\tgoto cleanup;\n+\t}\n \n-\tif (prepare_revision_walk(&revs) < 0)\n-\t\treturn error(_(\"make_script: error preparing revisions\"));\n+\tif (prepare_revision_walk(&revs) < 0) {\n+\t\tret = error(_(\"make_script: error preparing revisions\"));\n+\t\tgoto cleanup;\n+\t}\n \n-\tif (rebase_merges)\n-\t\treturn make_script_with_merges(&pp, &revs, out, flags);\n+\tif (rebase_merges) {\n+\t\tret = make_script_with_merges(&pp, &revs, out, flags);\n+\t\tgoto cleanup;\n+\t}\n \n \twhile ((commit = get_revision(&revs))) {\n \t\tint is_empty = is_original_commit_empty(commit);\n@@ -5410,7 +5417,9 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \tif (skipped_commit)\n \t\tadvise_if_enabled(ADVICE_SKIPPED_CHERRY_PICKS,\n \t\t\t\t  _(\"use --reapply-cherry-picks to include skipped commits\"));\n-\treturn 0;\n+cleanup:\n+\trelease_revisions(&revs);\n+\treturn ret;\n }\n \n /*\ndiff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c\nindex 993b90eaedd..4e5553e2024 100644\n--- a/t/helper/test-fast-rebase.c\n+++ b/t/helper/test-fast-rebase.c\n@@ -99,6 +99,7 @@ int cmd__fast_rebase(int argc, const char **argv)\n \tstruct merge_result result;\n \tstruct strbuf reflog_msg = STRBUF_INIT;\n \tstruct strbuf branch_name = STRBUF_INIT;\n+\tint ret = 0;\n \n \t/*\n \t * test-tool stuff doesn't set up the git directory by default; need to\n@@ -137,13 +138,17 @@ int cmd__fast_rebase(int argc, const char **argv)\n \trevs.topo_order = 1;\n \tstrvec_pushl(&rev_walk_args, \"\", argv[4], \"--not\", argv[3], NULL);\n \n-\tif (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1)\n-\t\treturn error(_(\"unhandled options\"));\n+\tif (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) {\n+\t\tret = error(_(\"unhandled options\"));\n+\t\tgoto cleanup;\n+\t}\n \n \tstrvec_clear(&rev_walk_args);\n \n-\tif (prepare_revision_walk(&revs) < 0)\n-\t\treturn error(_(\"error preparing revisions\"));\n+\tif (prepare_revision_walk(&revs) < 0) {\n+\t\tret = error(_(\"error preparing revisions\"));\n+\t\tgoto cleanup;\n+\t}\n \n \tinit_merge_options(&merge_opt, the_repository);\n \tmemset(&result, 0, sizeof(result));\n@@ -220,7 +225,10 @@ int cmd__fast_rebase(int argc, const char **argv)\n \t\t\t       COMMIT_LOCK | SKIP_IF_UNCHANGED))\n \t\tdie(_(\"unable to write %s\"), get_index_file());\n \n+\tret = (result.clean == 0);\n+cleanup:\n \tstrbuf_release(&reflog_msg);\n \tstrbuf_release(&branch_name);\n-\treturn (result.clean == 0);\n+\trelease_revisions(&revs);\n+\treturn ret;\n }\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452043","messageId":"patch-v2-11.27-6fb2caf4d17-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 11/27] revisions API users: use release_revisions() in http-push.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:32:01Z","receivedAt":"2022-03-23T20:32:59Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In the case of cmd_main() in http-push.c we need to move the\ndeceleration of the \"struct rev-list\" into the loop over the\n\"remote_refs\" when adding a release_revisions().\n\nWe'd previously set up the \"revs\" for each remote, but would\npotentially leak memory on each one.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n http-push.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/http-push.c b/http-push.c\nindex f0c044dcf76..01e7c2ac5c8 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -1689,7 +1689,6 @@ int cmd_main(int argc, const char **argv)\n \tstruct refspec rs = REFSPEC_INIT_PUSH;\n \tstruct remote_lock *ref_lock = NULL;\n \tstruct remote_lock *info_ref_lock = NULL;\n-\tstruct rev_info revs;\n \tint delete_branch = 0;\n \tint force_delete = 0;\n \tint objects_to_send;\n@@ -1825,6 +1824,7 @@ int cmd_main(int argc, const char **argv)\n \n \tnew_refs = 0;\n \tfor (ref = remote_refs; ref; ref = ref->next) {\n+\t\tstruct rev_info revs;\n \t\tstruct strvec commit_argv = STRVEC_INIT;\n \n \t\tif (!ref->peer_ref)\n@@ -1941,6 +1941,7 @@ int cmd_main(int argc, const char **argv)\n \t\tunlock_remote(ref_lock);\n \t\tcheck_locks();\n \t\tstrvec_clear(&commit_argv);\n+\t\trelease_revisions(&revs);\n \t}\n \n \t/* Update remote server info if appropriate */\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452044","messageId":"patch-v2-09.27-88289157533-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 09/27] stash: always have the owner of \"stash_info\" free it","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:31:59Z","receivedAt":"2022-03-23T20:33:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the initialization of the \"revision\" member of \"struct\nstash_info\" to be initialized vi a macro, and more importantly that\nthat initializing function be tasked to free it, usually via \"goto\ncleanup\" pattern.\n\nDespite the \"revision\" name (and the topic of the series containing\nthis commit) the \"stash info\" has nothing to do with the \"struct\nrev_info\". I'm making this change because in the subsequent commit\nwhen we do want to free the \"struct rev_info\" via a \"goto cleanup\"\npattern we'd otherwise free() uninitialized memory in some cases, as\nwe only strbuf_init() the string in get_stash_info().\n\nSo while it's the smallest possible change, let's convert all users of\nthis pattern in the file while we're at it.\n\nA good follow-up to this change would be to change all the \"ret = -1;\ngoto done;\" in this file to instead use a \"goto cleanup\", and\ninitialize \"int ret = -1\" at the start of the relevant functions. That\nwould allow us to drop a lot of needless brace verbosity on two-line\n\"if\" statements, but let's leave that alone for now.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/stash.c | 70 ++++++++++++++++++++++++++-----------------------\n 1 file changed, 37 insertions(+), 33 deletions(-)\n\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 8a8093c5eea..4c8c8af8a09 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -117,6 +117,10 @@ struct stash_info {\n \tint has_u;\n };\n \n+#define STASH_INFO_INIT { \\\n+\t.revision = STRBUF_INIT, \\\n+}\n+\n static void free_stash_info(struct stash_info *info)\n {\n \tstrbuf_release(&info->revision);\n@@ -158,10 +162,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)\n \tif (argc == 1)\n \t\tcommit = argv[0];\n \n-\tstrbuf_init(&info->revision, 0);\n \tif (!commit) {\n \t\tif (!ref_exists(ref_stash)) {\n-\t\t\tfree_stash_info(info);\n \t\t\tfprintf_ln(stderr, _(\"No stash entries found.\"));\n \t\t\treturn -1;\n \t\t}\n@@ -175,11 +177,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)\n \n \trevision = info->revision.buf;\n \n-\tif (get_oid(revision, &info->w_commit)) {\n-\t\terror(_(\"%s is not a valid reference\"), revision);\n-\t\tfree_stash_info(info);\n-\t\treturn -1;\n-\t}\n+\tif (get_oid(revision, &info->w_commit))\n+\t\treturn error(_(\"%s is not a valid reference\"), revision);\n \n \tassert_stash_like(info, revision);\n \n@@ -198,7 +197,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)\n \t\tinfo->is_stash_ref = !strcmp(expanded_ref, ref_stash);\n \t\tbreak;\n \tdefault: /* Invalid or ambiguous */\n-\t\tfree_stash_info(info);\n+\t\tbreak;\n \t}\n \n \tfree(expanded_ref);\n@@ -599,10 +598,10 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,\n \n static int apply_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret;\n+\tint ret = -1;\n \tint quiet = 0;\n \tint index = 0;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n \t\tOPT_BOOL(0, \"index\", &index,\n@@ -614,9 +613,10 @@ static int apply_stash(int argc, const char **argv, const char *prefix)\n \t\t\t     git_stash_apply_usage, 0);\n \n \tif (get_stash_info(&info, argc, argv))\n-\t\treturn -1;\n+\t\tgoto cleanup;\n \n \tret = do_apply_stash(prefix, &info, index, quiet);\n+cleanup:\n \tfree_stash_info(&info);\n \treturn ret;\n }\n@@ -663,9 +663,9 @@ static void assert_stash_ref(struct stash_info *info)\n \n static int drop_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret;\n+\tint ret = -1;\n \tint quiet = 0;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n \t\tOPT_END()\n@@ -675,21 +675,22 @@ static int drop_stash(int argc, const char **argv, const char *prefix)\n \t\t\t     git_stash_drop_usage, 0);\n \n \tif (get_stash_info(&info, argc, argv))\n-\t\treturn -1;\n+\t\tgoto cleanup;\n \n \tassert_stash_ref(&info);\n \n \tret = do_drop_stash(&info, quiet);\n+cleanup:\n \tfree_stash_info(&info);\n \treturn ret;\n }\n \n static int pop_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret;\n+\tint ret = -1;\n \tint index = 0;\n \tint quiet = 0;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n \t\tOPT_BOOL(0, \"index\", &index,\n@@ -701,7 +702,7 @@ static int pop_stash(int argc, const char **argv, const char *prefix)\n \t\t\t     git_stash_pop_usage, 0);\n \n \tif (get_stash_info(&info, argc, argv))\n-\t\treturn -1;\n+\t\tgoto cleanup;\n \n \tassert_stash_ref(&info);\n \tif ((ret = do_apply_stash(prefix, &info, index, quiet)))\n@@ -710,15 +711,16 @@ static int pop_stash(int argc, const char **argv, const char *prefix)\n \telse\n \t\tret = do_drop_stash(&info, quiet);\n \n+cleanup:\n \tfree_stash_info(&info);\n \treturn ret;\n }\n \n static int branch_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret;\n+\tint ret = -1;\n \tconst char *branch = NULL;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tstruct option options[] = {\n \t\tOPT_END()\n@@ -735,7 +737,7 @@ static int branch_stash(int argc, const char **argv, const char *prefix)\n \tbranch = argv[0];\n \n \tif (get_stash_info(&info, argc - 1, argv + 1))\n-\t\treturn -1;\n+\t\tgoto cleanup;\n \n \tcp.git_cmd = 1;\n \tstrvec_pushl(&cp.args, \"checkout\", \"-b\", NULL);\n@@ -747,8 +749,8 @@ static int branch_stash(int argc, const char **argv, const char *prefix)\n \tif (!ret && info.is_stash_ref)\n \t\tret = do_drop_stash(&info, 0);\n \n+cleanup:\n \tfree_stash_info(&info);\n-\n \treturn ret;\n }\n \n@@ -826,8 +828,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op\n static int show_stash(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n-\tint ret = 0;\n-\tstruct stash_info info;\n+\tint ret = -1;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct rev_info rev;\n \tstruct strvec stash_args = STRVEC_INIT;\n \tstruct strvec revision_args = STRVEC_INIT;\n@@ -862,10 +864,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \t\t\tstrvec_push(&revision_args, argv[i]);\n \t}\n \n-\tret = get_stash_info(&info, stash_args.nr, stash_args.v);\n-\tstrvec_clear(&stash_args);\n-\tif (ret)\n-\t\treturn -1;\n+\tif (get_stash_info(&info, stash_args.nr, stash_args.v))\n+\t\tgoto cleanup;\n \n \t/*\n \t * The config settings are applied only if there are not passed\n@@ -879,8 +879,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \t\t\trev.diffopt.output_format |= DIFF_FORMAT_PATCH;\n \n \t\tif (!show_stat && !show_patch) {\n-\t\t\tfree_stash_info(&info);\n-\t\t\treturn 0;\n+\t\t\tret = 0;\n+\t\t\tgoto cleanup;\n \t\t}\n \t}\n \n@@ -913,8 +913,11 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \t}\n \tlog_tree_diff_flush(&rev);\n \n+\tret = diff_result_code(&rev.diffopt, 0);;\n+cleanup:\n+\tstrvec_clear(&stash_args);\n \tfree_stash_info(&info);\n-\treturn diff_result_code(&rev.diffopt, 0);\n+\treturn ret;\n }\n \n static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,\n@@ -1410,9 +1413,9 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b\n \n static int create_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret = 0;\n+\tint ret;\n \tstruct strbuf stash_msg_buf = STRBUF_INIT;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct pathspec ps;\n \n \t/* Starting with argv[1], since argv[0] is \"create\" */\n@@ -1427,6 +1430,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)\n \tif (!ret)\n \t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n \n+\tfree_stash_info(&info);\n \tstrbuf_release(&stash_msg_buf);\n \treturn ret;\n }\n@@ -1435,7 +1439,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q\n \t\t\t int keep_index, int patch_mode, int include_untracked, int only_staged)\n {\n \tint ret = 0;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct strbuf patch = STRBUF_INIT;\n \tstruct strbuf stash_msg_buf = STRBUF_INIT;\n \tstruct strbuf untracked_files = STRBUF_INIT;\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452045","messageId":"patch-v2-14.27-38f4f1c4ecb-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 14/27] revisions API users: use release_revisions() in submodule.c edge case","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:32:04Z","receivedAt":"2022-03-23T20:33:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use release_revisions() on the the \"struct rev_info\" in\nshow_submodule_diff_summary() where we need to pre-initialize with \"{\n0 }\" to make sure that we won't segfault when calling\nrelease_revisions().\n\nMost functions that use \"struct rev_info\" will call\nrepo_init_revisions() early, which will do an equivalent memset(). In\nthis case we might \"goto out\" before doing the equivalent of that\noperation, see the \"goto\" on \"(!left || !right || !sub)\" added in\n8e6df65015f (submodule: refactor show_submodule_summary with helper\nfunction, 2016-08-31).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n submodule.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/submodule.c b/submodule.c\nindex 0510cb193b6..285c1896c9e 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -638,7 +638,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,\n \t\tstruct object_id *one, struct object_id *two,\n \t\tunsigned dirty_submodule)\n {\n-\tstruct rev_info rev;\n+\tstruct rev_info rev = { 0 };\n \tstruct commit *left = NULL, *right = NULL;\n \tstruct commit_list *merge_bases = NULL;\n \tstruct repository *sub;\n@@ -664,6 +664,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,\n \tprint_submodule_diff_summary(sub, &rev, o);\n \n out:\n+\trelease_revisions(&rev);\n \tif (merge_bases)\n \t\tfree_commit_list(merge_bases);\n \tclear_commit_marks(left, ~0);\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452046","messageId":"patch-v2-16.27-bc36810e56e-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 16/27] revisions API: have release_revisions() release \"commits\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:32:06Z","receivedAt":"2022-03-23T20:33:05Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"commits\" in the \"struct ref_info\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/revision.c b/revision.c\nindex 90bac9ada03..13b568aea76 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2937,6 +2937,7 @@ void release_revisions(struct rev_info *revs)\n {\n \tif (!revs)\n \t\treturn;\n+\trelease_revisions_commit_list(revs);\n \tobject_array_clear(&revs->pending);\n }\n \n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452047","messageId":"patch-v2-15.27-566b739ac6c-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 15/27] revisions API users: use release_revisions() for \"prune_data\" users","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:32:05Z","receivedAt":"2022-03-23T20:33:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use release_revisions() for users of \"struct rev_list\" that reach into\nthe \"struct rev_info\" and clear the \"prune_data\" already.\n\nIn a subsequent commit we'll teach release_revisions() to clear this\nitself, but in the meantime let's invoke release_revisions() here to\nclear anything else we may have missed, and for reasons of having\nconsistent boilerplate.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/add.c   | 1 +\n builtin/stash.c | 1 +\n diff-lib.c      | 1 +\n wt-status.c     | 1 +\n 4 files changed, 4 insertions(+)\n\ndiff --git a/builtin/add.c b/builtin/add.c\nindex f507d2191cd..115a26ea633 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -143,6 +143,7 @@ int add_files_to_cache(const char *prefix,\n \trev.max_count = 0; /* do not compare unmerged paths with stage #2 */\n \trun_diff_files(&rev, DIFF_RACY_IS_MODIFIED);\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n \treturn !!data.add_errors;\n }\n \ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 4fc717d5578..878eaf2e125 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1061,6 +1061,7 @@ static int check_changes_tracked_files(const struct pathspec *ps)\n \n done:\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n \treturn ret;\n }\n \ndiff --git a/diff-lib.c b/diff-lib.c\nindex d6800274bd5..0f16281253f 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -642,6 +642,7 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)\n \tif (diff_cache(&revs, tree_oid, NULL, 1))\n \t\texit(128);\n \tclear_pathspec(&revs.prune_data);\n+\trelease_revisions(&revs);\n \treturn 0;\n }\n \ndiff --git a/wt-status.c b/wt-status.c\nindex f9100621375..a14fad1e03a 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -617,6 +617,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)\n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_files(&rev, 0);\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n }\n \n static void wt_status_collect_changes_index(struct wt_status *s)\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452048","messageId":"patch-v2-19.27-d5b7a8a690e-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 19/27] revisions API: have release_revisions() release \"filter\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:32:09Z","receivedAt":"2022-03-23T20:33:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"cmdline\" in the \"struct ref_info\". This in combination with a\npreceding change to free \"cmdline\" means that we can mark another set\nof tests as passing under \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nThe \"filter\" member was added recently in ffaa137f646 (revision: put\nobject filter into struct rev_info, 2022-03-09), and this fixes leaks\nintruded in the subsequent leak 7940941de1f (pack-objects: use\nrev.filter when possible, 2022-03-09) and 105c6f14ad3 (bundle: parse\nfilter capability, 2022-03-09).\n\nThe \"builtin/pack-objects.c\" leak in 7940941de1f was effectively with\nus already, but the variable was referred to by a \"static\" file-scoped\nvariable. The \"bundle.c \" leak in 105c6f14ad3 was newly introduced\nwith the new \"filter\" feature for bundles.\n\nThe \"t5600-clone-fail-cleanup.sh\" change here to add\n\"TEST_PASSES_SANITIZE_LEAK=true\" is one of the cases where\nrun-command.c in not carrying the abort() exit code upwards would have\nhad that test passing before, but now it *actually* passes[1]. We\nshould fix the lack of 1=1 mapping of SANITIZE=leak testing to actual\nleaks some other time, but it's an existing edge case, let's just mark\nthe really-passing test as passing for now.\n\n1. https://lore.kernel.org/git/220303.86fsnz5o9w.gmgdl@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c                         | 1 +\n t/t1060-object-corruption.sh       | 1 +\n t/t2015-checkout-unborn.sh         | 1 +\n t/t4207-log-decoration-colors.sh   | 1 +\n t/t5301-sliding-window.sh          | 2 ++\n t/t5313-pack-bounds-checks.sh      | 2 ++\n t/t5316-pack-delta-depth.sh        | 2 ++\n t/t5320-delta-islands.sh           | 2 ++\n t/t5322-pack-objects-sparse.sh     | 1 +\n t/t5506-remote-groups.sh           | 1 +\n t/t5513-fetch-track.sh             | 1 +\n t/t5532-fetch-proxy.sh             | 2 ++\n t/t5600-clone-fail-cleanup.sh      | 1 +\n t/t5900-repo-selection.sh          | 2 ++\n t/t6101-rev-parse-parents.sh       | 1 +\n t/t6114-keep-packs.sh              | 2 ++\n t/t7702-repack-cyclic-alternate.sh | 2 ++\n t/t9127-git-svn-partial-rebuild.sh | 1 -\n 18 files changed, 25 insertions(+), 1 deletion(-)\n\ndiff --git a/revision.c b/revision.c\nindex f44298299a4..71f4ef3530d 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2959,6 +2959,7 @@ void release_revisions(struct rev_info *revs)\n \trelease_revisions_commit_list(revs);\n \tobject_array_clear(&revs->pending);\n \trelease_revisions_cmdline(&revs->cmdline);\n+\tlist_objects_filter_release(&revs->filter);\n \trelease_revisions_mailmap(revs->mailmap);\n }\n \ndiff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh\nindex e8a58b15897..5b8e47e346c 100755\n--- a/t/t1060-object-corruption.sh\n+++ b/t/t1060-object-corruption.sh\n@@ -2,6 +2,7 @@\n \n test_description='see how we handle various forms of corruption'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # convert \"1234abcd\" to \".git/objects/12/34abcd\"\ndiff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh\nindex a9721215fab..9425aae6395 100755\n--- a/t/t2015-checkout-unborn.sh\n+++ b/t/t2015-checkout-unborn.sh\n@@ -4,6 +4,7 @@ test_description='checkout from unborn branch'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh\nindex b8709424981..36ac6aff1e4 100755\n--- a/t/t4207-log-decoration-colors.sh\n+++ b/t/t4207-log-decoration-colors.sh\n@@ -8,6 +8,7 @@ test_description='Test for \"git log --decorate\" colors'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t5301-sliding-window.sh b/t/t5301-sliding-window.sh\nindex 76f9798ab95..3ccaaeb3977 100755\n--- a/t/t5301-sliding-window.sh\n+++ b/t/t5301-sliding-window.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='mmap sliding window tests'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success \\\ndiff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh\nindex 535313e4dc8..cc4cfaa9d37 100755\n--- a/t/t5313-pack-bounds-checks.sh\n+++ b/t/t5313-pack-bounds-checks.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='bounds-checking of access to mmapped on-disk file formats'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n clear_base () {\ndiff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh\nindex e9045009a11..eb4ef3dda4d 100755\n--- a/t/t5316-pack-delta-depth.sh\n+++ b/t/t5316-pack-delta-depth.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='pack-objects breaks long cross-pack delta chains'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # This mirrors a repeated push setup:\ndiff --git a/t/t5320-delta-islands.sh b/t/t5320-delta-islands.sh\nindex fea92a5777f..124d47603df 100755\n--- a/t/t5320-delta-islands.sh\n+++ b/t/t5320-delta-islands.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='exercise delta islands'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # returns true iff $1 is a delta based on $2\ndiff --git a/t/t5322-pack-objects-sparse.sh b/t/t5322-pack-objects-sparse.sh\nindex d39958c066d..770695c9278 100755\n--- a/t/t5322-pack-objects-sparse.sh\n+++ b/t/t5322-pack-objects-sparse.sh\n@@ -4,6 +4,7 @@ test_description='pack-objects object selection using sparse algorithm'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup repo' '\ndiff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh\nindex 8f150c0793e..5bac03ede81 100755\n--- a/t/t5506-remote-groups.sh\n+++ b/t/t5506-remote-groups.sh\n@@ -4,6 +4,7 @@ test_description='git remote group handling'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n mark() {\ndiff --git a/t/t5513-fetch-track.sh b/t/t5513-fetch-track.sh\nindex 65d1e05bd62..c46c4dbaefc 100755\n--- a/t/t5513-fetch-track.sh\n+++ b/t/t5513-fetch-track.sh\n@@ -2,6 +2,7 @@\n \n test_description='fetch follows remote-tracking branches correctly'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh\nindex 9c2798603b4..d664912799b 100755\n--- a/t/t5532-fetch-proxy.sh\n+++ b/t/t5532-fetch-proxy.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='fetching via git:// using core.gitproxy'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup remote repo' '\ndiff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh\nindex 34b3df40275..c814afa5656 100755\n--- a/t/t5600-clone-fail-cleanup.sh\n+++ b/t/t5600-clone-fail-cleanup.sh\n@@ -13,6 +13,7 @@ Unless the directory already exists, in which case we clean up only what we\n wrote.\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n corrupt_repo () {\ndiff --git a/t/t5900-repo-selection.sh b/t/t5900-repo-selection.sh\nindex 14e59c5b3e4..a84faac242d 100755\n--- a/t/t5900-repo-selection.sh\n+++ b/t/t5900-repo-selection.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='selecting remote repo in ambiguous cases'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n reset() {\ndiff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh\nindex c571fa51797..a3a41c7a3e4 100755\n--- a/t/t6101-rev-parse-parents.sh\n+++ b/t/t6101-rev-parse-parents.sh\n@@ -8,6 +8,7 @@ test_description='Test git rev-parse with different parent options'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_cmp_rev_output () {\ndiff --git a/t/t6114-keep-packs.sh b/t/t6114-keep-packs.sh\nindex 9239d8aa46d..44246f8a63e 100755\n--- a/t/t6114-keep-packs.sh\n+++ b/t/t6114-keep-packs.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='rev-list with .keep packs'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t7702-repack-cyclic-alternate.sh b/t/t7702-repack-cyclic-alternate.sh\nindex 93b74867ac8..f3cdb98eec2 100755\n--- a/t/t7702-repack-cyclic-alternate.sh\n+++ b/t/t7702-repack-cyclic-alternate.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='repack involving cyclic alternate'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh\nindex 90b1b30dde5..97f495bd49b 100755\n--- a/t/t9127-git-svn-partial-rebuild.sh\n+++ b/t/t9127-git-svn-partial-rebuild.sh\n@@ -5,7 +5,6 @@\n \n test_description='git svn partial-rebuild tests'\n \n-TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize svnrepo' '\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452049","messageId":"patch-v2-18.27-424fac7ab95-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 18/27] revisions API: have release_revisions() release \"cmdline\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:32:08Z","receivedAt":"2022-03-23T20:33:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"cmdline\" in the \"struct ref_info\". This in combination with a\npreceding change to free \"commits\" and \"mailmap\" means that we can\nwhitelist another test under \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nThere was a proposal in [1] to do away with xstrdup()-ing this\nadd_rev_cmdline(), perhaps that would be worthwhile, but for now let's\njust free() it.\n\nWe could also make that a \"char *\" in \"struct rev_cmdline_entry\"\nitself, but since we own it let's expose it as a constant to outside\ncallers. I proposed that in [2] but have since changed my mind. See\n14d30cdfc04 (ref-filter: fix memory leak in `free_array_item()`,\n2019-07-10), c514c62a4fd (checkout: fix leak of non-existent branch\nnames, 2020-08-14) and other log history hits for \"free((char *)\" for\nprior art.\n\nThis includes the tests we had false-positive passes on before my\n6798b08e848 (perl Git.pm: don't ignore signalled failure in\n_cmd_close(), 2022-02-01), now they pass for real.\n\nSince there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to\nlist those that don't pass than to touch most of those 66. So let's\nintroduce a \"TEST_FAILS_SANITIZE_LEAK=true\", which if set in the tests\nwon't cause lib-git-svn.sh to set \"TEST_PASSES_SANITIZE_LEAK=true.\n\nThis change also marks all the tests that we removed\n\"TEST_FAILS_SANITIZE_LEAK=true\" from in an earlier commit due to\nremoving the UNLEAK() from cmd_format_patch(), we can now assert that\nits API use doesn't leak any \"struct rev_info\" memory.\n\nThis change also made commit \"t5503-tagfollow.sh\" pass on current\nmaster, but that would regress when combined with\nps/fetch-atomic-fixup's de004e848a9 (t5503: simplify setup of test\nwhich exercises failure of backfill, 2022-03-03) (through no fault of\nthat topic, that change started using \"git clone\" in the test, which\nhas an outstanding leak). Let's leave that test out for now to avoid\nin-flight semantic conflicts.\n\n1. https://lore.kernel.org/git/YUj%2FgFRh6pwrZalY@carlos-mbp.lan/\n2. https://lore.kernel.org/git/87o88obkb1.fsf@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n\nfixup with bc0e0439040?\n---\n revision.c                                 | 12 ++++++++++++\n t/lib-git-svn.sh                           |  4 ++++\n t/t0062-revision-walking.sh                |  1 +\n t/t0101-at-syntax.sh                       |  2 ++\n t/t1060-object-corruption.sh               |  1 +\n t/t3303-notes-subtrees.sh                  |  1 +\n t/t3305-notes-fanout.sh                    |  1 +\n t/t3408-rebase-multi-line.sh               |  1 +\n t/t4027-diff-submodule.sh                  |  1 +\n t/t4128-apply-root.sh                      |  1 +\n t/t4212-log-corrupt.sh                     |  1 +\n t/t5515-fetch-merge-logic.sh               |  1 +\n t/t5518-fetch-exit-status.sh               |  1 +\n t/t6002-rev-list-bisect.sh                 |  1 +\n t/t6003-rev-list-topo-order.sh             |  1 +\n t/t6005-rev-list-count.sh                  |  1 +\n t/t6018-rev-list-glob.sh                   |  1 +\n t/t6100-rev-list-in-order.sh               |  1 +\n t/t9100-git-svn-basic.sh                   |  1 +\n t/t9101-git-svn-props.sh                   |  2 ++\n t/t9104-git-svn-follow-parent.sh           |  2 ++\n t/t9106-git-svn-commit-diff-clobber.sh     |  2 ++\n t/t9115-git-svn-dcommit-funky-renames.sh   |  1 +\n t/t9116-git-svn-log.sh                     |  2 ++\n t/t9122-git-svn-author.sh                  |  2 ++\n t/t9127-git-svn-partial-rebuild.sh         |  2 ++\n t/t9129-git-svn-i18n-commitencoding.sh     |  1 +\n t/t9132-git-svn-broken-symlink.sh          |  1 +\n t/t9139-git-svn-non-utf8-commitencoding.sh |  1 +\n t/t9146-git-svn-empty-dirs.sh              |  2 ++\n t/t9148-git-svn-propset.sh                 |  1 +\n t/t9151-svn-mergeinfo.sh                   |  1 +\n t/t9160-git-svn-preserve-empty-dirs.sh     |  1 +\n t/t9162-git-svn-dcommit-interactive.sh     |  2 ++\n t/t9164-git-svn-dcommit-concurrent.sh      |  2 ++\n t/t9501-gitweb-standalone-http-status.sh   |  1 +\n 36 files changed, 60 insertions(+)\n\ndiff --git a/revision.c b/revision.c\nindex d9e2b171f6d..f44298299a4 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2933,6 +2933,17 @@ static void release_revisions_commit_list(struct rev_info *revs)\n \trevs->commits = NULL;\n }\n \n+static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)\n+{\n+\tunsigned int i;\n+\n+\tif (!cmdline)\n+\t\treturn;\n+\tfor (i = 0; i < cmdline->nr; i++)\n+\t\tfree((char *)cmdline->rev[i].name);\n+\tFREE_AND_NULL(cmdline->rev);\n+}\n+\n static void release_revisions_mailmap(struct string_list *mailmap)\n {\n \tif (!mailmap)\n@@ -2947,6 +2958,7 @@ void release_revisions(struct rev_info *revs)\n \t\treturn;\n \trelease_revisions_commit_list(revs);\n \tobject_array_clear(&revs->pending);\n+\trelease_revisions_cmdline(&revs->cmdline);\n \trelease_revisions_mailmap(revs->mailmap);\n }\n \ndiff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh\nindex 2fde2353fd3..ea28971e8ee 100644\n--- a/t/lib-git-svn.sh\n+++ b/t/lib-git-svn.sh\n@@ -1,3 +1,7 @@\n+if test -z \"$TEST_FAILS_SANITIZE_LEAK\"\n+then\n+\tTEST_PASSES_SANITIZE_LEAK=true\n+fi\n . ./test-lib.sh\n \n if test -n \"$NO_SVN_TESTS\"\ndiff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh\nindex 8e215867b8c..b9480c81781 100755\n--- a/t/t0062-revision-walking.sh\n+++ b/t/t0062-revision-walking.sh\n@@ -5,6 +5,7 @@\n \n test_description='Test revision walking api'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n cat >run_twice_expected <<-EOF\ndiff --git a/t/t0101-at-syntax.sh b/t/t0101-at-syntax.sh\nindex a1998b558f9..878aadd64c9 100755\n--- a/t/t0101-at-syntax.sh\n+++ b/t/t0101-at-syntax.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='various @{whatever} syntax tests'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh\nindex bc89371f534..e8a58b15897 100755\n--- a/t/t1060-object-corruption.sh\n+++ b/t/t1060-object-corruption.sh\n@@ -1,6 +1,7 @@\n #!/bin/sh\n \n test_description='see how we handle various forms of corruption'\n+\n . ./test-lib.sh\n \n # convert \"1234abcd\" to \".git/objects/12/34abcd\"\ndiff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh\nindex eac193757bf..bc9b791d1b9 100755\n--- a/t/t3303-notes-subtrees.sh\n+++ b/t/t3303-notes-subtrees.sh\n@@ -5,6 +5,7 @@ test_description='Test commit notes organized in subtrees'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n number_of_commits=100\ndiff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh\nindex 9976d787f47..64a9915761a 100755\n--- a/t/t3305-notes-fanout.sh\n+++ b/t/t3305-notes-fanout.sh\n@@ -2,6 +2,7 @@\n \n test_description='Test that adding/removing many notes triggers automatic fanout restructuring'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n path_has_fanout() {\ndiff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh\nindex cde3562e3a6..7b4607d72f2 100755\n--- a/t/t3408-rebase-multi-line.sh\n+++ b/t/t3408-rebase-multi-line.sh\n@@ -5,6 +5,7 @@ test_description='rebasing a commit with multi-line first paragraph.'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh\nindex 295da987cce..40164ae07d2 100755\n--- a/t/t4027-diff-submodule.sh\n+++ b/t/t4027-diff-submodule.sh\n@@ -2,6 +2,7 @@\n \n test_description='difference in submodules'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-diff.sh\n \ndiff --git a/t/t4128-apply-root.sh b/t/t4128-apply-root.sh\nindex f6db5a79dd9..ed94c90204e 100755\n--- a/t/t4128-apply-root.sh\n+++ b/t/t4128-apply-root.sh\n@@ -2,6 +2,7 @@\n \n test_description='apply same filename'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh\nindex 0244888a5a7..30a219894bb 100755\n--- a/t/t4212-log-corrupt.sh\n+++ b/t/t4212-log-corrupt.sh\n@@ -2,6 +2,7 @@\n \n test_description='git log with invalid commit headers'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh\nindex 320d26796d2..c100a809c5e 100755\n--- a/t/t5515-fetch-merge-logic.sh\n+++ b/t/t5515-fetch-merge-logic.sh\n@@ -14,6 +14,7 @@ export GIT_TEST_PROTOCOL_VERSION\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n build_script () {\ndiff --git a/t/t5518-fetch-exit-status.sh b/t/t5518-fetch-exit-status.sh\nindex 5c4ac2556e7..c13120088fa 100755\n--- a/t/t5518-fetch-exit-status.sh\n+++ b/t/t5518-fetch-exit-status.sh\n@@ -8,6 +8,7 @@ test_description='fetch exit status test'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh\nindex b95a0212adf..162cf50778d 100755\n--- a/t/t6002-rev-list-bisect.sh\n+++ b/t/t6002-rev-list-bisect.sh\n@@ -4,6 +4,7 @@\n #\n test_description='Tests git rev-list --bisect functionality'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-t6000.sh # t6xxx specific functions\n \ndiff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh\nindex 24d1836f417..1f7d7dd20c1 100755\n--- a/t/t6003-rev-list-topo-order.sh\n+++ b/t/t6003-rev-list-topo-order.sh\n@@ -5,6 +5,7 @@\n \n test_description='Tests git rev-list --topo-order functionality'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-t6000.sh # t6xxx specific functions\n \ndiff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh\nindex e960049f647..0729f800c3c 100755\n--- a/t/t6005-rev-list-count.sh\n+++ b/t/t6005-rev-list-count.sh\n@@ -2,6 +2,7 @@\n \n test_description='git rev-list --max-count and --skip test'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh\nindex 24b34add833..e1abc5c2b32 100755\n--- a/t/t6018-rev-list-glob.sh\n+++ b/t/t6018-rev-list-glob.sh\n@@ -5,6 +5,7 @@ test_description='rev-list/rev-parse --glob'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n commit () {\ndiff --git a/t/t6100-rev-list-in-order.sh b/t/t6100-rev-list-in-order.sh\nindex e934bc239c5..88ed7bd75a7 100755\n--- a/t/t6100-rev-list-in-order.sh\n+++ b/t/t6100-rev-list-in-order.sh\n@@ -2,6 +2,7 @@\n \n test_description='rev-list testing in-commit-order'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup a commit history with trees, blobs' '\ndiff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh\nindex fea41b3c360..7c5b847f584 100755\n--- a/t/t9100-git-svn-basic.sh\n+++ b/t/t9100-git-svn-basic.sh\n@@ -8,6 +8,7 @@ test_description='git svn basic tests'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n prepare_utf8_locale\ndiff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh\nindex 8b5681dd68a..d043e80fc34 100755\n--- a/t/t9101-git-svn-props.sh\n+++ b/t/t9101-git-svn-props.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn property tests'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n mkdir import\ndiff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh\nindex c7d8e0bf00f..5cf2ef4b8b0 100755\n--- a/t/t9104-git-svn-follow-parent.sh\n+++ b/t/t9104-git-svn-follow-parent.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn fetching'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh\nindex aec45bca3b7..3cab0b9720a 100755\n--- a/t/t9106-git-svn-commit-diff-clobber.sh\n+++ b/t/t9106-git-svn-commit-diff-clobber.sh\n@@ -2,6 +2,8 @@\n #\n # Copyright (c) 2006 Eric Wong\n test_description='git svn commit-diff clobber'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh\nindex 743fbe1fe46..419f055721d 100755\n--- a/t/t9115-git-svn-dcommit-funky-renames.sh\n+++ b/t/t9115-git-svn-dcommit-funky-renames.sh\n@@ -5,6 +5,7 @@\n \n test_description='git svn dcommit can commit renames of files with ugly names'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'load repository with strange names' '\ndiff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh\nindex 0a9f1ef366d..34f6c80dea3 100755\n--- a/t/t9116-git-svn-log.sh\n+++ b/t/t9116-git-svn-log.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn log tests'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup repository and import' '\ndiff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh\nindex 9e8fe38e7ef..527ba3d2932 100755\n--- a/t/t9122-git-svn-author.sh\n+++ b/t/t9122-git-svn-author.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='git svn authorship'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup svn repository' '\ndiff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh\nindex 2e4789d061f..90b1b30dde5 100755\n--- a/t/t9127-git-svn-partial-rebuild.sh\n+++ b/t/t9127-git-svn-partial-rebuild.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn partial-rebuild tests'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize svnrepo' '\ndiff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh\nindex 01e1e8a8f76..185248a4cd7 100755\n--- a/t/t9129-git-svn-i18n-commitencoding.sh\n+++ b/t/t9129-git-svn-i18n-commitencoding.sh\n@@ -4,6 +4,7 @@\n \n test_description='git svn honors i18n.commitEncoding in config'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n compare_git_head_with () {\ndiff --git a/t/t9132-git-svn-broken-symlink.sh b/t/t9132-git-svn-broken-symlink.sh\nindex aeceffaf7b0..4d8d0584b79 100755\n--- a/t/t9132-git-svn-broken-symlink.sh\n+++ b/t/t9132-git-svn-broken-symlink.sh\n@@ -2,6 +2,7 @@\n \n test_description='test that git handles an svn repository with empty symlinks'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n test_expect_success 'load svn dumpfile' '\n \tsvnadmin load \"$rawsvnrepo\" <<EOF\ndiff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh\nindex 22d80b0be2b..b7f756b2b7f 100755\n--- a/t/t9139-git-svn-non-utf8-commitencoding.sh\n+++ b/t/t9139-git-svn-non-utf8-commitencoding.sh\n@@ -4,6 +4,7 @@\n \n test_description='git svn refuses to dcommit non-UTF8 messages'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n # ISO-2022-JP can pass for valid UTF-8, so skipping that in this test\ndiff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh\nindex 80cb55fee70..79c26ed69c1 100755\n--- a/t/t9146-git-svn-empty-dirs.sh\n+++ b/t/t9146-git-svn-empty-dirs.sh\n@@ -3,6 +3,8 @@\n # Copyright (c) 2009 Eric Wong\n \n test_description='git svn creates empty directories'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9148-git-svn-propset.sh b/t/t9148-git-svn-propset.sh\nindex aebb28995e5..6cc76a07b39 100755\n--- a/t/t9148-git-svn-propset.sh\n+++ b/t/t9148-git-svn-propset.sh\n@@ -5,6 +5,7 @@\n \n test_description='git svn propset tests'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup propset via import' '\ndiff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh\nindex c93a5beab25..85221d439bd 100755\n--- a/t/t9151-svn-mergeinfo.sh\n+++ b/t/t9151-svn-mergeinfo.sh\n@@ -5,6 +5,7 @@\n \n test_description='git-svn svn mergeinfo properties'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'load svn dump' \"\ndiff --git a/t/t9160-git-svn-preserve-empty-dirs.sh b/t/t9160-git-svn-preserve-empty-dirs.sh\nindex 36c6b1a12ff..9cf7a1427ab 100755\n--- a/t/t9160-git-svn-preserve-empty-dirs.sh\n+++ b/t/t9160-git-svn-preserve-empty-dirs.sh\n@@ -9,6 +9,7 @@ This test uses git to clone a Subversion repository that contains empty\n directories, and checks that corresponding directories are created in the\n local Git repository with placeholder files.'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n GIT_REPO=git-svn-repo\ndiff --git a/t/t9162-git-svn-dcommit-interactive.sh b/t/t9162-git-svn-dcommit-interactive.sh\nindex e38d9fa37b5..e2aa8ed88a9 100755\n--- a/t/t9162-git-svn-dcommit-interactive.sh\n+++ b/t/t9162-git-svn-dcommit-interactive.sh\n@@ -3,6 +3,8 @@\n # Copyright (c) 2011 Frédéric Heitzmann\n \n test_description='git svn dcommit --interactive series'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9164-git-svn-dcommit-concurrent.sh b/t/t9164-git-svn-dcommit-concurrent.sh\nindex 8466269bf50..1465156072e 100755\n--- a/t/t9164-git-svn-dcommit-concurrent.sh\n+++ b/t/t9164-git-svn-dcommit-concurrent.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='concurrent git svn dcommit'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n \ndiff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh\nindex 32814e75df5..c900231079c 100755\n--- a/t/t9501-gitweb-standalone-http-status.sh\n+++ b/t/t9501-gitweb-standalone-http-status.sh\n@@ -13,6 +13,7 @@ code and message.'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./lib-gitweb.sh\n \n #\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452050","messageId":"patch-v2-17.27-a93251edf85-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 17/27] revisions API: have release_revisions() release \"mailmap\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:32:07Z","receivedAt":"2022-03-23T20:33:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"prune_data\" in the \"struct ref_info\".\n\nThe log family of functions now calls the clear_mailmap() function\nadded in fa8afd18e5a (revisions API: provide and use a\nrelease_revisions(), 2021-09-19), allowing us to whitelist some tests\nwith \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nUnfortunately having a pointer to a mailmap in \"struct rev_info\"\ninstead of an embedded member that we \"own\" get a bit messy, as can be\nseen in the change to builtin/commit.c.\n\nWhen we free() this data we won't be able to tell apart a pointer to a\n\"mailmap\" on the heap from one on the stack. As seen in\nea57bc0d41b (log: add --use-mailmap option, 2013-01-05) the \"log\"\nfamily allocates it on the heap, but in the find_author_by_nickname()\ncode added in ea16794e430 (commit: search author pattern against\nmailmap, 2013-08-23) we allocated it on the stack instead.\n\nIdeally we'd simply change that member to a \"struct string_list\nmailmap\" and never free() the \"mailmap\" itself, but that would be a\nmuch larger change to the revisions API.\n\nWe have code that needs to hand an existing \"mailmap\" to a \"struct\nrev_info\", while we could change all of that, let's not go there\nnow.\n\nThe complexity isn't in the ownership of the \"mailmap\" per-se, but\nthat various things assume a \"rev_info.mailmap == NULL\" means \"doesn't\nwant mailmap\", if we changed that to an init'd \"struct string_list\nwe'd need to carefully refactor things to change those assumptions.\n\nLet's instead always free() it, and simply declare that if you add\nsuch a \"mailmap\" it must be allocated on the heap. Any modern libc\nwill correctly panic if we free() a stack variable, so this should be\nsafe going forward.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/commit.c                   | 5 ++---\n revision.c                         | 9 +++++++++\n t/t0056-git-C.sh                   | 1 +\n t/t3302-notes-index-expensive.sh   | 1 +\n t/t4055-diff-context.sh            | 1 +\n t/t4066-diff-emit-delay.sh         | 1 +\n t/t7008-filter-branch-null-sha1.sh | 1 +\n 7 files changed, 16 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex c7eda9bbb72..cd6cebcf8c8 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1100,7 +1100,6 @@ static const char *find_author_by_nickname(const char *name)\n \tstruct rev_info revs;\n \tstruct commit *commit;\n \tstruct strbuf buf = STRBUF_INIT;\n-\tstruct string_list mailmap = STRING_LIST_INIT_NODUP;\n \tconst char *av[20];\n \tint ac = 0;\n \n@@ -1111,7 +1110,8 @@ static const char *find_author_by_nickname(const char *name)\n \tav[++ac] = buf.buf;\n \tav[++ac] = NULL;\n \tsetup_revisions(ac, av, &revs, NULL);\n-\trevs.mailmap = &mailmap;\n+\trevs.mailmap = xmalloc(sizeof(struct string_list));\n+\tstring_list_init_nodup(revs.mailmap);\n \tread_mailmap(revs.mailmap);\n \n \tif (prepare_revision_walk(&revs))\n@@ -1122,7 +1122,6 @@ static const char *find_author_by_nickname(const char *name)\n \t\tctx.date_mode.type = DATE_NORMAL;\n \t\tstrbuf_release(&buf);\n \t\tformat_commit_message(commit, \"%aN <%aE>\", &buf, &ctx);\n-\t\tclear_mailmap(&mailmap);\n \t\trelease_revisions(&revs);\n \t\treturn strbuf_detach(&buf, NULL);\n \t}\ndiff --git a/revision.c b/revision.c\nindex 13b568aea76..d9e2b171f6d 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2933,12 +2933,21 @@ static void release_revisions_commit_list(struct rev_info *revs)\n \trevs->commits = NULL;\n }\n \n+static void release_revisions_mailmap(struct string_list *mailmap)\n+{\n+\tif (!mailmap)\n+\t\treturn;\n+\tclear_mailmap(mailmap);\n+\tfree(mailmap);\n+}\n+\n void release_revisions(struct rev_info *revs)\n {\n \tif (!revs)\n \t\treturn;\n \trelease_revisions_commit_list(revs);\n \tobject_array_clear(&revs->pending);\n+\trelease_revisions_mailmap(revs->mailmap);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\ndiff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh\nindex 2630e756dab..752aa8c9454 100755\n--- a/t/t0056-git-C.sh\n+++ b/t/t0056-git-C.sh\n@@ -2,6 +2,7 @@\n \n test_description='\"-C <path>\" option and its effects on other path-related options'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success '\"git -C <path>\" runs git from the directory <path>' '\ndiff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh\nindex bb5fea02a03..d0c4d38b4d4 100755\n--- a/t/t3302-notes-index-expensive.sh\n+++ b/t/t3302-notes-index-expensive.sh\n@@ -8,6 +8,7 @@ test_description='Test commit notes index (expensive!)'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n create_repo () {\ndiff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh\nindex 741e0803c1a..73048d0a526 100755\n--- a/t/t4055-diff-context.sh\n+++ b/t/t4055-diff-context.sh\n@@ -5,6 +5,7 @@\n \n test_description='diff.context configuration'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t4066-diff-emit-delay.sh b/t/t4066-diff-emit-delay.sh\nindex a1de63b77f8..0ecb3915412 100755\n--- a/t/t4066-diff-emit-delay.sh\n+++ b/t/t4066-diff-emit-delay.sh\n@@ -4,6 +4,7 @@ test_description='test combined/stat/moved interaction'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # This test covers a weird 3-way interaction between \"--cc -p\", which will run\ndiff --git a/t/t7008-filter-branch-null-sha1.sh b/t/t7008-filter-branch-null-sha1.sh\nindex 9ba9f24ad2f..93fbc92b8db 100755\n--- a/t/t7008-filter-branch-null-sha1.sh\n+++ b/t/t7008-filter-branch-null-sha1.sh\n@@ -1,6 +1,7 @@\n #!/bin/sh\n \n test_description='filter-branch removal of trees with null sha1'\n+\n . ./test-lib.sh\n \n test_expect_success 'setup: base commits' '\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452051","messageId":"patch-v2-20.27-857880bb4b6-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 20/27] revisions API: have release_revisions() release \"grep_filter\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:32:10Z","receivedAt":"2022-03-23T20:33:24Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"grep_filter\" in the \"struct ref_info\".This allows us to mark a test\nas passing under \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c               | 1 +\n t/t9151-svn-mergeinfo.sh | 1 -\n 2 files changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/revision.c b/revision.c\nindex 71f4ef3530d..eefb188d05e 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2961,6 +2961,7 @@ void release_revisions(struct rev_info *revs)\n \trelease_revisions_cmdline(&revs->cmdline);\n \tlist_objects_filter_release(&revs->filter);\n \trelease_revisions_mailmap(revs->mailmap);\n+\tfree_grep_patterns(&revs->grep_filter);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\ndiff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh\nindex 85221d439bd..c93a5beab25 100755\n--- a/t/t9151-svn-mergeinfo.sh\n+++ b/t/t9151-svn-mergeinfo.sh\n@@ -5,7 +5,6 @@\n \n test_description='git-svn svn mergeinfo properties'\n \n-TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'load svn dump' \"\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452052","messageId":"patch-v2-22.27-827430315fa-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 22/27] revisions API: clear \"boundary_commits\" in release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:32:12Z","receivedAt":"2022-03-23T20:33:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Clear the \"boundary_commits\" object_array in release_revisions(). This\nmakes a few more tests pass under SANITIZE=leak, including\n\"t/t4126-apply-empty.sh\" which started failed as an UNLEAK() in\ncmd_format_patch() was removed in a preceding commit.\n\nThis also re-marks the various tests relying on \"git format-patch\" as\npassing under \"SANITIZE=leak\", in the preceding \"revisions API users:\nuse release_revisions() in builtin/log.c\" commit those were marked as\nfailing as we removed the UNLEAK(rev) from cmd_format_patch() in\n\"builtin/log.c\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c                           | 1 +\n t/t4021-format-patch-numbered.sh     | 1 +\n t/t4028-format-patch-mime-headers.sh | 2 ++\n t/t4036-format-patch-signer-mime.sh  | 1 +\n t/t4122-apply-symlink-inside.sh      | 1 +\n t/t4126-apply-empty.sh               | 1 +\n t/t6110-rev-list-sparse.sh           | 1 +\n t/t9001-send-email.sh                | 1 +\n t/t9116-git-svn-log.sh               | 1 -\n 9 files changed, 9 insertions(+), 1 deletion(-)\n\ndiff --git a/revision.c b/revision.c\nindex 0f9ded78f5b..020f32f6eca 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2958,6 +2958,7 @@ void release_revisions(struct rev_info *revs)\n \t\treturn;\n \trelease_revisions_commit_list(revs);\n \tobject_array_clear(&revs->pending);\n+\tobject_array_clear(&revs->boundary_commits);\n \trelease_revisions_cmdline(&revs->cmdline);\n \tlist_objects_filter_release(&revs->filter);\n \tclear_pathspec(&revs->prune_data);\ndiff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh\nindex 9be65fd4440..1219aa226dc 100755\n--- a/t/t4021-format-patch-numbered.sh\n+++ b/t/t4021-format-patch-numbered.sh\n@@ -5,6 +5,7 @@\n \n test_description='Format-patch numbering options'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4028-format-patch-mime-headers.sh b/t/t4028-format-patch-mime-headers.sh\nindex 204ba673cb5..60cb819c42e 100755\n--- a/t/t4028-format-patch-mime-headers.sh\n+++ b/t/t4028-format-patch-mime-headers.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='format-patch mime headers and extra headers do not conflict'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'create commit with utf-8 body' '\ndiff --git a/t/t4036-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh\nindex 98d9713d8b2..48655bcc789 100755\n--- a/t/t4036-format-patch-signer-mime.sh\n+++ b/t/t4036-format-patch-signer-mime.sh\n@@ -2,6 +2,7 @@\n \n test_description='format-patch -s should force MIME encoding as needed'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh\nindex aa52de401b9..96965373036 100755\n--- a/t/t4122-apply-symlink-inside.sh\n+++ b/t/t4122-apply-symlink-inside.sh\n@@ -4,6 +4,7 @@ test_description='apply to deeper directory without getting fooled with symlink'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh\nindex 66a7ba8ab8f..ece9fae207d 100755\n--- a/t/t4126-apply-empty.sh\n+++ b/t/t4126-apply-empty.sh\n@@ -2,6 +2,7 @@\n \n test_description='apply empty'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t6110-rev-list-sparse.sh b/t/t6110-rev-list-sparse.sh\nindex 13c1da53528..ddefc7f24ee 100755\n--- a/t/t6110-rev-list-sparse.sh\n+++ b/t/t6110-rev-list-sparse.sh\n@@ -4,6 +4,7 @@ test_description='operations that cull histories in unusual ways'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh\nindex 84d0f40d76a..dfa6b20f7a6 100755\n--- a/t/t9001-send-email.sh\n+++ b/t/t9001-send-email.sh\n@@ -4,6 +4,7 @@ test_description='git send-email'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # May be altered later in the test\ndiff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh\nindex 34f6c80dea3..d74d7b2de68 100755\n--- a/t/t9116-git-svn-log.sh\n+++ b/t/t9116-git-svn-log.sh\n@@ -5,7 +5,6 @@\n \n test_description='git svn log tests'\n \n-TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup repository and import' '\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452053","messageId":"patch-v2-21.27-f98d473f08e-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 21/27] revisions API: have release_revisions() release \"prune_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:32:11Z","receivedAt":"2022-03-23T20:33:35Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"prune_data\" in the \"struct ref_info\". This means that any code that\ncalls \"release_revisions()\" already can get rid of adjacent calls to\nclear_pathspec().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n add-interactive.c | 2 --\n builtin/add.c     | 1 -\n builtin/stash.c   | 2 --\n diff-lib.c        | 1 -\n revision.c        | 1 +\n wt-status.c       | 2 --\n 6 files changed, 1 insertion(+), 8 deletions(-)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex 54cdfc82017..6047e8f6489 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -568,8 +568,6 @@ static int get_modified_files(struct repository *r,\n \t\t\trun_diff_files(&rev, 0);\n \t\t}\n \n-\t\tif (ps)\n-\t\t\tclear_pathspec(&rev.prune_data);\n \t\trelease_revisions(&rev);\n \t}\n \thashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 115a26ea633..fc729e14c17 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -142,7 +142,6 @@ int add_files_to_cache(const char *prefix,\n \trev.diffopt.flags.override_submodule_config = 1;\n \trev.max_count = 0; /* do not compare unmerged paths with stage #2 */\n \trun_diff_files(&rev, DIFF_RACY_IS_MODIFIED);\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n \treturn !!data.add_errors;\n }\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 878eaf2e125..ac83d7f8af4 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1060,7 +1060,6 @@ static int check_changes_tracked_files(const struct pathspec *ps)\n \t}\n \n done:\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n \treturn ret;\n }\n@@ -1272,7 +1271,6 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps\n \n done:\n \tdiscard_index(&istate);\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n \tstrbuf_release(&diff_output);\n \tremove_path(stash_index_path.buf);\ndiff --git a/diff-lib.c b/diff-lib.c\nindex 0f16281253f..298265e5b54 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -641,7 +641,6 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)\n \n \tif (diff_cache(&revs, tree_oid, NULL, 1))\n \t\texit(128);\n-\tclear_pathspec(&revs.prune_data);\n \trelease_revisions(&revs);\n \treturn 0;\n }\ndiff --git a/revision.c b/revision.c\nindex eefb188d05e..0f9ded78f5b 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2960,6 +2960,7 @@ void release_revisions(struct rev_info *revs)\n \tobject_array_clear(&revs->pending);\n \trelease_revisions_cmdline(&revs->cmdline);\n \tlist_objects_filter_release(&revs->filter);\n+\tclear_pathspec(&revs->prune_data);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n }\ndiff --git a/wt-status.c b/wt-status.c\nindex a14fad1e03a..61e0c1022f5 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -616,7 +616,6 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)\n \trev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;\n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_files(&rev, 0);\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n }\n \n@@ -664,7 +663,6 @@ static void wt_status_collect_changes_index(struct wt_status *s)\n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_index(&rev, 1);\n \trelease_revisions(&rev);\n-\tclear_pathspec(&rev.prune_data);\n }\n \n static int add_file_to_list(const struct object_id *oid,\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452054","messageId":"patch-v2-23.27-c559b7d538b-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 23/27] revisions API: release \"reflog_info\" in release revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:32:13Z","receivedAt":"2022-03-23T20:33:46Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a missing reflog_walk_info_release() to \"reflog-walk.c\" and use it\nin release_revisions().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n reflog-walk.c            | 24 +++++++++++++++++++++++-\n reflog-walk.h            |  1 +\n revision.c               |  1 +\n t/t0100-previous.sh      |  1 +\n t/t1401-symbolic-ref.sh  |  2 ++\n t/t1411-reflog-show.sh   |  1 +\n t/t1412-reflog-loop.sh   |  2 ++\n t/t1415-worktree-refs.sh |  1 +\n 8 files changed, 32 insertions(+), 1 deletion(-)\n\ndiff --git a/reflog-walk.c b/reflog-walk.c\nindex 8ac4b284b6b..7aa6595a51f 100644\n--- a/reflog-walk.c\n+++ b/reflog-walk.c\n@@ -8,7 +8,7 @@\n \n struct complete_reflogs {\n \tchar *ref;\n-\tconst char *short_ref;\n+\tchar *short_ref;\n \tstruct reflog_info {\n \t\tstruct object_id ooid, noid;\n \t\tchar *email;\n@@ -51,9 +51,16 @@ static void free_complete_reflog(struct complete_reflogs *array)\n \t}\n \tfree(array->items);\n \tfree(array->ref);\n+\tfree(array->short_ref);\n \tfree(array);\n }\n \n+static void complete_reflogs_clear(void *util, const char *str)\n+{\n+\tstruct complete_reflogs *array = util;\n+\tfree_complete_reflog(array);\n+}\n+\n static struct complete_reflogs *read_complete_reflog(const char *ref)\n {\n \tstruct complete_reflogs *reflogs =\n@@ -116,6 +123,21 @@ void init_reflog_walk(struct reflog_walk_info **info)\n \t(*info)->complete_reflogs.strdup_strings = 1;\n }\n \n+void reflog_walk_info_release(struct reflog_walk_info *info)\n+{\n+\tsize_t i;\n+\n+\tif (!info)\n+\t\treturn;\n+\n+\tfor (i = 0; i < info->nr; i++)\n+\t\tfree(info->logs[i]);\n+\tstring_list_clear_func(&info->complete_reflogs,\n+\t\t\t       complete_reflogs_clear);\n+\tfree(info->logs);\n+\tfree(info);\n+}\n+\n int add_reflog_for_walk(struct reflog_walk_info *info,\n \t\tstruct commit *commit, const char *name)\n {\ndiff --git a/reflog-walk.h b/reflog-walk.h\nindex e9e00ffd479..8076f10d9fb 100644\n--- a/reflog-walk.h\n+++ b/reflog-walk.h\n@@ -8,6 +8,7 @@ struct reflog_walk_info;\n struct date_mode;\n \n void init_reflog_walk(struct reflog_walk_info **info);\n+void reflog_walk_info_release(struct reflog_walk_info *info);\n int add_reflog_for_walk(struct reflog_walk_info *info,\n \t\t\tstruct commit *commit, const char *name);\n void show_reflog_message(struct reflog_walk_info *info, int,\ndiff --git a/revision.c b/revision.c\nindex 020f32f6eca..ce23590a219 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2964,6 +2964,7 @@ void release_revisions(struct rev_info *revs)\n \tclear_pathspec(&revs->prune_data);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n+\treflog_walk_info_release(revs->reflog_info);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\ndiff --git a/t/t0100-previous.sh b/t/t0100-previous.sh\nindex 69beb59f627..a16cc3d2983 100755\n--- a/t/t0100-previous.sh\n+++ b/t/t0100-previous.sh\n@@ -5,6 +5,7 @@ test_description='previous branch syntax @{-n}'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'branch -d @{-1}' '\ndiff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh\nindex 132a1b885ac..9fb0b90f252 100755\n--- a/t/t1401-symbolic-ref.sh\n+++ b/t/t1401-symbolic-ref.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='basic symbolic-ref tests'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # If the tests munging HEAD fail, they can break detection of\ndiff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh\nindex 0bb319b944a..3770ceffafd 100755\n--- a/t/t1411-reflog-show.sh\n+++ b/t/t1411-reflog-show.sh\n@@ -4,6 +4,7 @@ test_description='Test reflog display routines'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t1412-reflog-loop.sh b/t/t1412-reflog-loop.sh\nindex 977603f7f1c..ff30874f940 100755\n--- a/t/t1412-reflog-loop.sh\n+++ b/t/t1412-reflog-loop.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='reflog walk shows repeated commits again'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup commits' '\ndiff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh\nindex a3e6ea08088..3b531842dd4 100755\n--- a/t/t1415-worktree-refs.sh\n+++ b/t/t1415-worktree-refs.sh\n@@ -2,6 +2,7 @@\n \n test_description='per-worktree refs'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452057","messageId":"patch-v2-24.27-c3a22f5b474-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:32:14Z","receivedAt":"2022-03-23T20:33:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Call diff_free() on the \"pruning\" member of \"struct rev_info\".  Doing\nso makes several tests pass under SANITIZE=leak.\n\nThis was also the last missing piece that allows us to remove the\nUNLEAK() in \"cmd_diff\" and \"cmd_diff_index\", which allows us to use\nthose commands as a canary for general leaks in the revisions API. See\n[1] for further rationale, and 886e1084d78 (builtin/: add UNLEAKs,\n2017-10-01) for the commit that added the UNLEAK() there.\n\n1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/diff-index.c                | 1 -\n builtin/diff.c                      | 1 -\n revision.c                          | 1 +\n t/t1001-read-tree-m-2way.sh         | 1 +\n t/t1002-read-tree-m-u-2way.sh       | 1 +\n t/t2200-add-update.sh               | 1 +\n t/t4039-diff-assume-unchanged.sh    | 1 +\n t/t4206-log-follow-harder-copies.sh | 2 ++\n t/t6131-pathspec-icase.sh           | 2 ++\n 9 files changed, 9 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/diff-index.c b/builtin/diff-index.c\nindex 3a83183c312..7d158af6b6d 100644\n--- a/builtin/diff-index.c\n+++ b/builtin/diff-index.c\n@@ -70,7 +70,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)\n \t\treturn -1;\n \t}\n \tresult = run_diff_index(&rev, option);\n-\tUNLEAK(rev);\n \tresult = diff_result_code(&rev.diffopt, result);\n \trelease_revisions(&rev);\n \treturn result;\ndiff --git a/builtin/diff.c b/builtin/diff.c\nindex dd48336da56..f539132ac68 100644\n--- a/builtin/diff.c\n+++ b/builtin/diff.c\n@@ -594,7 +594,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \tresult = diff_result_code(&rev.diffopt, result);\n \tif (1 < rev.diffopt.skip_stat_unmatch)\n \t\trefresh_index_quietly();\n-\tUNLEAK(rev);\n \trelease_revisions(&rev);\n \tUNLEAK(ent);\n \tUNLEAK(blob);\ndiff --git a/revision.c b/revision.c\nindex ce23590a219..32e4d10f27e 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2964,6 +2964,7 @@ void release_revisions(struct rev_info *revs)\n \tclear_pathspec(&revs->prune_data);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n+\tdiff_free(&revs->pruning);\n \treflog_walk_info_release(revs->reflog_info);\n }\n \ndiff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh\nindex 0710b1fb1e9..516a6112fdc 100755\n--- a/t/t1001-read-tree-m-2way.sh\n+++ b/t/t1001-read-tree-m-2way.sh\n@@ -21,6 +21,7 @@ In the test, these paths are used:\n \tyomin   - not in H or M\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-read-tree.sh\n \ndiff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh\nindex 46cbd5514a6..bd5313caec9 100755\n--- a/t/t1002-read-tree-m-u-2way.sh\n+++ b/t/t1002-read-tree-m-u-2way.sh\n@@ -9,6 +9,7 @@ This is identical to t1001, but uses -u to update the work tree as well.\n \n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-read-tree.sh\n \ndiff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh\nindex 0c38f8e3569..be394f1131a 100755\n--- a/t/t2200-add-update.sh\n+++ b/t/t2200-add-update.sh\n@@ -14,6 +14,7 @@ only the updates to dir/sub.\n Also tested are \"git add -u\" without limiting, and \"git add -u\"\n without contents changes, and other conditions'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh\nindex 0eb0314a8b3..78090e6852d 100755\n--- a/t/t4039-diff-assume-unchanged.sh\n+++ b/t/t4039-diff-assume-unchanged.sh\n@@ -2,6 +2,7 @@\n \n test_description='diff with assume-unchanged entries'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # external diff has been tested in t4020-diff-external.sh\ndiff --git a/t/t4206-log-follow-harder-copies.sh b/t/t4206-log-follow-harder-copies.sh\nindex 4871a5dc92f..33ecf82c7f9 100755\n--- a/t/t4206-log-follow-harder-copies.sh\n+++ b/t/t4206-log-follow-harder-copies.sh\n@@ -6,6 +6,8 @@\n test_description='Test --follow should always find copies hard in git log.\n \n '\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-diff.sh\n \ndiff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh\nindex 39fc3f6769b..770cce026cb 100755\n--- a/t/t6131-pathspec-icase.sh\n+++ b/t/t6131-pathspec-icase.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='test case insensitive pathspec limiting'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n if test_have_prereq CASE_INSENSITIVE_FS\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452055","messageId":"patch-v2-27.27-2a4d50a0ec8-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 27/27] revisions API: add a TODO for diff_free(&revs->diffopt)","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:32:17Z","receivedAt":"2022-03-23T20:33:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a TODO comment indicating that we should release \"diffopt\" in\nrelease_revisions(). In a preceding commit we started releasing the\n\"pruning\" member of the same type, but handling \"diffopt\" will require\nus to untangle the \"no_free\" conditions I added in e900d494dcf (diff:\nadd an API for deferred freeing, 2021-02-11).\n\nLet's leave a TODO comment to that effect, and so that we don't forget\nrefactor code that was changed to use release_revisions() in earlier\ncommits to stop using the \"diffopt\" member after a call to\nrelease_revisions(). This works currently, but would become a logic\nerror as soon as we started freeing \"diffopt\". Doing that change now\ndoesn't harm anything, and future-proofs us against a later change to\nrelease_revisions().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n diff-lib.c  | 4 +++-\n revision.c  | 1 +\n wt-status.c | 6 ++++--\n 3 files changed, 8 insertions(+), 3 deletions(-)\n\ndiff --git a/diff-lib.c b/diff-lib.c\nindex 298265e5b54..7eb66a417aa 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -651,6 +651,7 @@ int index_differs_from(struct repository *r,\n {\n \tstruct rev_info rev;\n \tstruct setup_revision_opt opt;\n+\tunsigned has_changes;\n \n \trepo_init_revisions(r, &rev, NULL);\n \tmemset(&opt, 0, sizeof(opt));\n@@ -662,8 +663,9 @@ int index_differs_from(struct repository *r,\n \t\tdiff_flags_or(&rev.diffopt.flags, flags);\n \trev.diffopt.ita_invisible_in_index = ita_invisible_in_index;\n \trun_diff_index(&rev, 1);\n+\thas_changes = rev.diffopt.flags.has_changes;\n \trelease_revisions(&rev);\n-\treturn (rev.diffopt.flags.has_changes != 0);\n+\treturn (has_changes != 0);\n }\n \n static struct strbuf *idiff_prefix_cb(struct diff_options *opt, void *data)\ndiff --git a/revision.c b/revision.c\nindex ab570ac754c..ebcb87acaa0 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2967,6 +2967,7 @@ void release_revisions(struct rev_info *revs)\n \tdate_mode_release(&revs->date_mode);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n+\t/* TODO (need to handle \"no_free\"): diff_free(&revs->diffopt) */\n \tdiff_free(&revs->pruning);\n \treflog_walk_info_release(revs->reflog_info);\n \trelease_revisions_topo_walk_info(revs->topo_walk_info);\ndiff --git a/wt-status.c b/wt-status.c\nindex 61e0c1022f5..102d904adcb 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -2545,8 +2545,9 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)\n \trev_info.diffopt.flags.quick = 1;\n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_files(&rev_info, 0);\n+\tresult = diff_result_code(&rev_info.diffopt, result);\n \trelease_revisions(&rev_info);\n-\treturn diff_result_code(&rev_info.diffopt, result);\n+\treturn result;\n }\n \n /**\n@@ -2578,8 +2579,9 @@ int has_uncommitted_changes(struct repository *r,\n \n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_index(&rev_info, 1);\n+\tresult = diff_result_code(&rev_info.diffopt, result);\n \trelease_revisions(&rev_info);\n-\treturn diff_result_code(&rev_info.diffopt, result);\n+\treturn result;\n }\n \n /**\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452056","messageId":"patch-v2-25.27-dabbb430ece-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 25/27] revisions API: have release_revisions() release \"date_mode\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:32:15Z","receivedAt":"2022-03-23T20:33:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"date_mode\" in the \"struct ref_info\".\n\nThis uses the date_mode_release() function added in 974c919d36d (date\nAPI: add and use a date_mode_release(), 2022-02-16). As that commit\nnotes \"t7004-tag.sh\" tests for the leaks that are being fixed\nhere. That test now fails \"only\" 44 tests, instead of the 46 it failed\nbefore this change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/revision.c b/revision.c\nindex 32e4d10f27e..62dddeeeab5 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2962,6 +2962,7 @@ void release_revisions(struct rev_info *revs)\n \trelease_revisions_cmdline(&revs->cmdline);\n \tlist_objects_filter_release(&revs->filter);\n \tclear_pathspec(&revs->prune_data);\n+\tdate_mode_release(&revs->date_mode);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n \tdiff_free(&revs->pruning);\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452058","messageId":"patch-v2-26.27-644d1b54afe-20220323T203149Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v2 26/27] revisions API: have release_revisions() release \"topo_walk_info\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-23T20:32:16Z","receivedAt":"2022-03-23T20:33:55Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor the existing reset_topo_walk() into a thin wrapper for a\nrelease_revisions_topo_walk_info() + resetting the member to \"NULL\",\nand call release_revisions_topo_walk_info() from release_revisions().\n\nThis fixes memory leaks that have been with us ever since\n\"topo_walk_info\" was added to revision.[ch] in\nf0d9cc4196a (revision.c: begin refactoring --topo-order logic,\n2018-11-01).\n\nDue to various other leaks this makes no tests pass in their entirety,\nbut e.g. before this running this on git.git:\n\n    ./git -P log --pretty=tformat:\"%P   %H | %s\" --parents --full-history --topo-order -3 -- README.md\n\nWould report under SANITIZE=leak:\n\n    SUMMARY: LeakSanitizer: 531064 byte(s) leaked in 6 allocation(s).\n\nNow we'll free all of that memory.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 16 ++++++++++++----\n 1 file changed, 12 insertions(+), 4 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex 62dddeeeab5..ab570ac754c 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2952,6 +2952,8 @@ static void release_revisions_mailmap(struct string_list *mailmap)\n \tfree(mailmap);\n }\n \n+static void release_revisions_topo_walk_info(struct topo_walk_info *info);\n+\n void release_revisions(struct rev_info *revs)\n {\n \tif (!revs)\n@@ -2967,6 +2969,7 @@ void release_revisions(struct rev_info *revs)\n \tfree_grep_patterns(&revs->grep_filter);\n \tdiff_free(&revs->pruning);\n \treflog_walk_info_release(revs->reflog_info);\n+\trelease_revisions_topo_walk_info(revs->topo_walk_info);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n@@ -3479,17 +3482,22 @@ static void compute_indegrees_to_depth(struct rev_info *revs,\n \t\tindegree_walk_step(revs);\n }\n \n-static void reset_topo_walk(struct rev_info *revs)\n+static void release_revisions_topo_walk_info(struct topo_walk_info *info)\n {\n-\tstruct topo_walk_info *info = revs->topo_walk_info;\n-\n+\tif (!info)\n+\t\treturn;\n \tclear_prio_queue(&info->explore_queue);\n \tclear_prio_queue(&info->indegree_queue);\n \tclear_prio_queue(&info->topo_queue);\n \tclear_indegree_slab(&info->indegree);\n \tclear_author_date_slab(&info->author_date);\n+\tfree(info);\n+}\n \n-\tFREE_AND_NULL(revs->topo_walk_info);\n+static void reset_topo_walk(struct rev_info *revs)\n+{\n+\trelease_revisions_topo_walk_info(revs->topo_walk_info);\n+\trevs->topo_walk_info = NULL;\n }\n \n static void init_topo_walk(struct rev_info *revs)\n-- \n2.35.1.1452.ga7cfc89151f\n\n"},{"id":"452083","messageId":"xmqqr16sm1im.fsf@gitster.g","threadId":"57525","inReplyTo":"patch-v2-01.27-8fbafe61053-20220323T203149Z-avarab@gmail.com","subject":"Re: [PATCH v2 01/27] t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-24T04:13:05Z","receivedAt":"2022-03-24T04:13:20Z","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> Fix a memory leak that's been with us since f9500261e0a (fast-rebase:\n> write conflict state to working tree, index, and HEAD, 2021-05-20)\n> changed this code to move these strbuf_release() into an if/else\n> block.\n>\n> We'll also add to \"reflog_msg\" in the \"else\" arm of the \"if\" block\n> being modified here, and we'll append to \"branch_msg\" in both\n> cases. But after f9500261e0a only the \"if\" block would free these two\n> \"struct strbuf\".\n>\n> This change is needed so that a subsequent addition of a \"goto\n> cleanup\" pattern when adding release_revisions() doesn't solve this\n> unrelated bug while it's at it.\n\nWith or without the goto-cleanup change, this fix alone is the right\nfix for these leaks.  I would not have written the last paragraph if\nI were working on this topic ;-)\n\n\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  t/helper/test-fast-rebase.c | 5 +++--\n>  1 file changed, 3 insertions(+), 2 deletions(-)\n>\n> diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c\n> index fc2d4609043..993b90eaedd 100644\n> --- a/t/helper/test-fast-rebase.c\n> +++ b/t/helper/test-fast-rebase.c\n> @@ -201,8 +201,6 @@ int cmd__fast_rebase(int argc, const char **argv)\n>  \t\t}\n>  \t\tif (create_symref(\"HEAD\", branch_name.buf, reflog_msg.buf) < 0)\n>  \t\t\tdie(_(\"unable to update HEAD\"));\n> -\t\tstrbuf_release(&reflog_msg);\n> -\t\tstrbuf_release(&branch_name);\n>  \n>  \t\tprime_cache_tree(the_repository, the_repository->index,\n>  \t\t\t\t result.tree);\n> @@ -221,5 +219,8 @@ int cmd__fast_rebase(int argc, const char **argv)\n>  \tif (write_locked_index(&the_index, &lock,\n>  \t\t\t       COMMIT_LOCK | SKIP_IF_UNCHANGED))\n>  \t\tdie(_(\"unable to write %s\"), get_index_file());\n> +\n> +\tstrbuf_release(&reflog_msg);\n> +\tstrbuf_release(&branch_name);\n>  \treturn (result.clean == 0);\n>  }\n"},{"id":"452084","messageId":"xmqqlex0m11y.fsf@gitster.g","threadId":"57525","inReplyTo":"patch-v2-02.27-77e7c2c321e-20220323T203149Z-avarab@gmail.com","subject":"Re: [PATCH v2 02/27] blame: use \"goto cleanup\" for cleanup_scoreboard()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-24T04:23:05Z","receivedAt":"2022-03-24T04:23:16Z","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> Amend a freeing pattern added in 0906ac2b54b (blame: use changed-path\n> Bloom filters, 2020-04-16) to use a \"goto cleanup\", so that we can be\n> sure that we call cleanup_scoreboard().\n\nMakes sense.\n\n> This change is needed so that a subsequent addition of a\n> release_revisions() doesn't solve this unrelated issue while it's at\n> it.\n\nAgain, this is irrelevant.  This patch alone with or without\nanything else is the right fix to plug the leakage of the\nscoreboard.\n\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>  builtin/blame.c | 3 ++-\n>  1 file changed, 2 insertions(+), 1 deletion(-)\n>\n> diff --git a/builtin/blame.c b/builtin/blame.c\n> index 8d15b68afc9..885b381ab83 100644\n> --- a/builtin/blame.c\n> +++ b/builtin/blame.c\n> @@ -1167,7 +1167,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n>  \tif (!incremental)\n>  \t\tsetup_pager();\n>  \telse\n> -\t\treturn 0;\n> +\t\tgoto cleanup;\n>  \n>  \tblame_sort_final(&sb);\n>  \n> @@ -1201,6 +1201,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n>  \t\tprintf(\"num commits: %d\\n\", sb.num_commits);\n>  \t}\n>  \n> +cleanup:\n>  \tcleanup_scoreboard(&sb);\n>  \treturn 0;\n>  }\n"},{"id":"452085","messageId":"xmqqa6dgm0l8.fsf@gitster.g","threadId":"57525","inReplyTo":"patch-v2-05.27-4c0718b43d7-20220323T203149Z-avarab@gmail.com","subject":"Re: [PATCH v2 05/27] revision.[ch]: split freeing of revs->commit into a function","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-24T04:33:07Z","receivedAt":"2022-03-24T04:33:16Z","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> +static void release_revisions_commit_list(struct rev_info *revs)\n> +{\n> +\tstruct commit_list *commits = revs->commits;\n> +\n> +\tif (!commits)\n> +\t\treturn;\n> +\tfree_commit_list(commits);\n> +\trevs->commits = NULL;\n> +}\n\nIt makes sense to have this as a separate helper, but the original\nimplementation this was lifted from is much easier to follow than\nthis version with an unnecessary rewrite, I would think.\n\n> @@ -4080,10 +4090,7 @@ static void create_boundary_commit_list(struct rev_info *revs)\n>  \t * boundary commits anyway.  (This is what the code has always\n>  \t * done.)\n>  \t */\n> -\tif (revs->commits) {\n> -\t\tfree_commit_list(revs->commits);\n> -\t\trevs->commits = NULL;\n> -\t}\n> +\trelease_revisions_commit_list(revs);\n\nIOW\n\nstatic void release_revisions_commit_list(struct rev_info *revs)\n{\n\tif (revs->commits) {\n\t\tfree_commit_list(revs->commits);\n\t\trevs->commits = NULL;\n\t}\n}\n\n"},{"id":"452086","messageId":"xmqqfsn8m0la.fsf@gitster.g","threadId":"57525","inReplyTo":"patch-v2-04.27-64a76629e23-20220323T203149Z-avarab@gmail.com","subject":"Re: [PATCH v2 04/27] format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-24T04:33:05Z","receivedAt":"2022-03-24T04:33:28Z","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> Fix two memory leaks in \"struct rev_info\" by freeing that memory in\n> cmd_format_patch(). These two are unusual special-cases in being in\n> the \"struct rev_info\", but not being \"owned\" by the code in\n> revision.c. I.e. they're members of the struct so that this code in\n> \"builtin/log.c\" can pass information code in log-tree.c.\n\nHmph, if we are eventually adding a new API function to clear a\nrev-info structure, shouldn't these members be released there\ninstead, I wonder.\n\nThis is the only user of this member, so it does not matter too much\neither way, though.\n\n\n> diff --git a/builtin/log.c b/builtin/log.c\n> index 634dc782cce..6f9928fabfe 100644\n> --- a/builtin/log.c\n> +++ b/builtin/log.c\n> @@ -1747,6 +1747,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n>  \tstruct commit *commit;\n>  \tstruct commit **list = NULL;\n>  \tstruct rev_info rev;\n> +\tchar *to_free = NULL;\n>  \tstruct setup_revision_opt s_r_opt;\n>  \tint nr = 0, total, i;\n>  \tint use_stdout = 0;\n> @@ -1947,7 +1948,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n>  \t\tstrbuf_addch(&buf, '\\n');\n>  \t}\n>  \n> -\trev.extra_headers = strbuf_detach(&buf, NULL);\n> +\trev.extra_headers = to_free = strbuf_detach(&buf, NULL);\n>  \n>  \tif (from) {\n>  \t\tif (split_ident_line(&rev.from_ident, from, strlen(from)))\n> @@ -2284,6 +2285,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n>  \tstrbuf_release(&rdiff1);\n>  \tstrbuf_release(&rdiff2);\n>  \tstrbuf_release(&rdiff_title);\n> +\tfree(to_free);\n> +\tif (rev.ref_message_ids)\n> +\t\tstring_list_clear(rev.ref_message_ids, 0);\n> +\tfree(rev.ref_message_ids);\n>  \tUNLEAK(rev);\n>  \treturn 0;\n>  }\n"},{"id":"452087","messageId":"xmqq4k3om04m.fsf@gitster.g","threadId":"57525","inReplyTo":"patch-v2-06.27-7194aac88e1-20220323T203149Z-avarab@gmail.com","subject":"Re: [PATCH v2 06/27] revision.[ch]: provide and start using a release_revisions()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-24T04:43:05Z","receivedAt":"2022-03-24T04:53:13Z","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> diff --git a/revision.c b/revision.c\n> index 303d1188207..90bac9ada03 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -2933,6 +2933,13 @@ static void release_revisions_commit_list(struct rev_info *revs)\n>  \trevs->commits = NULL;\n>  }\n>  \n> +void release_revisions(struct rev_info *revs)\n> +{\n> +\tif (!revs)\n> +\t\treturn;\n> +\tobject_array_clear(&revs->pending);\n> +}\n\nYay.\n\nIt is unclear why we want to allow passing NULL to this, though.  Do\nwe even have any code paths that allocate on-heap rev-info?  Address\nof an on-stack or global rev_info will never be NULL.\n"},{"id":"452088","messageId":"xmqqy210kl3i.fsf@gitster.g","threadId":"57525","inReplyTo":"patch-v2-08.27-42ad1208934-20220323T203149Z-avarab@gmail.com","subject":"Re: [PATCH v2 08/27] revisions API users: use release_revisions() needing \"{ 0 }\" init","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-24T04:53:05Z","receivedAt":"2022-03-24T04:53:13Z","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> Use release_revisions() to various users of \"struct rev_list\" which\n> need to have their \"struct rev_info\" zero-initialized before we can\n> start using it. In all of these cases we might \"goto cleanup\" (or equivalent),\n\nI didn't look at the bisect code, but the bundle one looks iffy from\nthe point of view of API cleanliness.  If we have not yet called\nrepo_init_revisions() on a revs, we should refrain from calling\nrelease_revisions() on it in the first place, no?\n"},{"id":"452191","messageId":"220324.86pmmbmglo.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"xmqqr16sm1im.fsf@gitster.g","subject":"Re: [PATCH v2 01/27] t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-24T16:57:39Z","receivedAt":"2022-03-24T16:59:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Mar 23 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> Fix a memory leak that's been with us since f9500261e0a (fast-rebase:\n>> write conflict state to working tree, index, and HEAD, 2021-05-20)\n>> changed this code to move these strbuf_release() into an if/else\n>> block.\n>>\n>> We'll also add to \"reflog_msg\" in the \"else\" arm of the \"if\" block\n>> being modified here, and we'll append to \"branch_msg\" in both\n>> cases. But after f9500261e0a only the \"if\" block would free these two\n>> \"struct strbuf\".\n>>\n>> This change is needed so that a subsequent addition of a \"goto\n>> cleanup\" pattern when adding release_revisions() doesn't solve this\n>> unrelated bug while it's at it.\n>\n> With or without the goto-cleanup change, this fix alone is the right\n> fix for these leaks.  I would not have written the last paragraph if\n> I were working on this topic ;-)\n\nTo this and your 02/27 comment: Sure, I can remove that.\n\nIn general I think it's bad form for individual commits to discuss the\nseries they're in, they should be stand-alone.\n\nBut I'm pretty sure if I left this out I'd have gotten questions about\nwhy these seemingly unrelated leak fixes are here as art of series\nimplementing release_revisions(), from either you or someone else :)\n"},{"id":"452192","messageId":"220324.86lewzmgiu.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"xmqqfsn8m0la.fsf@gitster.g","subject":"Re: [PATCH v2 04/27] format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-24T16:59:49Z","receivedAt":"2022-03-24T17:01:29Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Mar 23 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> Fix two memory leaks in \"struct rev_info\" by freeing that memory in\n>> cmd_format_patch(). These two are unusual special-cases in being in\n>> the \"struct rev_info\", but not being \"owned\" by the code in\n>> revision.c. I.e. they're members of the struct so that this code in\n>> \"builtin/log.c\" can pass information code in log-tree.c.\n>\n> Hmph, if we are eventually adding a new API function to clear a\n> rev-info structure, shouldn't these members be released there\n> instead, I wonder.\n>\n> This is the only user of this member, so it does not matter too much\n> either way, though.\n\nIdeally, but in practice \"struct rev_info\" is both before and after this\nchange a mixed bag of stuff it \"owns\" and stuff that others \"hand over\"\nto it, sometimes permanently, and sometimes just to hold on for a bit.\n\nSo it can't really free() everything in the struct, at least witohut\nsome larger refactorings, which I'm trying to avoid here.\n\nThe \"diffopt\" at the end is another such case, which is left for another\nfollow-up.\n\nBut for now is it OK if we keep this as-is?\n"},{"id":"452193","messageId":"220324.86h77nmgha.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"xmqqa6dgm0l8.fsf@gitster.g","subject":"Re: [PATCH v2 05/27] revision.[ch]: split freeing of revs->commit into a function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-24T17:01:22Z","receivedAt":"2022-03-24T17:02:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Mar 23 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> +static void release_revisions_commit_list(struct rev_info *revs)\n>> +{\n>> +\tstruct commit_list *commits = revs->commits;\n>> +\n>> +\tif (!commits)\n>> +\t\treturn;\n>> +\tfree_commit_list(commits);\n>> +\trevs->commits = NULL;\n>> +}\n>\n> It makes sense to have this as a separate helper, but the original\n> implementation this was lifted from is much easier to follow than\n> this version with an unnecessary rewrite, I would think.\n>\n>> @@ -4080,10 +4090,7 @@ static void create_boundary_commit_list(struct rev_info *revs)\n>>  \t * boundary commits anyway.  (This is what the code has always\n>>  \t * done.)\n>>  \t */\n>> -\tif (revs->commits) {\n>> -\t\tfree_commit_list(revs->commits);\n>> -\t\trevs->commits = NULL;\n>> -\t}\n>> +\trelease_revisions_commit_list(revs);\n>\n> IOW\n>\n> static void release_revisions_commit_list(struct rev_info *revs)\n> {\n> \tif (revs->commits) {\n> \t\tfree_commit_list(revs->commits);\n> \t\trevs->commits = NULL;\n> \t}\n> }\n\nSure, will change it.\n\nI think the pre-image is preferrable in context, i.e. to have them all\nuse the same early abort pattern, but not enough to argue the point :)\n\n\n\n"},{"id":"452194","messageId":"220324.86czibmgft.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"xmqq4k3om04m.fsf@gitster.g","subject":"Re: [PATCH v2 06/27] revision.[ch]: provide and start using a release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-24T17:02:26Z","receivedAt":"2022-03-24T17:03:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Mar 23 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> diff --git a/revision.c b/revision.c\n>> index 303d1188207..90bac9ada03 100644\n>> --- a/revision.c\n>> +++ b/revision.c\n>> @@ -2933,6 +2933,13 @@ static void release_revisions_commit_list(struct rev_info *revs)\n>>  \trevs->commits = NULL;\n>>  }\n>>  \n>> +void release_revisions(struct rev_info *revs)\n>> +{\n>> +\tif (!revs)\n>> +\t\treturn;\n>> +\tobject_array_clear(&revs->pending);\n>> +}\n>\n> Yay.\n>\n> It is unclear why we want to allow passing NULL to this, though.  Do\n> we even have any code paths that allocate on-heap rev-info?  Address\n> of an on-stack or global rev_info will never be NULL.\n\nI'm almost certain it's boilerplate from early testing, and running the\ntests with it removed just now passed.\n\nI'll make a bit more sure sure it's OK (with SANITIZE=address et al) and\ndrop it in a v3.\n"},{"id":"452195","messageId":"220324.868rszmga6.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"xmqqy210kl3i.fsf@gitster.g","subject":"Re: [PATCH v2 08/27] revisions API users: use release_revisions() needing \"{ 0 }\" init","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-24T17:04:34Z","receivedAt":"2022-03-24T17:06:33Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Wed, Mar 23 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> Use release_revisions() to various users of \"struct rev_list\" which\n>> need to have their \"struct rev_info\" zero-initialized before we can\n>> start using it. In all of these cases we might \"goto cleanup\" (or equivalent),\n>\n> I didn't look at the bisect code, but the bundle one looks iffy from\n> the point of view of API cleanliness.  If we have not yet called\n> repo_init_revisions() on a revs, we should refrain from calling\n> release_revisions() on it in the first place, no?\n\nIt could be avoided, but I'd really prefer not to for this series.\n\nrepo_init_revisions() is a non-trivial function, and changing the\nvarious bits in this series that can easily have a \"goto\" pattern\nbecause we assume that { 0 }-init'd is safe to pass to\nrelease_revisions() would be a larger change...\n\nWe assume that in a lot of other destructors throughout the codebase, I\nfigured we could leave this for later.\n\nIs that OK with you?\n"},{"id":"452199","messageId":"xmqq35j7jlud.fsf@gitster.g","threadId":"57525","inReplyTo":"220324.86pmmbmglo.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v2 01/27] t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-24T17:34:34Z","receivedAt":"2022-03-24T17:34:41Z","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 I'm pretty sure if I left this out I'd have gotten questions about\n> why these seemingly unrelated leak fixes are here as art of series\n> implementing release_revisions(), from either you or someone else :)\n\nThat is an indication that these changes are outside the scope of\nthe series, and should be done as a preliminary and separate series,\nisn't it?\n"},{"id":"452200","messageId":"xmqqwngji72a.fsf@gitster.g","threadId":"57525","inReplyTo":"220324.868rszmga6.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v2 08/27] revisions API users: use release_revisions() needing \"{ 0 }\" init","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-24T17:39:09Z","receivedAt":"2022-03-24T17:39:15Z","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> On Wed, Mar 23 2022, Junio C Hamano wrote:\n>\n>> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>>\n>>> Use release_revisions() to various users of \"struct rev_list\" which\n>>> need to have their \"struct rev_info\" zero-initialized before we can\n>>> start using it. In all of these cases we might \"goto cleanup\" (or equivalent),\n>>\n>> I didn't look at the bisect code, but the bundle one looks iffy from\n>> the point of view of API cleanliness.  If we have not yet called\n>> repo_init_revisions() on a revs, we should refrain from calling\n>> release_revisions() on it in the first place, no?\n>\n> It could be avoided, but I'd really prefer not to for this series.\n>\n> repo_init_revisions() is a non-trivial function, and changing the\n> various bits in this series that can easily have a \"goto\" pattern\n> because we assume that { 0 }-init'd is safe to pass to\n> release_revisions() would be a larger change...\n>\n> We assume that in a lot of other destructors throughout the codebase, I\n> figured we could leave this for later.\n>\n> Is that OK with you?\n\nNot really.  If you introduce \"#define REV_INFO_INIT { 0 }\",\nperhaps, though.\n\nWithout such a mechanism to clearly say \"here is what we initialize\na rev_info\", the first call to repo_init_revisions() looks like the\nplace that initializes a rev_info, and call to release_revisions()\non a rev_info that did not go through repo_init_revisions() looks\nlike a call to free() of a pointer that hasn't been assigned the\nresult from an allocation from the heap.  That is where my \"iffy\nfrom the API cleanliness POV\" comes from.\n\n"},{"id":"452242","messageId":"xmqqr16qeu2s.fsf@gitster.g","threadId":"57525","inReplyTo":"xmqqwngji72a.fsf@gitster.g","subject":"Re: [PATCH v2 08/27] revisions API users: use release_revisions() needing \"{ 0 }\" init","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-25T00:47:55Z","receivedAt":"2022-03-25T00:48:01Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Junio C Hamano <gitster@pobox.com> writes:\n\n> Without such a mechanism to clearly say \"here is what we initialize\n\n\"what\" -> \"where and how\".\n\n> a rev_info\", the first call to repo_init_revisions() looks like the\n> place that initializes a rev_info, and call to release_revisions()\n> on a rev_info that did not go through repo_init_revisions() looks\n> like a call to free() of a pointer that hasn't been assigned the\n> result from an allocation from the heap.  That is where my \"iffy\n> from the API cleanliness POV\" comes from.\n"},{"id":"452244","messageId":"xmqqlewyetwi.fsf@gitster.g","threadId":"57525","inReplyTo":"patch-v2-09.27-88289157533-20220323T203149Z-avarab@gmail.com","subject":"Re: [PATCH v2 09/27] stash: always have the owner of \"stash_info\" free it","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-25T00:51:41Z","receivedAt":"2022-03-25T00:51:48Z","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> Change the initialization of the \"revision\" member of \"struct\n> stash_info\" to be initialized vi a macro, and more importantly that\n\n\"vi a\" \"via a\"\n\n"},{"id":"452245","messageId":"xmqqh77metuf.fsf@gitster.g","threadId":"57525","inReplyTo":"patch-v2-10.27-eafe57663fc-20220323T203149Z-avarab@gmail.com","subject":"Re: [PATCH v2 10/27] revisions API users: add \"goto cleanup\" for release_revisions()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-25T00:52:56Z","receivedAt":"2022-03-25T00:53:02Z","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> Add a release_revisions() to various users of \"struct rev_list\" which\n\n\"rev_list\" -> \"rev_info\", no?\n"},{"id":"452246","messageId":"xmqq5yo2etcq.fsf@gitster.g","threadId":"57525","inReplyTo":"patch-v2-12.27-74818bc372f-20220323T203149Z-avarab@gmail.com","subject":"Re: [PATCH v2 12/27] revisions API users: use release_revisions() in builtin/log.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-25T01:03:33Z","receivedAt":"2022-03-25T01:03:41Z","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> In preparation for having the \"log\" family of functions make wider use\n> of release_revisions() let's have them call it just before\n> exiting. This changes the \"log\", \"whatchanged\", \"show\",\n> \"format-patch\", etc. commands, all of which live in this file.\n>\n> The release_revisions() API still only frees the \"pending\" member, but\n> will learn to more members of \"struct rev_info\" in subsequent commits.\n\nLearn to do what to more members?\n\n> In the case of \"format-patch\" revert the addition of UNLEAK() in\n> dee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16), and\n> which will cause several tests that previously passed under\n> \"TEST_PASSES_SANITIZE_LEAK=true\" to start failing.\n\n\", and which\" -> \", which\".\n\n> diff --git a/builtin/log.c b/builtin/log.c\n> index 6f9928fabfe..c40ebe1c3f4 100644\n> --- a/builtin/log.c\n> +++ b/builtin/log.c\n> @@ -295,6 +295,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,\n>  \tcmd_log_init_finish(argc, argv, prefix, rev, opt);\n>  }\n>  \n> +static int cmd_log_deinit(int ret, struct rev_info *rev)\n> +{\n> +\trelease_revisions(rev);\n> +\treturn ret;\n> +}\n\nUgly but cute.\n\n> @@ -732,8 +738,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n>  \t\t\tret = error(_(\"unknown type: %d\"), o->type);\n>  \t\t}\n>  \t}\n> -\tfree(objects);\n> -\treturn ret;\n> +\treturn cmd_log_deinit(ret, &rev);\n>  }\n\nThis probably makes things worse tentatively for OBJ_COMMIT case,\nwhere we reuse the rev structure to \"(un)walk\" the given commit\nafter clearing rev.pending, but hopefully you'll fix it up in a\nlater step or two?\n"},{"id":"452247","messageId":"xmqq1qyqet5k.fsf@gitster.g","threadId":"57525","inReplyTo":"patch-v2-14.27-38f4f1c4ecb-20220323T203149Z-avarab@gmail.com","subject":"Re: [PATCH v2 14/27] revisions API users: use release_revisions() in submodule.c edge case","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-25T01:07:51Z","receivedAt":"2022-03-25T01:07:59Z","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> diff --git a/submodule.c b/submodule.c\n> index 0510cb193b6..285c1896c9e 100644\n> --- a/submodule.c\n> +++ b/submodule.c\n> @@ -638,7 +638,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,\n>  \t\tstruct object_id *one, struct object_id *two,\n>  \t\tunsigned dirty_submodule)\n>  {\n> -\tstruct rev_info rev;\n> +\tstruct rev_info rev = { 0 };\n\nExactly the same comment as an earlier step about bundle and bisect\napplies here.\n\n>  \tstruct commit *left = NULL, *right = NULL;\n>  \tstruct commit_list *merge_bases = NULL;\n>  \tstruct repository *sub;\n> @@ -664,6 +664,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,\n>  \tprint_submodule_diff_summary(sub, &rev, o);\n>  \n>  out:\n> +\trelease_revisions(&rev);\n>  \tif (merge_bases)\n>  \t\tfree_commit_list(merge_bases);\n>  \tclear_commit_marks(left, ~0);\n"},{"id":"452248","messageId":"xmqqwngideea.fsf@gitster.g","threadId":"57525","inReplyTo":"patch-v2-17.27-a93251edf85-20220323T203149Z-avarab@gmail.com","subject":"Re: [PATCH v2 17/27] revisions API: have release_revisions() release \"mailmap\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-25T01:11:57Z","receivedAt":"2022-03-25T01:12:09Z","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> Extend the the release_revisions() function so that it frees the\n> \"prune_data\" in the \"struct ref_info\".\n\nHuh?  The patch text does make sense but it is all about mailmap as\nfar as I can see.\n\n> diff --git a/builtin/commit.c b/builtin/commit.c\n> index c7eda9bbb72..cd6cebcf8c8 100644\n> --- a/builtin/commit.c\n> +++ b/builtin/commit.c\n> @@ -1100,7 +1100,6 @@ static const char *find_author_by_nickname(const char *name)\n>  \tstruct rev_info revs;\n>  \tstruct commit *commit;\n>  \tstruct strbuf buf = STRBUF_INIT;\n> -\tstruct string_list mailmap = STRING_LIST_INIT_NODUP;\n>  \tconst char *av[20];\n>  \tint ac = 0;\n>  \n> @@ -1111,7 +1110,8 @@ static const char *find_author_by_nickname(const char *name)\n>  \tav[++ac] = buf.buf;\n>  \tav[++ac] = NULL;\n>  \tsetup_revisions(ac, av, &revs, NULL);\n> -\trevs.mailmap = &mailmap;\n> +\trevs.mailmap = xmalloc(sizeof(struct string_list));\n> +\tstring_list_init_nodup(revs.mailmap);\n>  \tread_mailmap(revs.mailmap);\n>  \n>  \tif (prepare_revision_walk(&revs))\n> @@ -1122,7 +1122,6 @@ static const char *find_author_by_nickname(const char *name)\n>  \t\tctx.date_mode.type = DATE_NORMAL;\n>  \t\tstrbuf_release(&buf);\n>  \t\tformat_commit_message(commit, \"%aN <%aE>\", &buf, &ctx);\n> -\t\tclear_mailmap(&mailmap);\n>  \t\trelease_revisions(&revs);\n>  \t\treturn strbuf_detach(&buf, NULL);\n>  \t}\n> diff --git a/revision.c b/revision.c\n> index 13b568aea76..d9e2b171f6d 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -2933,12 +2933,21 @@ static void release_revisions_commit_list(struct rev_info *revs)\n>  \trevs->commits = NULL;\n>  }\n>  \n> +static void release_revisions_mailmap(struct string_list *mailmap)\n> +{\n> +\tif (!mailmap)\n> +\t\treturn;\n> +\tclear_mailmap(mailmap);\n> +\tfree(mailmap);\n> +}\n> +\n>  void release_revisions(struct rev_info *revs)\n>  {\n>  \tif (!revs)\n>  \t\treturn;\n>  \trelease_revisions_commit_list(revs);\n>  \tobject_array_clear(&revs->pending);\n> +\trelease_revisions_mailmap(revs->mailmap);\n>  }\n>  \n>  static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n> diff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh\n> index 2630e756dab..752aa8c9454 100755\n> --- a/t/t0056-git-C.sh\n> +++ b/t/t0056-git-C.sh\n> @@ -2,6 +2,7 @@\n>  \n>  test_description='\"-C <path>\" option and its effects on other path-related options'\n>  \n> +TEST_PASSES_SANITIZE_LEAK=true\n>  . ./test-lib.sh\n>  \n>  test_expect_success '\"git -C <path>\" runs git from the directory <path>' '\n> diff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh\n> index bb5fea02a03..d0c4d38b4d4 100755\n> --- a/t/t3302-notes-index-expensive.sh\n> +++ b/t/t3302-notes-index-expensive.sh\n> @@ -8,6 +8,7 @@ test_description='Test commit notes index (expensive!)'\n>  GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n>  export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>  \n> +TEST_PASSES_SANITIZE_LEAK=true\n>  . ./test-lib.sh\n>  \n>  create_repo () {\n> diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh\n> index 741e0803c1a..73048d0a526 100755\n> --- a/t/t4055-diff-context.sh\n> +++ b/t/t4055-diff-context.sh\n> @@ -5,6 +5,7 @@\n>  \n>  test_description='diff.context configuration'\n>  \n> +TEST_PASSES_SANITIZE_LEAK=true\n>  . ./test-lib.sh\n>  \n>  test_expect_success 'setup' '\n> diff --git a/t/t4066-diff-emit-delay.sh b/t/t4066-diff-emit-delay.sh\n> index a1de63b77f8..0ecb3915412 100755\n> --- a/t/t4066-diff-emit-delay.sh\n> +++ b/t/t4066-diff-emit-delay.sh\n> @@ -4,6 +4,7 @@ test_description='test combined/stat/moved interaction'\n>  GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n>  export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>  \n> +TEST_PASSES_SANITIZE_LEAK=true\n>  . ./test-lib.sh\n>  \n>  # This test covers a weird 3-way interaction between \"--cc -p\", which will run\n> diff --git a/t/t7008-filter-branch-null-sha1.sh b/t/t7008-filter-branch-null-sha1.sh\n> index 9ba9f24ad2f..93fbc92b8db 100755\n> --- a/t/t7008-filter-branch-null-sha1.sh\n> +++ b/t/t7008-filter-branch-null-sha1.sh\n> @@ -1,6 +1,7 @@\n>  #!/bin/sh\n>  \n>  test_description='filter-branch removal of trees with null sha1'\n> +\n>  . ./test-lib.sh\n>  \n>  test_expect_success 'setup: base commits' '\n"},{"id":"452249","messageId":"xmqqsfr6de6j.fsf@gitster.g","threadId":"57525","inReplyTo":"patch-v2-18.27-424fac7ab95-20220323T203149Z-avarab@gmail.com","subject":"Re: [PATCH v2 18/27] revisions API: have release_revisions() release \"cmdline\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-25T01:16:36Z","receivedAt":"2022-03-25T01:16:46Z","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> in-flight semantic conflicts.\n>\n> 1. https://lore.kernel.org/git/YUj%2FgFRh6pwrZalY@carlos-mbp.lan/\n> 2. https://lore.kernel.org/git/87o88obkb1.fsf@evledraar.gmail.com/\n>\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>\n> fixup with bc0e0439040?\n\nWTH is this line?\n\n> diff --git a/revision.c b/revision.c\n> index d9e2b171f6d..f44298299a4 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -2933,6 +2933,17 @@ static void release_revisions_commit_list(struct rev_info *revs)\n>  \trevs->commits = NULL;\n>  }\n>  \n> +static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)\n> +{\n> +\tunsigned int i;\n> +\n> +\tif (!cmdline)\n> +\t\treturn;\n> +\tfor (i = 0; i < cmdline->nr; i++)\n> +\t\tfree((char *)cmdline->rev[i].name);\n> +\tFREE_AND_NULL(cmdline->rev);\n> +}\n> +\n>  static void release_revisions_mailmap(struct string_list *mailmap)\n>  {\n>  \tif (!mailmap)\n> @@ -2947,6 +2958,7 @@ void release_revisions(struct rev_info *revs)\n>  \t\treturn;\n>  \trelease_revisions_commit_list(revs);\n>  \tobject_array_clear(&revs->pending);\n> +\trelease_revisions_cmdline(&revs->cmdline);\n>  \trelease_revisions_mailmap(revs->mailmap);\n>  }\n\nThat patch is quite unexpectedly small ;-)\n\nWhen I created cmdline, I recall liberally stuffing borrowed strings\nin the array, sort-of knowinly making it unnecessarily hard to\n\"free\".\n\nQuite pleasing.\n\n"},{"id":"452250","messageId":"xmqqo81ude48.fsf@gitster.g","threadId":"57525","inReplyTo":"patch-v2-19.27-d5b7a8a690e-20220323T203149Z-avarab@gmail.com","subject":"Re: [PATCH v2 19/27] revisions API: have release_revisions() release \"filter\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-25T01:17:59Z","receivedAt":"2022-03-25T01:18:07Z","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> Extend the the release_revisions() function so that it frees the\n> \"cmdline\" in the \"struct ref_info\". This in combination with a\n> preceding change to free \"cmdline\" means that we can mark another set\n> of tests as passing under \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nRead that again, perhaps twice if needed, and you already know which\ntypo I was about to point out ;-).\n\n\n"},{"id":"452251","messageId":"xmqqk0cide0j.fsf@gitster.g","threadId":"57525","inReplyTo":"patch-v2-21.27-f98d473f08e-20220323T203149Z-avarab@gmail.com","subject":"Re: [PATCH v2 21/27] revisions API: have release_revisions() release \"prune_data\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-25T01:20:12Z","receivedAt":"2022-03-25T01:20:18Z","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> Extend the the release_revisions() function so that it frees the\n> \"prune_data\" in the \"struct ref_info\". This means that any code that\n\nThe same comment as the \"cmdline\" -> \"filter\" in a few patches ago\napplies here, this time \"rev_info\".\n\n\n\n"},{"id":"452252","messageId":"xmqqa6dedd1g.fsf@gitster.g","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"Re: [PATCH v2 00/27] revision.[ch]: add and use release_revisions()","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-25T01:41:15Z","receivedAt":"2022-03-25T01:45:03Z","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> This is a re-roll of the v1 of [1] now that 7391ecd338e (Merge branch\n> 'ds/partial-bundles', 2022-03-21) has landed, which it had a conflict\n> with. I believe this v2 addresses all the feedback brought up on v1.\n\nThanks.  It was mostly a pleasant read but with a huge caveat---I\nhave no confidence in the code that would not try to release what it\ndid not allocate (simply because I do not have time to audit all\nallocations to various members of rev-info structure).  But at least\nif we try to free something we borrowed from say command line, we'd\nimmediately get a crash so with enough cooking and guinea-pig testing,\nsuch a bug would be easy to catch.\n\nI suspect cmd_show() still is leaky when fed a few commits.\n\n    int cmd_show(int argc, const char **argv, const char *prefix)\n    {\n            struct rev_info rev;\n            struct object_array_entry *objects;\n            struct setup_revision_opt opt;\n            struct pathspec match_all;\n            int i, count, ret = 0;\n\n            init_log_defaults();\n            git_config(git_log_config, NULL);\n\n            memset(&match_all, 0, sizeof(match_all));\n            repo_init_revisions(the_repository, &rev, prefix);\n            ...\n            cmd_log_init(argc, argv, prefix, &rev, &opt);\n\n            if (!rev.no_walk)\n                    return cmd_log_deinit(cmd_log_walk(&rev), &rev);\n\nAt this point, we only have positive objects in pending and rev.no_walk\nis set.\n\n            count = rev.pending.nr;\n            objects = rev.pending.objects;\n\nWe grab .objects and we \"show\" each one of them.\n\n            for (i = 0; i < count && !ret; i++) {\n                    struct object *o = objects[i].item;\n                    const char *name = objects[i].name;\n                    switch (o->type) {\n                    ...\n\nThere are ways to show different types of objects, but what we are\ninterested in is what happens to commits.\n\n                    case OBJ_COMMIT:\n                            rev.pending.nr = rev.pending.alloc = 0;\n                            rev.pending.objects = NULL;\n\nWe clear pending.objects because we want to reuse the rev_info we\nalready have here (things like \"git show -U8 master maint\" is harder\nto do if we do not allow the reuse), and push this single commit to\nthe pending array, and let log_walk() machinery to \"show\" it (note\nthat we do not \"walk\", as we have the .no_walk bit set).\n\n                            add_object_array(o, name, &rev.pending);\n                            ret = cmd_log_walk(&rev);\n\nAfter we are done with this \"traversal\" of a single object, we\nprocess another element in the original \"objects\" list, which was\ntaken from the original rev.pending.objects[] array, and we can keep\ngoing, because nobody else has access to objects[] array.\n\nI do not offhand recall what other members of rev_info\ncmd_log_walk() touches, but we are not clearing them.  Not clearing\nrev.pending.objects here means we can be leaking the newly created\none-element object array to hold this single object 'o', UNLESS it\nis the last object in the original objects[] array.\n\n                            break;\n                    default:\n                            ret = error(_(\"unknown type: %d\"), o->type);\n                    }\n            }\n\nAnd even worse, we used to free objects[] here, but we no longer do.\nInstead we let cmd_log_deinit() clear rev.pending.objects[], which\nis the same as objects[] if and only if there are NO commit objects\non the command line.  If there is even one commit object on the\ncommand line, we would have cleared rev.pending.objects and created\na new one, which is what is cleared by the call to cmd_log_deinit().\nThe original one, pointed by the local variable objects[], is no\nlonger freed.\n\n            return cmd_log_deinit(ret, &rev);\n    }\n"},{"id":"452259","messageId":"220325.86zglel7di.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"xmqqa6dedd1g.fsf@gitster.g","subject":"Re: [PATCH v2 00/27] revision.[ch]: add and use release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T09:14:13Z","receivedAt":"2022-03-25T09:16:31Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Mar 24 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> This is a re-roll of the v1 of [1] now that 7391ecd338e (Merge branch\n>> 'ds/partial-bundles', 2022-03-21) has landed, which it had a conflict\n>> with. I believe this v2 addresses all the feedback brought up on v1.\n>\n> Thanks.  It was mostly a pleasant read but with a huge caveat---I\n> have no confidence in the code that would not try to release what it\n> did not allocate (simply because I do not have time to audit all\n> allocations to various members of rev-info structure).  But at least\n> if we try to free something we borrowed from say command line, we'd\n> immediately get a crash so with enough cooking and guinea-pig testing,\n> such a bug would be easy to catch.\n\nYes, especially with SANITIZE=address and friends.\n\n> I suspect cmd_show() still is leaky when fed a few commits.\n> [...]\n> There are ways to show different types of objects, but what we are\n> interested in is what happens to commits.\n>\n>                     case OBJ_COMMIT:\n>                             rev.pending.nr = rev.pending.alloc = 0;\n>                             rev.pending.objects = NULL;\n\nYes, FWIW I know about that one, and will add some passing mention of it\nto a commit message.\n\nThere's still a bunch of leaks related to the revisions.c code after\nthis series, which doesn't address all of them.\n\nBut since it's already getting up to ~30 patches I wanted to address the\nmost common ones, this and various other tricky edge cases have been\nleft for a follow-up.\n"},{"id":"452278","messageId":"patch-v3-21.27-842faebddc4-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 21/27] revisions API: have release_revisions() release \"prune_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:45Z","receivedAt":"2022-03-25T17:28:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"prune_data\" in the \"struct rev_info\". This means that any code that\ncalls \"release_revisions()\" already can get rid of adjacent calls to\nclear_pathspec().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n add-interactive.c | 2 --\n builtin/add.c     | 1 -\n builtin/stash.c   | 2 --\n diff-lib.c        | 1 -\n revision.c        | 1 +\n wt-status.c       | 2 --\n 6 files changed, 1 insertion(+), 8 deletions(-)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex 54cdfc82017..6047e8f6489 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -568,8 +568,6 @@ static int get_modified_files(struct repository *r,\n \t\t\trun_diff_files(&rev, 0);\n \t\t}\n \n-\t\tif (ps)\n-\t\t\tclear_pathspec(&rev.prune_data);\n \t\trelease_revisions(&rev);\n \t}\n \thashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 115a26ea633..fc729e14c17 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -142,7 +142,6 @@ int add_files_to_cache(const char *prefix,\n \trev.diffopt.flags.override_submodule_config = 1;\n \trev.max_count = 0; /* do not compare unmerged paths with stage #2 */\n \trun_diff_files(&rev, DIFF_RACY_IS_MODIFIED);\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n \treturn !!data.add_errors;\n }\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex a6ee030d4bd..5237d0d061a 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1059,7 +1059,6 @@ static int check_changes_tracked_files(const struct pathspec *ps)\n \t}\n \n done:\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n \treturn ret;\n }\n@@ -1271,7 +1270,6 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps\n \n done:\n \tdiscard_index(&istate);\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n \tstrbuf_release(&diff_output);\n \tremove_path(stash_index_path.buf);\ndiff --git a/diff-lib.c b/diff-lib.c\nindex 0f16281253f..298265e5b54 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -641,7 +641,6 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)\n \n \tif (diff_cache(&revs, tree_oid, NULL, 1))\n \t\texit(128);\n-\tclear_pathspec(&revs.prune_data);\n \trelease_revisions(&revs);\n \treturn 0;\n }\ndiff --git a/revision.c b/revision.c\nindex da7e6356e68..6fd582819fe 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2956,6 +2956,7 @@ void release_revisions(struct rev_info *revs)\n \tobject_array_clear(&revs->pending);\n \trelease_revisions_cmdline(&revs->cmdline);\n \tlist_objects_filter_release(&revs->filter);\n+\tclear_pathspec(&revs->prune_data);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n }\ndiff --git a/wt-status.c b/wt-status.c\nindex a14fad1e03a..61e0c1022f5 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -616,7 +616,6 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)\n \trev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;\n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_files(&rev, 0);\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n }\n \n@@ -664,7 +663,6 @@ static void wt_status_collect_changes_index(struct wt_status *s)\n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_index(&rev, 1);\n \trelease_revisions(&rev);\n-\tclear_pathspec(&rev.prune_data);\n }\n \n static int add_file_to_list(const struct object_id *oid,\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452279","messageId":"patch-v3-01.27-7c8298f564f-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 01/27] t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:25Z","receivedAt":"2022-03-25T17:28:45Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak that's been with us since f9500261e0a (fast-rebase:\nwrite conflict state to working tree, index, and HEAD, 2021-05-20)\nchanged this code to move these strbuf_release() into an if/else\nblock.\n\nWe'll also add to \"reflog_msg\" in the \"else\" arm of the \"if\" block\nbeing modified here, and we'll append to \"branch_msg\" in both\ncases. But after f9500261e0a only the \"if\" block would free these two\n\"struct strbuf\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/helper/test-fast-rebase.c | 5 +++--\n 1 file changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c\nindex fc2d4609043..993b90eaedd 100644\n--- a/t/helper/test-fast-rebase.c\n+++ b/t/helper/test-fast-rebase.c\n@@ -201,8 +201,6 @@ int cmd__fast_rebase(int argc, const char **argv)\n \t\t}\n \t\tif (create_symref(\"HEAD\", branch_name.buf, reflog_msg.buf) < 0)\n \t\t\tdie(_(\"unable to update HEAD\"));\n-\t\tstrbuf_release(&reflog_msg);\n-\t\tstrbuf_release(&branch_name);\n \n \t\tprime_cache_tree(the_repository, the_repository->index,\n \t\t\t\t result.tree);\n@@ -221,5 +219,8 @@ int cmd__fast_rebase(int argc, const char **argv)\n \tif (write_locked_index(&the_index, &lock,\n \t\t\t       COMMIT_LOCK | SKIP_IF_UNCHANGED))\n \t\tdie(_(\"unable to write %s\"), get_index_file());\n+\n+\tstrbuf_release(&reflog_msg);\n+\tstrbuf_release(&branch_name);\n \treturn (result.clean == 0);\n }\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452280","messageId":"patch-v3-13.27-2eae0e809e5-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 13/27] revisions API users: use release_revisions() in builtin/log.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:37Z","receivedAt":"2022-03-25T17:28:46Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In preparation for having the \"log\" family of functions make wider use\nof release_revisions() let's have them call it just before\nexiting. This changes the \"log\", \"whatchanged\", \"show\",\n\"format-patch\", etc. commands, all of which live in this file.\n\nThe release_revisions() API still only frees the \"pending\" member, but\nwill learn to release more members of \"struct rev_info\" in subsequent\ncommits.\n\nIn the case of \"format-patch\" revert the addition of UNLEAK() in\ndee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16),\nwhich will cause several tests that previously passed under\n\"TEST_PASSES_SANITIZE_LEAK=true\" to start failing.\n\nIn subsequent commits we'll now be able to use those tests to check\nwhether that part of the API is really leaking memory, and will fix\nall of those memory leaks. Removing the UNLEAK() allows us to make\nincremental progress in that direction. See [1] for further details\nabout this approach.\n\nNote that the release_revisions() will not be sufficient to deal with\nthe code in cmd_show() added in 5d7eeee2ac6 (git-show: grok blobs,\ntrees and tags, too, 2006-12-14) which clobbers the \"pending\" array in\nthe case of \"OBJ_COMMIT\". That will need to be dealt with by some\nfuture follow-up work.\n\n1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/log.c          | 20 ++++++++++++--------\n t/t4126-apply-empty.sh |  2 --\n 2 files changed, 12 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 6f9928fabfe..c40ebe1c3f4 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -295,6 +295,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,\n \tcmd_log_init_finish(argc, argv, prefix, rev, opt);\n }\n \n+static int cmd_log_deinit(int ret, struct rev_info *rev)\n+{\n+\trelease_revisions(rev);\n+\treturn ret;\n+}\n+\n /*\n  * This gives a rough estimate for how many commits we\n  * will print out in the list.\n@@ -558,7 +564,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)\n \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n \tif (!rev.diffopt.output_format)\n \t\trev.diffopt.output_format = DIFF_FORMAT_RAW;\n-\treturn cmd_log_walk(&rev);\n+\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n }\n \n static void show_tagger(const char *buf, struct rev_info *rev)\n@@ -677,7 +683,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n \n \tif (!rev.no_walk)\n-\t\treturn cmd_log_walk(&rev);\n+\t\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n \n \tcount = rev.pending.nr;\n \tobjects = rev.pending.objects;\n@@ -732,8 +738,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \t\t\tret = error(_(\"unknown type: %d\"), o->type);\n \t\t}\n \t}\n-\tfree(objects);\n-\treturn ret;\n+\treturn cmd_log_deinit(ret, &rev);\n }\n \n /*\n@@ -761,7 +766,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)\n \trev.always_show_header = 1;\n \tcmd_log_init_finish(argc, argv, prefix, &rev, &opt);\n \n-\treturn cmd_log_walk(&rev);\n+\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n }\n \n static void log_setup_revisions_tweak(struct rev_info *rev,\n@@ -792,7 +797,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)\n \topt.revarg_opt = REVARG_COMMITTISH;\n \topt.tweak = log_setup_revisions_tweak;\n \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n-\treturn cmd_log_walk(&rev);\n+\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n }\n \n /* format-patch */\n@@ -2289,8 +2294,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tif (rev.ref_message_ids)\n \t\tstring_list_clear(rev.ref_message_ids, 0);\n \tfree(rev.ref_message_ids);\n-\tUNLEAK(rev);\n-\treturn 0;\n+\treturn cmd_log_deinit(0, &rev);\n }\n \n static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)\ndiff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh\nindex 33860d38290..66a7ba8ab8f 100755\n--- a/t/t4126-apply-empty.sh\n+++ b/t/t4126-apply-empty.sh\n@@ -2,8 +2,6 @@\n \n test_description='apply empty'\n \n-\n-TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452281","messageId":"patch-v3-09.27-5f5c0d26395-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 09/27] revisions API users: use release_revisions() in submodule.c edge case","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:33Z","receivedAt":"2022-03-25T17:28:54Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use release_revisions() on the the \"struct rev_info\" in\nshow_submodule_diff_summary() where we'd otherwise need to do the\nequivalent of pre-initializing the \"struct rev_info\" with \"{ 0 }\" if\nwe were going to add it to the cleanup being performed in the \"out\"\npart of the function, let's instead introduce an \"out_no_rev\" for the\nreasons discussed in the preceding commit.\n\nDoing so for the \"goto\" on \"(!left || !right || !sub)\" added in\n8e6df65015f (submodule: refactor show_submodule_summary with helper\nfunction, 2016-08-31) would have been straightforward, as in the\npreceding commit.\n\nBut for the case of prepare_submodule_diff_summary() failing it's less\nstraightforward. Reading the pre-image we could simply retain the\n\"goto out\" if it fails, because we can see that the first thing it\ndoes is call repo_init_revisions().\n\nBut having a hard reliance on that would be a bit nasty, as we'd\npotentially introduce a segfault if the function did some other\ninitialization first, and early aborted if that failed.\n\nLet's just fold that helper function away into\nshow_submodule_diff_summary() itself, which was its only user. Now\nfollowing the flow of initialization is more obvious, and it's\nimmediately clear that the \"goto out\" if prepare_revision_walk()\nreturns non-zero is safe.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n submodule.c | 41 +++++++++++++++++------------------------\n 1 file changed, 17 insertions(+), 24 deletions(-)\n\ndiff --git a/submodule.c b/submodule.c\nindex 0510cb193b6..1dd61476307 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -477,28 +477,6 @@ void handle_ignore_submodules_arg(struct diff_options *diffopt,\n \t */\n }\n \n-static int prepare_submodule_diff_summary(struct repository *r, struct rev_info *rev,\n-\t\t\t\t\t  const char *path,\n-\t\t\t\t\t  struct commit *left, struct commit *right,\n-\t\t\t\t\t  struct commit_list *merge_bases)\n-{\n-\tstruct commit_list *list;\n-\n-\trepo_init_revisions(r, rev, NULL);\n-\tsetup_revisions(0, NULL, rev, NULL);\n-\trev->left_right = 1;\n-\trev->first_parent_only = 1;\n-\tleft->object.flags |= SYMMETRIC_LEFT;\n-\tadd_pending_object(rev, &left->object, path);\n-\tadd_pending_object(rev, &right->object, path);\n-\tfor (list = merge_bases; list; list = list->next) {\n-\t\tlist->item->object.flags |= UNINTERESTING;\n-\t\tadd_pending_object(rev, &list->item->object,\n-\t\t\toid_to_hex(&list->item->object.oid));\n-\t}\n-\treturn prepare_revision_walk(rev);\n-}\n-\n static void print_submodule_diff_summary(struct repository *r, struct rev_info *rev, struct diff_options *o)\n {\n \tstatic const char format[] = \"  %m %s\";\n@@ -642,6 +620,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,\n \tstruct commit *left = NULL, *right = NULL;\n \tstruct commit_list *merge_bases = NULL;\n \tstruct repository *sub;\n+\tstruct commit_list *list;\n \n \tsub = open_submodule(path);\n \tshow_submodule_header(o, path, one, two, dirty_submodule,\n@@ -653,10 +632,22 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,\n \t * all the information the user needs.\n \t */\n \tif (!left || !right || !sub)\n-\t\tgoto out;\n+\t\tgoto out_no_rev;\n \n+\trepo_init_revisions(sub, &rev, NULL);\n+\tsetup_revisions(0, NULL, &rev, NULL);\n+\trev.left_right = 1;\n+\trev.first_parent_only = 1;\n+\tleft->object.flags |= SYMMETRIC_LEFT;\n+\tadd_pending_object(&rev, &left->object, path);\n+\tadd_pending_object(&rev, &right->object, path);\n+\tfor (list = merge_bases; list; list = list->next) {\n+\t\tlist->item->object.flags |= UNINTERESTING;\n+\t\tadd_pending_object(&rev, &list->item->object,\n+\t\t\toid_to_hex(&list->item->object.oid));\n+\t}\n \t/* Treat revision walker failure the same as missing commits */\n-\tif (prepare_submodule_diff_summary(sub, &rev, path, left, right, merge_bases)) {\n+\tif (prepare_revision_walk(&rev)) {\n \t\tdiff_emit_submodule_error(o, \"(revision walker failed)\\n\");\n \t\tgoto out;\n \t}\n@@ -664,6 +655,8 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,\n \tprint_submodule_diff_summary(sub, &rev, o);\n \n out:\n+\trelease_revisions(&rev);\n+out_no_rev:\n \tif (merge_bases)\n \t\tfree_commit_list(merge_bases);\n \tclear_commit_marks(left, ~0);\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452282","messageId":"patch-v3-26.27-5efc0ae58ed-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 26/27] revisions API: have release_revisions() release \"topo_walk_info\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:50Z","receivedAt":"2022-03-25T17:29:00Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor the existing reset_topo_walk() into a thin wrapper for a\nrelease_revisions_topo_walk_info() + resetting the member to \"NULL\",\nand call release_revisions_topo_walk_info() from release_revisions().\n\nThis fixes memory leaks that have been with us ever since\n\"topo_walk_info\" was added to revision.[ch] in\nf0d9cc4196a (revision.c: begin refactoring --topo-order logic,\n2018-11-01).\n\nDue to various other leaks this makes no tests pass in their entirety,\nbut e.g. before this running this on git.git:\n\n    ./git -P log --pretty=tformat:\"%P   %H | %s\" --parents --full-history --topo-order -3 -- README.md\n\nWould report under SANITIZE=leak:\n\n    SUMMARY: LeakSanitizer: 531064 byte(s) leaked in 6 allocation(s).\n\nNow we'll free all of that memory.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 16 ++++++++++++----\n 1 file changed, 12 insertions(+), 4 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex 49668012ed1..cff1c2c6021 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2950,6 +2950,8 @@ static void release_revisions_mailmap(struct string_list *mailmap)\n \tfree(mailmap);\n }\n \n+static void release_revisions_topo_walk_info(struct topo_walk_info *info);\n+\n void release_revisions(struct rev_info *revs)\n {\n \trelease_revisions_commit_list(revs);\n@@ -2963,6 +2965,7 @@ void release_revisions(struct rev_info *revs)\n \tfree_grep_patterns(&revs->grep_filter);\n \tdiff_free(&revs->pruning);\n \treflog_walk_info_release(revs->reflog_info);\n+\trelease_revisions_topo_walk_info(revs->topo_walk_info);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n@@ -3475,17 +3478,22 @@ static void compute_indegrees_to_depth(struct rev_info *revs,\n \t\tindegree_walk_step(revs);\n }\n \n-static void reset_topo_walk(struct rev_info *revs)\n+static void release_revisions_topo_walk_info(struct topo_walk_info *info)\n {\n-\tstruct topo_walk_info *info = revs->topo_walk_info;\n-\n+\tif (!info)\n+\t\treturn;\n \tclear_prio_queue(&info->explore_queue);\n \tclear_prio_queue(&info->indegree_queue);\n \tclear_prio_queue(&info->topo_queue);\n \tclear_indegree_slab(&info->indegree);\n \tclear_author_date_slab(&info->author_date);\n+\tfree(info);\n+}\n \n-\tFREE_AND_NULL(revs->topo_walk_info);\n+static void reset_topo_walk(struct rev_info *revs)\n+{\n+\trelease_revisions_topo_walk_info(revs->topo_walk_info);\n+\trevs->topo_walk_info = NULL;\n }\n \n static void init_topo_walk(struct rev_info *revs)\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452283","messageId":"patch-v3-23.27-af874c17fbd-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 23/27] revisions API: release \"reflog_info\" in release revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:47Z","receivedAt":"2022-03-25T17:29:03Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a missing reflog_walk_info_release() to \"reflog-walk.c\" and use it\nin release_revisions().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n reflog-walk.c            | 24 +++++++++++++++++++++++-\n reflog-walk.h            |  1 +\n revision.c               |  1 +\n t/t0100-previous.sh      |  1 +\n t/t1401-symbolic-ref.sh  |  2 ++\n t/t1411-reflog-show.sh   |  1 +\n t/t1412-reflog-loop.sh   |  2 ++\n t/t1415-worktree-refs.sh |  1 +\n 8 files changed, 32 insertions(+), 1 deletion(-)\n\ndiff --git a/reflog-walk.c b/reflog-walk.c\nindex 8ac4b284b6b..7aa6595a51f 100644\n--- a/reflog-walk.c\n+++ b/reflog-walk.c\n@@ -8,7 +8,7 @@\n \n struct complete_reflogs {\n \tchar *ref;\n-\tconst char *short_ref;\n+\tchar *short_ref;\n \tstruct reflog_info {\n \t\tstruct object_id ooid, noid;\n \t\tchar *email;\n@@ -51,9 +51,16 @@ static void free_complete_reflog(struct complete_reflogs *array)\n \t}\n \tfree(array->items);\n \tfree(array->ref);\n+\tfree(array->short_ref);\n \tfree(array);\n }\n \n+static void complete_reflogs_clear(void *util, const char *str)\n+{\n+\tstruct complete_reflogs *array = util;\n+\tfree_complete_reflog(array);\n+}\n+\n static struct complete_reflogs *read_complete_reflog(const char *ref)\n {\n \tstruct complete_reflogs *reflogs =\n@@ -116,6 +123,21 @@ void init_reflog_walk(struct reflog_walk_info **info)\n \t(*info)->complete_reflogs.strdup_strings = 1;\n }\n \n+void reflog_walk_info_release(struct reflog_walk_info *info)\n+{\n+\tsize_t i;\n+\n+\tif (!info)\n+\t\treturn;\n+\n+\tfor (i = 0; i < info->nr; i++)\n+\t\tfree(info->logs[i]);\n+\tstring_list_clear_func(&info->complete_reflogs,\n+\t\t\t       complete_reflogs_clear);\n+\tfree(info->logs);\n+\tfree(info);\n+}\n+\n int add_reflog_for_walk(struct reflog_walk_info *info,\n \t\tstruct commit *commit, const char *name)\n {\ndiff --git a/reflog-walk.h b/reflog-walk.h\nindex e9e00ffd479..8076f10d9fb 100644\n--- a/reflog-walk.h\n+++ b/reflog-walk.h\n@@ -8,6 +8,7 @@ struct reflog_walk_info;\n struct date_mode;\n \n void init_reflog_walk(struct reflog_walk_info **info);\n+void reflog_walk_info_release(struct reflog_walk_info *info);\n int add_reflog_for_walk(struct reflog_walk_info *info,\n \t\t\tstruct commit *commit, const char *name);\n void show_reflog_message(struct reflog_walk_info *info, int,\ndiff --git a/revision.c b/revision.c\nindex 53b3ab6dc2d..380e6d9fe43 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2960,6 +2960,7 @@ void release_revisions(struct rev_info *revs)\n \tclear_pathspec(&revs->prune_data);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n+\treflog_walk_info_release(revs->reflog_info);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\ndiff --git a/t/t0100-previous.sh b/t/t0100-previous.sh\nindex 69beb59f627..a16cc3d2983 100755\n--- a/t/t0100-previous.sh\n+++ b/t/t0100-previous.sh\n@@ -5,6 +5,7 @@ test_description='previous branch syntax @{-n}'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'branch -d @{-1}' '\ndiff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh\nindex 132a1b885ac..9fb0b90f252 100755\n--- a/t/t1401-symbolic-ref.sh\n+++ b/t/t1401-symbolic-ref.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='basic symbolic-ref tests'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # If the tests munging HEAD fail, they can break detection of\ndiff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh\nindex 0bb319b944a..3770ceffafd 100755\n--- a/t/t1411-reflog-show.sh\n+++ b/t/t1411-reflog-show.sh\n@@ -4,6 +4,7 @@ test_description='Test reflog display routines'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t1412-reflog-loop.sh b/t/t1412-reflog-loop.sh\nindex 977603f7f1c..ff30874f940 100755\n--- a/t/t1412-reflog-loop.sh\n+++ b/t/t1412-reflog-loop.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='reflog walk shows repeated commits again'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup commits' '\ndiff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh\nindex a3e6ea08088..3b531842dd4 100755\n--- a/t/t1415-worktree-refs.sh\n+++ b/t/t1415-worktree-refs.sh\n@@ -2,6 +2,7 @@\n \n test_description='per-worktree refs'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452284","messageId":"patch-v3-08.27-f8a9443fe6f-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 08/27] revisions API users: add \"goto cleanup\" for \"rev_info\" early exit","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:32Z","receivedAt":"2022-03-25T17:29:05Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add release_revisions() in various users of \"struct rev_info\" that can\nmostly use a \"goto cleanup\" pattern, but also have an early \"return\"\nbefore we've called repo_init_revisions(). We need to avoid calling\nrelease_revisions() with uninitialized memory.\n\nIt would be a lot cleaner to be able to initialize \"struct rev_info\"\nwith \"{ 0 }\" here, or if a \"REV_INFO_INIT\" existed, we'll hopefully\nget around to making the initialization easier in the future (now it\ncan't be done via a macro).\n\nUntil then let's leave a \"cleanup_no_rev[s]\" in place to document the\nintention here. Only status_submodule() in builtin/submodule--helper.c\nstrictly speaking needs this, the other ones could keep their \"return\"\nfor the early exit. But let's have them also use the \"goto\ncleanup[...]\" for consistency, and for the eventual migration to\nsimpler initialization.\n\nFor the bundle.c code see the early exit case added in\n3bbbe467f29 (bundle verify: error out if called without an object\ndatabase, 2019-05-27).\n\nFor the relevant bisect.c code see 45b6370812c (bisect: libify\n`check_good_are_ancestors_of_bad` and its dependents, 2020-02-17).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n bisect.c                    | 17 ++++++++++++-----\n builtin/submodule--helper.c |  6 ++++--\n bundle.c                    | 12 +++++++++---\n 3 files changed, 25 insertions(+), 10 deletions(-)\n\ndiff --git a/bisect.c b/bisect.c\nindex cc6b8b6230d..c2e9f6ca9f6 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -1035,7 +1035,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \n \tres = check_good_are_ancestors_of_bad(r, prefix, no_checkout);\n \tif (res)\n-\t\treturn res;\n+\t\tgoto cleanup_no_revs;\n \n \tbisect_rev_setup(r, &revs, prefix, \"%s\", \"^%s\", 1);\n \n@@ -1060,14 +1060,16 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \t\t       term_good,\n \t\t       term_bad);\n \n-\t\treturn BISECT_FAILED;\n+\t\tres = BISECT_FAILED;\n+\t\tgoto cleanup;\n \t}\n \n \tif (!all) {\n \t\tfprintf(stderr, _(\"No testable commit found.\\n\"\n \t\t\t\"Maybe you started with bad path arguments?\\n\"));\n \n-\t\treturn BISECT_NO_TESTABLE_COMMIT;\n+\t\tres = BISECT_NO_TESTABLE_COMMIT;\n+\t\tgoto cleanup;\n \t}\n \n \tbisect_rev = &revs.commits->item->object.oid;\n@@ -1087,7 +1089,8 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \t\t * for negative return values for early returns up\n \t\t * until the cmd_bisect__helper() caller.\n \t\t */\n-\t\treturn BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;\n+\t\tres = BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;\n+\t\tgoto cleanup;\n \t}\n \n \tnr = all - reaches - 1;\n@@ -1106,7 +1109,11 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \t/* Clean up objects used, as they will be reused. */\n \trepo_clear_commit_marks(r, ALL_REV_FLAGS);\n \n-\treturn bisect_checkout(bisect_rev, no_checkout);\n+\tres = bisect_checkout(bisect_rev, no_checkout);\n+cleanup:\n+\trelease_revisions(&revs);\n+cleanup_no_revs:\n+\treturn res;\n }\n \n static inline int log2i(int n)\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 24980863f68..d1b656c0909 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -779,7 +779,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,\n \n \tif ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) {\n \t\tprint_status(flags, 'U', path, null_oid(), displaypath);\n-\t\tgoto cleanup;\n+\t\tgoto cleanup_no_rev;\n \t}\n \n \tstrbuf_addf(&buf, \"%s/.git\", path);\n@@ -791,7 +791,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,\n \t    !is_git_directory(git_dir)) {\n \t\tprint_status(flags, '-', path, ce_oid, displaypath);\n \t\tstrbuf_release(&buf);\n-\t\tgoto cleanup;\n+\t\tgoto cleanup_no_rev;\n \t}\n \tstrbuf_release(&buf);\n \n@@ -851,6 +851,8 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,\n \t}\n \n cleanup:\n+\trelease_revisions(&rev);\n+cleanup_no_rev:\n \tstrvec_clear(&diff_files_args);\n \tfree(displaypath);\n }\ndiff --git a/bundle.c b/bundle.c\nindex e359370cfcd..9b7c0bc55c4 100644\n--- a/bundle.c\n+++ b/bundle.c\n@@ -202,8 +202,10 @@ int verify_bundle(struct repository *r,\n \tint i, ret = 0, req_nr;\n \tconst char *message = _(\"Repository lacks these prerequisite commits:\");\n \n-\tif (!r || !r->objects || !r->objects->odb)\n-\t\treturn error(_(\"need a repository to verify a bundle\"));\n+\tif (!r || !r->objects || !r->objects->odb) {\n+\t\tret = error(_(\"need a repository to verify a bundle\"));\n+\t\tgoto cleanup_no_revs;\n+\t}\n \n \trepo_init_revisions(r, &revs, NULL);\n \tfor (i = 0; i < p->nr; i++) {\n@@ -221,7 +223,7 @@ int verify_bundle(struct repository *r,\n \t\terror(\"%s %s\", oid_to_hex(oid), name);\n \t}\n \tif (revs.pending.nr != p->nr)\n-\t\treturn ret;\n+\t\tgoto cleanup;\n \treq_nr = revs.pending.nr;\n \tsetup_revisions(2, argv, &revs, NULL);\n \n@@ -283,6 +285,10 @@ int verify_bundle(struct repository *r,\n \t\t\tlist_refs(r, 0, NULL);\n \t\t}\n \t}\n+\n+cleanup:\n+\trelease_revisions(&revs);\n+cleanup_no_revs:\n \treturn ret;\n }\n \n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452285","messageId":"patch-v3-25.27-0eacbd66711-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 25/27] revisions API: have release_revisions() release \"date_mode\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:49Z","receivedAt":"2022-03-25T17:29:07Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"date_mode\" in the \"struct ref_info\".\n\nThis uses the date_mode_release() function added in 974c919d36d (date\nAPI: add and use a date_mode_release(), 2022-02-16). As that commit\nnotes \"t7004-tag.sh\" tests for the leaks that are being fixed\nhere. That test now fails \"only\" 44 tests, instead of the 46 it failed\nbefore this change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/revision.c b/revision.c\nindex 9ac06e4d285..49668012ed1 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2958,6 +2958,7 @@ void release_revisions(struct rev_info *revs)\n \trelease_revisions_cmdline(&revs->cmdline);\n \tlist_objects_filter_release(&revs->filter);\n \tclear_pathspec(&revs->prune_data);\n+\tdate_mode_release(&revs->date_mode);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n \tdiff_free(&revs->pruning);\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452286","messageId":"patch-v3-14.27-216eb3b41f7-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 14/27] revisions API users: use release_revisions() with UNLEAK()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:38Z","receivedAt":"2022-03-25T17:29:09Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use a release_revisions() with those \"struct rev_list\" users which\nalready \"UNLEAK\" the struct. It may seem odd to simultaneously attempt\nto free() memory, but also to explicitly ignore whether we have memory\nleaks in the same.\n\nAs explained in preceding commits this is being done to use the\nbuilt-in commands as a guinea pig for whether the release_revisions()\nfunction works as expected, we'd like to test e.g. whether we segfault\nas we change it. In subsequent commits we'll then remove these\nUNLEAK() as the function is made to free the memory that caused us to\nadd them in the first place.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/diff-index.c | 4 +++-\n builtin/diff.c       | 1 +\n 2 files changed, 4 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/diff-index.c b/builtin/diff-index.c\nindex 5fd23ab5b6c..3a83183c312 100644\n--- a/builtin/diff-index.c\n+++ b/builtin/diff-index.c\n@@ -71,5 +71,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)\n \t}\n \tresult = run_diff_index(&rev, option);\n \tUNLEAK(rev);\n-\treturn diff_result_code(&rev.diffopt, result);\n+\tresult = diff_result_code(&rev.diffopt, result);\n+\trelease_revisions(&rev);\n+\treturn result;\n }\ndiff --git a/builtin/diff.c b/builtin/diff.c\nindex bb7fafca618..dd48336da56 100644\n--- a/builtin/diff.c\n+++ b/builtin/diff.c\n@@ -595,6 +595,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \tif (1 < rev.diffopt.skip_stat_unmatch)\n \t\trefresh_index_quietly();\n \tUNLEAK(rev);\n+\trelease_revisions(&rev);\n \tUNLEAK(ent);\n \tUNLEAK(blob);\n \treturn result;\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452288","messageId":"patch-v3-20.27-26163e5e754-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 20/27] revisions API: have release_revisions() release \"grep_filter\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:44Z","receivedAt":"2022-03-25T17:29:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"grep_filter\" in the \"struct rev_info\".This allows us to mark a test\nas passing under \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c               | 1 +\n t/t9151-svn-mergeinfo.sh | 1 -\n 2 files changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/revision.c b/revision.c\nindex 55f2f4291f2..da7e6356e68 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2957,6 +2957,7 @@ void release_revisions(struct rev_info *revs)\n \trelease_revisions_cmdline(&revs->cmdline);\n \tlist_objects_filter_release(&revs->filter);\n \trelease_revisions_mailmap(revs->mailmap);\n+\tfree_grep_patterns(&revs->grep_filter);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\ndiff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh\nindex 85221d439bd..c93a5beab25 100755\n--- a/t/t9151-svn-mergeinfo.sh\n+++ b/t/t9151-svn-mergeinfo.sh\n@@ -5,7 +5,6 @@\n \n test_description='git-svn svn mergeinfo properties'\n \n-TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'load svn dump' \"\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452289","messageId":"patch-v3-07.27-c4fc1c98e7b-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 07/27] revisions API users: add straightforward release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:31Z","receivedAt":"2022-03-25T17:29:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a release_revisions() to various users of \"struct rev_list\" in\nthose straightforward cases where we only need to add the\nrelease_revisions() call to the end of a block, and don't need to\ne.g. refactor anything to use a \"goto cleanup\" pattern.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n add-interactive.c                | 1 +\n bisect.c                         | 2 ++\n builtin/add.c                    | 1 +\n builtin/am.c                     | 3 +++\n builtin/bisect--helper.c         | 2 ++\n builtin/blame.c                  | 1 +\n builtin/checkout.c               | 1 +\n builtin/commit.c                 | 1 +\n builtin/describe.c               | 2 ++\n builtin/fast-export.c            | 1 +\n builtin/merge.c                  | 2 ++\n builtin/pack-objects.c           | 2 ++\n builtin/prune.c                  | 1 +\n builtin/reflog.c                 | 1 +\n builtin/shortlog.c               | 2 ++\n builtin/submodule--helper.c      | 1 +\n fmt-merge-msg.c                  | 1 +\n merge-ort.c                      | 1 +\n merge-recursive.c                | 1 +\n midx.c                           | 1 +\n pack-bitmap-write.c              | 1 +\n ref-filter.c                     | 1 +\n remote.c                         | 1 +\n sequencer.c                      | 3 +++\n shallow.c                        | 1 +\n submodule.c                      | 2 ++\n t/helper/test-revision-walking.c | 1 +\n wt-status.c                      | 1 +\n 28 files changed, 39 insertions(+)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex 72472103017..54cdfc82017 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -570,6 +570,7 @@ static int get_modified_files(struct repository *r,\n \n \t\tif (ps)\n \t\t\tclear_pathspec(&rev.prune_data);\n+\t\trelease_revisions(&rev);\n \t}\n \thashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);\n \tif (unmerged_count)\ndiff --git a/bisect.c b/bisect.c\nindex 9e6a2b7f201..cc6b8b6230d 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -884,6 +884,7 @@ static int check_ancestors(struct repository *r, int rev_nr,\n \t/* Clean up objects used, as they will be reused. */\n \tclear_commit_marks_many(rev_nr, rev, ALL_REV_FLAGS);\n \n+\trelease_revisions(&revs);\n \treturn res;\n }\n \n@@ -964,6 +965,7 @@ static void show_diff_tree(struct repository *r,\n \n \tsetup_revisions(ARRAY_SIZE(argv) - 1, argv, &opt, NULL);\n \tlog_tree_commit(&opt, commit);\n+\trelease_revisions(&opt);\n }\n \n /*\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 3ffb86a4338..f507d2191cd 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -340,6 +340,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)\n \n \tunlink(file);\n \tfree(file);\n+\trelease_revisions(&rev);\n \treturn 0;\n }\n \ndiff --git a/builtin/am.c b/builtin/am.c\nindex 0f4111bafa0..93bec62afa9 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -1397,6 +1397,7 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm\n \tadd_pending_object(&rev_info, &commit->object, \"\");\n \tdiff_setup_done(&rev_info.diffopt);\n \tlog_tree_commit(&rev_info, commit);\n+\trelease_revisions(&rev_info);\n }\n \n /**\n@@ -1429,6 +1430,7 @@ static void write_index_patch(const struct am_state *state)\n \tadd_pending_object(&rev_info, &tree->object, \"\");\n \tdiff_setup_done(&rev_info.diffopt);\n \trun_diff_index(&rev_info, 1);\n+\trelease_revisions(&rev_info);\n }\n \n /**\n@@ -1582,6 +1584,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \t\tadd_pending_oid(&rev_info, \"HEAD\", &our_tree, 0);\n \t\tdiff_setup_done(&rev_info.diffopt);\n \t\trun_diff_index(&rev_info, 1);\n+\t\trelease_revisions(&rev_info);\n \t}\n \n \tif (run_apply(state, index_path))\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex 8b2b259ff0d..e4d7b6779ae 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -555,6 +555,7 @@ static int bisect_skipped_commits(struct bisect_terms *terms)\n \treset_revision_walk();\n \n \tstrbuf_release(&commit_name);\n+\trelease_revisions(&revs);\n \tfclose(fp);\n \treturn 0;\n }\n@@ -1041,6 +1042,7 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar\n \t\t\t\t\t\toid_to_hex(&commit->object.oid));\n \n \t\t\treset_revision_walk();\n+\t\t\trelease_revisions(&revs);\n \t\t} else {\n \t\t\tstrvec_push(&argv_state, argv[i]);\n \t\t}\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 885b381ab83..24bac822c56 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1203,5 +1203,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \n cleanup:\n \tcleanup_scoreboard(&sb);\n+\trelease_revisions(&revs);\n \treturn 0;\n }\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 4d9e0bd3ac1..7ad4a7113c9 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1082,6 +1082,7 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne\n \n \t/* Clean up objects used, as they will be reused. */\n \trepo_clear_commit_marks(the_repository, ALL_REV_FLAGS);\n+\trelease_revisions(&revs);\n }\n \n static int switch_branches(const struct checkout_opts *opts,\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 009a1de0a3d..c7eda9bbb72 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1123,6 +1123,7 @@ static const char *find_author_by_nickname(const char *name)\n \t\tstrbuf_release(&buf);\n \t\tformat_commit_message(commit, \"%aN <%aE>\", &buf, &ctx);\n \t\tclear_mailmap(&mailmap);\n+\t\trelease_revisions(&revs);\n \t\treturn strbuf_detach(&buf, NULL);\n \t}\n \tdie(_(\"--author '%s' is not 'Name <email>' and matches no existing author\"), name);\ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex 42159cd26bd..a76f1a1a7a7 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -517,6 +517,7 @@ static void describe_blob(struct object_id oid, struct strbuf *dst)\n \n \ttraverse_commit_list(&revs, process_commit, process_object, &pcd);\n \treset_revision_walk();\n+\trelease_revisions(&revs);\n }\n \n static void describe(const char *arg, int last_one)\n@@ -667,6 +668,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)\n \t\t\t\tsuffix = NULL;\n \t\t\telse\n \t\t\t\tsuffix = dirty;\n+\t\t\trelease_revisions(&revs);\n \t\t}\n \t\tdescribe(\"HEAD\", 1);\n \t} else if (dirty) {\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex a7d72697fba..f34ae451ee3 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -1275,6 +1275,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)\n \t\tprintf(\"done\\n\");\n \n \trefspec_clear(&refspecs);\n+\trelease_revisions(&revs);\n \n \treturn 0;\n }\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex f178f5a3ee1..d9784d4891c 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -443,6 +443,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead\n \t}\n \twrite_file_buf(git_path_squash_msg(the_repository), out.buf, out.len);\n \tstrbuf_release(&out);\n+\trelease_revisions(&rev);\n }\n \n static void finish(struct commit *head_commit,\n@@ -998,6 +999,7 @@ static int evaluate_result(void)\n \t */\n \tcnt += count_unmerged_entries();\n \n+\trelease_revisions(&rev);\n \treturn cnt;\n }\n \ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 829ca359cf9..0aa194f68dc 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -3384,6 +3384,7 @@ static void read_packs_list_from_stdin(void)\n \tstrbuf_release(&buf);\n \tstring_list_clear(&include_packs, 0);\n \tstring_list_clear(&exclude_packs, 0);\n+\trelease_revisions(&revs);\n }\n \n static void read_object_list_from_stdin(void)\n@@ -3800,6 +3801,7 @@ static void get_object_list(int ac, const char **av)\n \tif (unpack_unreachable)\n \t\tloosen_unused_packed_objects();\n \n+\trelease_revisions(&revs);\n \toid_array_clear(&recent_objects);\n }\n \ndiff --git a/builtin/prune.c b/builtin/prune.c\nindex c2bcdc07db4..df376b2ed1e 100644\n--- a/builtin/prune.c\n+++ b/builtin/prune.c\n@@ -196,5 +196,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix)\n \t\tprune_shallow(show_only ? PRUNE_SHOW_ONLY : 0);\n \t}\n \n+\trelease_revisions(&revs);\n \treturn 0;\n }\ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex 9407f835cb6..592d5d33442 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -248,6 +248,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)\n \t\tif (verbose)\n \t\t\tprintf(_(\"Marking reachable objects...\"));\n \t\tmark_reachable_objects(&revs, 0, 0, NULL);\n+\t\trelease_revisions(&revs);\n \t\tif (verbose)\n \t\t\tputchar('\\n');\n \t}\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex fcde07c9367..35825f075e3 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -422,6 +422,8 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)\n \telse\n \t\tget_from_rev(&rev, &log);\n \n+\trelease_revisions(&rev);\n+\n \tshortlog_output(&log);\n \tif (log.file != stdout)\n \t\tfclose(log.file);\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 5301612d24b..24980863f68 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1269,6 +1269,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \t\trun_diff_files(&rev, 0);\n \tprepare_submodule_summary(info, &list);\n \tstrvec_clear(&diff_args);\n+\trelease_revisions(&rev);\n \treturn 0;\n }\n \ndiff --git a/fmt-merge-msg.c b/fmt-merge-msg.c\nindex baca57d5b64..f48f44f9cd1 100644\n--- a/fmt-merge-msg.c\n+++ b/fmt-merge-msg.c\n@@ -699,6 +699,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,\n \t\t\tshortlog(origins.items[i].string,\n \t\t\t\t origins.items[i].util,\n \t\t\t\t head, &rev, opts, out);\n+\t\trelease_revisions(&rev);\n \t}\n \n \tstrbuf_complete_line(out);\ndiff --git a/merge-ort.c b/merge-ort.c\nindex 8545354dafd..4c0d4eba19b 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -1594,6 +1594,7 @@ static int find_first_merges(struct repository *repo,\n \t}\n \n \tobject_array_clear(&merges);\n+\trelease_revisions(&revs);\n \treturn result->nr;\n }\n \ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 32bbba5fbb1..acd13b2b069 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1160,6 +1160,7 @@ static int find_first_merges(struct repository *repo,\n \t}\n \n \tobject_array_clear(&merges);\n+\trelease_revisions(&revs);\n \treturn result->nr;\n }\n \ndiff --git a/midx.c b/midx.c\nindex 865170bad05..702c8a9b178 100644\n--- a/midx.c\n+++ b/midx.c\n@@ -1061,6 +1061,7 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr\n \tif (indexed_commits_nr_p)\n \t\t*indexed_commits_nr_p = cb.commits_nr;\n \n+\trelease_revisions(&revs);\n \treturn cb.commits;\n }\n \ndiff --git a/pack-bitmap-write.c b/pack-bitmap-write.c\nindex cab3eaa2acd..ea8e0b51cdf 100644\n--- a/pack-bitmap-write.c\n+++ b/pack-bitmap-write.c\n@@ -326,6 +326,7 @@ static void bitmap_builder_init(struct bitmap_builder *bb,\n \ttrace2_data_intmax(\"pack-bitmap-write\", the_repository,\n \t\t\t   \"num_maximal_commits\", num_maximal);\n \n+\trelease_revisions(&revs);\n \tfree_commit_list(reusable);\n }\n \ndiff --git a/ref-filter.c b/ref-filter.c\nindex 7838bd22b8d..a91688bbf17 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -2392,6 +2392,7 @@ static void reach_filter(struct ref_array *array,\n \t\tclear_commit_marks(merge_commit, ALL_REV_FLAGS);\n \t}\n \n+\trelease_revisions(&revs);\n \tfree(to_clear);\n }\n \ndiff --git a/remote.c b/remote.c\nindex 42a4e7106e1..fa3152a5d52 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -2172,6 +2172,7 @@ static int stat_branch_pair(const char *branch_name, const char *base,\n \tclear_commit_marks(theirs, ALL_REV_FLAGS);\n \n \tstrvec_clear(&argv);\n+\trelease_revisions(&revs);\n \treturn 1;\n }\n \ndiff --git a/sequencer.c b/sequencer.c\nindex a1bb39383db..f9d7acd1065 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1347,6 +1347,7 @@ void print_commit_summary(struct repository *r,\n \t\tlog_tree_commit(&rev, commit);\n \t}\n \n+\trelease_revisions(&rev);\n \tstrbuf_release(&format);\n }\n \n@@ -3415,6 +3416,7 @@ static int make_patch(struct repository *r,\n \t\tunuse_commit_buffer(commit, commit_buffer);\n \t}\n \tstrbuf_release(&buf);\n+\trelease_revisions(&log_tree_opt);\n \n \treturn res;\n }\n@@ -4525,6 +4527,7 @@ static int pick_commits(struct repository *r,\n \t\t\t\t\t      &log_tree_opt.diffopt);\n \t\t\t\tlog_tree_diff_flush(&log_tree_opt);\n \t\t\t}\n+\t\t\trelease_revisions(&log_tree_opt);\n \t\t}\n \t\tflush_rewritten_pending();\n \t\tif (!stat(rebase_path_rewritten_list(), &st) &&\ndiff --git a/shallow.c b/shallow.c\nindex 71e5876f377..2552f139f61 100644\n--- a/shallow.c\n+++ b/shallow.c\n@@ -261,6 +261,7 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,\n \t\tif ((o->flags & both_flags) == both_flags)\n \t\t\to->flags &= ~not_shallow_flag;\n \t}\n+\trelease_revisions(&revs);\n \treturn result;\n }\n \ndiff --git a/submodule.c b/submodule.c\nindex 5ace18a7d94..0510cb193b6 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -902,9 +902,11 @@ static void collect_changed_submodules(struct repository *r,\n \t\tdiff_rev.diffopt.format_callback_data = &data;\n \t\tdiff_rev.dense_combined_merges = 1;\n \t\tdiff_tree_combined_merge(commit, &diff_rev);\n+\t\trelease_revisions(&diff_rev);\n \t}\n \n \treset_revision_walk();\n+\trelease_revisions(&rev);\n }\n \n static void free_submodules_oids(struct string_list *submodules)\ndiff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c\nindex 625b2dbf822..4a45d5bac2a 100644\n--- a/t/helper/test-revision-walking.c\n+++ b/t/helper/test-revision-walking.c\n@@ -43,6 +43,7 @@ static int run_revision_walk(void)\n \t}\n \n \treset_revision_walk();\n+\trelease_revisions(&rev);\n \treturn got_revision;\n }\n \ndiff --git a/wt-status.c b/wt-status.c\nindex 922cf787f95..f9100621375 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1152,6 +1152,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)\n \t\trev.diffopt.b_prefix = \"w/\";\n \t\trun_diff_files(&rev, 0);\n \t}\n+\trelease_revisions(&rev);\n }\n \n static void wt_longstatus_print_tracking(struct wt_status *s)\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452290","messageId":"patch-v3-06.27-1e93a60d81a-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 06/27] revision.[ch]: provide and start using a release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:30Z","receivedAt":"2022-03-25T17:29:21Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The users of the revision.[ch] API's \"struct rev_info\" are a major\nsource of memory leaks in the test suite under SANITIZE=leak, which in\nturn adds a lot of noise when trying to mark up tests with\n\"TEST_PASSES_SANITIZE_LEAK=true\".\n\nThe users of that API are largely one-shot, e.g. \"git log\", so\narguably freeing the memory is a waste of time, but in many cases\nthey've actually been trying to free the memory, and just doing that\nin a buggy manner.\n\nLet's provide a release_revisions() function for these users, and\nstart migrating them over per the plan outlined in [1]. Right now this\nonly handles the \"pending\" member of the struct, but more will be\nadded in subsequent commits.\n\nEven though we only clear the \"pending\" member now, let's not leave a\ntrap in code like the pre-image of index_differs_from(), where we'd\nstart doing the wrong thing as soon as the release_revisions() learned\nto clear its \"diffopt\". I.e. we need to call release_revisions() after\nwe've inspected any state in \"struct rev_info\".\n\nThis leaves in place e.g. clear_pathspec(&rev.prune_data) in\nstash_working_tree() in builtin/stash.c, subsequent commits will teach\nrelease_revisions() to free \"prune_data\" and other members that in\nsome cases are individually cleared by users of \"struct rev_info\" by\nreaching into its members. Those subsequent commits will remove the\nrelevant calls to e.g. clear_pathspec().\n\nWe avoid amending code in index_differs_from() in diff-lib.c as well\nas wt_status_collect_changes_index(), has_unstaged_changes() and\nhas_uncommitted_changes() in wt-status.c in a way that assumes that we\nare already clearing the \"diffopt\" member. That will be handled in a\nsubsequent commit.\n\n1. https://lore.kernel.org/git/87a6k8daeu.fsf@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/checkout.c | 2 +-\n builtin/stash.c    | 3 +--\n diff-lib.c         | 2 +-\n range-diff.c       | 2 +-\n revision.c         | 5 +++++\n revision.h         | 6 ++++++\n wt-status.c        | 5 +++--\n 7 files changed, 18 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 797681481d1..4d9e0bd3ac1 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -629,7 +629,7 @@ static void show_local_changes(struct object *head,\n \tdiff_setup_done(&rev.diffopt);\n \tadd_pending_object(&rev, head, NULL);\n \trun_diff_index(&rev, 0);\n-\tobject_array_clear(&rev.pending);\n+\trelease_revisions(&rev);\n }\n \n static void describe_detached_head(const char *msg, struct commit *commit)\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex ccdfdab44be..ad74624c2f7 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1266,9 +1266,8 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps\n \n done:\n \tdiscard_index(&istate);\n-\tUNLEAK(rev);\n-\tobject_array_clear(&rev.pending);\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n \tstrbuf_release(&diff_output);\n \tremove_path(stash_index_path.buf);\n \treturn ret;\ndiff --git a/diff-lib.c b/diff-lib.c\nindex ca085a03efc..d6800274bd5 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -662,7 +662,7 @@ int index_differs_from(struct repository *r,\n \t\tdiff_flags_or(&rev.diffopt.flags, flags);\n \trev.diffopt.ita_invisible_in_index = ita_invisible_in_index;\n \trun_diff_index(&rev, 1);\n-\tobject_array_clear(&rev.pending);\n+\trelease_revisions(&rev);\n \treturn (rev.diffopt.flags.has_changes != 0);\n }\n \ndiff --git a/range-diff.c b/range-diff.c\nindex b72eb9fdbee..39cc010c628 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -596,6 +596,6 @@ int is_range_diff_range(const char *arg)\n \t}\n \n \tfree(copy);\n-\tobject_array_clear(&revs.pending);\n+\trelease_revisions(&revs);\n \treturn negative > 0 && positive > 0;\n }\ndiff --git a/revision.c b/revision.c\nindex ef45762bf21..895bba9ae57 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2931,6 +2931,11 @@ static void release_revisions_commit_list(struct rev_info *revs)\n \t}\n }\n \n+void release_revisions(struct rev_info *revs)\n+{\n+\tobject_array_clear(&revs->pending);\n+}\n+\n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n {\n \tstruct commit_list *l = xcalloc(1, sizeof(*l));\ndiff --git a/revision.h b/revision.h\nindex 5bc59c7bfe1..ad325a30453 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -377,6 +377,12 @@ void repo_init_revisions(struct repository *r,\n int setup_revisions(int argc, const char **argv, struct rev_info *revs,\n \t\t    struct setup_revision_opt *);\n \n+/**\n+ * Free data allocated in a \"struct rev_info\" after it's been setup\n+ * with repo_init_revisions() and setup_revisions().\n+ */\n+void release_revisions(struct rev_info *revs);\n+\n void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,\n \t\t\tconst struct option *options,\n \t\t\tconst char * const usagestr[]);\ndiff --git a/wt-status.c b/wt-status.c\nindex d33f9272b72..922cf787f95 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -662,7 +662,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)\n \n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_index(&rev, 1);\n-\tobject_array_clear(&rev.pending);\n+\trelease_revisions(&rev);\n \tclear_pathspec(&rev.prune_data);\n }\n \n@@ -2545,6 +2545,7 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)\n \trev_info.diffopt.flags.quick = 1;\n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_files(&rev_info, 0);\n+\trelease_revisions(&rev_info);\n \treturn diff_result_code(&rev_info.diffopt, result);\n }\n \n@@ -2577,7 +2578,7 @@ int has_uncommitted_changes(struct repository *r,\n \n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_index(&rev_info, 1);\n-\tobject_array_clear(&rev_info.pending);\n+\trelease_revisions(&rev_info);\n \treturn diff_result_code(&rev_info.diffopt, result);\n }\n \n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452291","messageId":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com","subject":"[PATCH v3 00/27] revision.[ch]: add and use release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:24Z","receivedAt":"2022-03-25T17:29:24Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"= Brief summary, more in the v1 CL[1]:\n\nThis series adds a release_revisions() and works towards fixing those\nmemory leaks one at a time. As the diffstat to t/* shows we can mark a\n*lot* of tests leak-free as a result, and those are only the the tests\nthat 100% pass with this series.\n\nAn approximate count of the total number of memory leaks in the test\nsuite (approximate because some are \"hidden\" due to SANITIZE=leak\ncausing \"git\" to die) is, before this series (running with \"--tee\" and\n\"--verbose-log\"):\n\n    $ grep -a \"SUMMARY: LeakSanitizer.*leaked in .*allocation\" test-results/*.out|wc -l\n    12032\n\nAfter:\n\n    $ grep -a \"SUMMARY: LeakSanitizer.*leaked in .*allocation\" test-results/*.out|wc -l\n    6652\n\n[NOTE: These numbers are for v1, and may not be *exactly* those\nanymore as \"master\" has moved, but are going to be in that ballpark].\n\n= Changes since v2[2]\n\nI think this addresses all the outstanding commments/feedback on the\nv2. Thanks to Junio for the review!\n\nThis still has a similar textual conflict as v2[2], and will require\nmy [3] on top of ds/partial-bundle-more to fully pass the leak tests,\ncurrently \"seen\" is failing due to that issue.\n\n1. https://lore.kernel.org/git/cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com/\n2. https://lore.kernel.org/git/cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com/\n3. https://lore.kernel.org/git/patch-1.1-193534b0f07-20220325T121715Z-avarab@gmail.com/\n\nÆvar Arnfjörð Bjarmason (27):\n  t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"\n  blame: use \"goto cleanup\" for cleanup_scoreboard()\n  string_list API users: use string_list_init_{no,}dup\n  format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"\n  revision.[ch]: split freeing of revs->commit into a function\n  revision.[ch]: provide and start using a release_revisions()\n  revisions API users: add straightforward release_revisions()\n  revisions API users: add \"goto cleanup\" for \"rev_info\" early exit\n  revisions API users: use release_revisions() in submodule.c edge case\n  stash: always have the owner of \"stash_info\" free it\n  revisions API users: add \"goto cleanup\" for release_revisions()\n  revisions API users: use release_revisions() in http-push.c\n  revisions API users: use release_revisions() in builtin/log.c\n  revisions API users: use release_revisions() with UNLEAK()\n  revisions API users: use release_revisions() for \"prune_data\" users\n  revisions API: have release_revisions() release \"commits\"\n  revisions API: have release_revisions() release \"mailmap\"\n  revisions API: have release_revisions() release \"cmdline\"\n  revisions API: have release_revisions() release \"filter\"\n  revisions API: have release_revisions() release \"grep_filter\"\n  revisions API: have release_revisions() release \"prune_data\"\n  revisions API: clear \"boundary_commits\" in release_revisions()\n  revisions API: release \"reflog_info\" in release revisions()\n  revisions API: call diff_free(&revs->pruning) in revisions_release()\n  revisions API: have release_revisions() release \"date_mode\"\n  revisions API: have release_revisions() release \"topo_walk_info\"\n  revisions API: add a TODO for diff_free(&revs->diffopt)\n\n add-interactive.c                          |  3 +-\n bisect.c                                   | 19 ++++--\n builtin/add.c                              |  3 +-\n builtin/am.c                               |  3 +\n builtin/bisect--helper.c                   |  2 +\n builtin/blame.c                            |  4 +-\n builtin/checkout.c                         |  3 +-\n builtin/commit.c                           |  6 +-\n builtin/describe.c                         |  2 +\n builtin/diff-files.c                       |  8 ++-\n builtin/diff-index.c                       |  5 +-\n builtin/diff.c                             |  2 +-\n builtin/fast-export.c                      |  1 +\n builtin/log.c                              | 36 ++++++----\n builtin/merge.c                            |  2 +\n builtin/pack-objects.c                     |  2 +\n builtin/prune.c                            |  1 +\n builtin/reflog.c                           |  1 +\n builtin/rev-list.c                         | 19 ++++--\n builtin/shortlog.c                         |  8 ++-\n builtin/stash.c                            | 77 +++++++++++-----------\n builtin/submodule--helper.c                | 17 +++--\n bundle.c                                   | 12 +++-\n diff-lib.c                                 |  8 ++-\n fmt-merge-msg.c                            |  1 +\n http-push.c                                |  3 +-\n merge-ort.c                                |  1 +\n merge-recursive.c                          |  5 +-\n midx.c                                     |  1 +\n pack-bitmap-write.c                        |  1 +\n range-diff.c                               |  2 +-\n ref-filter.c                               |  1 +\n reflog-walk.c                              | 24 ++++++-\n reflog-walk.h                              |  1 +\n remote.c                                   |  1 +\n revision.c                                 | 64 +++++++++++++++---\n revision.h                                 |  6 ++\n sequencer.c                                | 26 ++++++--\n shallow.c                                  |  1 +\n submodule.c                                | 43 ++++++------\n t/helper/test-fast-rebase.c                | 23 +++++--\n t/helper/test-revision-walking.c           |  1 +\n t/lib-git-svn.sh                           |  4 ++\n t/t0056-git-C.sh                           |  1 +\n t/t0062-revision-walking.sh                |  1 +\n t/t0100-previous.sh                        |  1 +\n t/t0101-at-syntax.sh                       |  2 +\n t/t1001-read-tree-m-2way.sh                |  1 +\n t/t1002-read-tree-m-u-2way.sh              |  1 +\n t/t1060-object-corruption.sh               |  2 +\n t/t1401-symbolic-ref.sh                    |  2 +\n t/t1411-reflog-show.sh                     |  1 +\n t/t1412-reflog-loop.sh                     |  2 +\n t/t1415-worktree-refs.sh                   |  1 +\n t/t2015-checkout-unborn.sh                 |  1 +\n t/t2200-add-update.sh                      |  1 +\n t/t3302-notes-index-expensive.sh           |  1 +\n t/t3303-notes-subtrees.sh                  |  1 +\n t/t3305-notes-fanout.sh                    |  1 +\n t/t3408-rebase-multi-line.sh               |  1 +\n t/t4021-format-patch-numbered.sh           |  1 +\n t/t4027-diff-submodule.sh                  |  1 +\n t/t4028-format-patch-mime-headers.sh       |  2 +\n t/t4036-format-patch-signer-mime.sh        |  1 +\n t/t4039-diff-assume-unchanged.sh           |  1 +\n t/t4055-diff-context.sh                    |  1 +\n t/t4066-diff-emit-delay.sh                 |  1 +\n t/t4122-apply-symlink-inside.sh            |  1 +\n t/t4126-apply-empty.sh                     |  1 -\n t/t4128-apply-root.sh                      |  1 +\n t/t4206-log-follow-harder-copies.sh        |  2 +\n t/t4207-log-decoration-colors.sh           |  1 +\n t/t4212-log-corrupt.sh                     |  1 +\n t/t5301-sliding-window.sh                  |  2 +\n t/t5313-pack-bounds-checks.sh              |  2 +\n t/t5316-pack-delta-depth.sh                |  2 +\n t/t5320-delta-islands.sh                   |  2 +\n t/t5322-pack-objects-sparse.sh             |  1 +\n t/t5506-remote-groups.sh                   |  1 +\n t/t5513-fetch-track.sh                     |  1 +\n t/t5515-fetch-merge-logic.sh               |  1 +\n t/t5518-fetch-exit-status.sh               |  1 +\n t/t5532-fetch-proxy.sh                     |  2 +\n t/t5600-clone-fail-cleanup.sh              |  1 +\n t/t5900-repo-selection.sh                  |  2 +\n t/t6002-rev-list-bisect.sh                 |  1 +\n t/t6003-rev-list-topo-order.sh             |  1 +\n t/t6005-rev-list-count.sh                  |  1 +\n t/t6018-rev-list-glob.sh                   |  1 +\n t/t6100-rev-list-in-order.sh               |  1 +\n t/t6101-rev-parse-parents.sh               |  1 +\n t/t6110-rev-list-sparse.sh                 |  1 +\n t/t6114-keep-packs.sh                      |  2 +\n t/t6131-pathspec-icase.sh                  |  2 +\n t/t7008-filter-branch-null-sha1.sh         |  1 +\n t/t7702-repack-cyclic-alternate.sh         |  2 +\n t/t9001-send-email.sh                      |  1 +\n t/t9100-git-svn-basic.sh                   |  1 +\n t/t9101-git-svn-props.sh                   |  2 +\n t/t9104-git-svn-follow-parent.sh           |  2 +\n t/t9106-git-svn-commit-diff-clobber.sh     |  2 +\n t/t9115-git-svn-dcommit-funky-renames.sh   |  1 +\n t/t9116-git-svn-log.sh                     |  1 +\n t/t9122-git-svn-author.sh                  |  2 +\n t/t9127-git-svn-partial-rebuild.sh         |  1 +\n t/t9129-git-svn-i18n-commitencoding.sh     |  1 +\n t/t9132-git-svn-broken-symlink.sh          |  1 +\n t/t9139-git-svn-non-utf8-commitencoding.sh |  1 +\n t/t9146-git-svn-empty-dirs.sh              |  2 +\n t/t9148-git-svn-propset.sh                 |  1 +\n t/t9160-git-svn-preserve-empty-dirs.sh     |  1 +\n t/t9162-git-svn-dcommit-interactive.sh     |  2 +\n t/t9164-git-svn-dcommit-concurrent.sh      |  2 +\n t/t9501-gitweb-standalone-http-status.sh   |  1 +\n wt-status.c                                | 15 +++--\n 115 files changed, 416 insertions(+), 145 deletions(-)\n\nRange-diff against v2:\n 1:  8fbafe61053 !  1:  7c8298f564f t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"\n    @@ Commit message\n         cases. But after f9500261e0a only the \"if\" block would free these two\n         \"struct strbuf\".\n     \n    -    This change is needed so that a subsequent addition of a \"goto\n    -    cleanup\" pattern when adding release_revisions() doesn't solve this\n    -    unrelated bug while it's at it.\n    -\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## t/helper/test-fast-rebase.c ##\n 2:  77e7c2c321e !  2:  515bacc8f27 blame: use \"goto cleanup\" for cleanup_scoreboard()\n    @@ Commit message\n         Bloom filters, 2020-04-16) to use a \"goto cleanup\", so that we can be\n         sure that we call cleanup_scoreboard().\n     \n    -    This change is needed so that a subsequent addition of a\n    -    release_revisions() doesn't solve this unrelated issue while it's at\n    -    it.\n    -\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/blame.c ##\n 3:  151d1175905 =  3:  58b59542c2b string_list API users: use string_list_init_{no,}dup\n 4:  64a76629e23 =  4:  71495dfd073 format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"\n 5:  4c0718b43d7 !  5:  12fb45f02c1 revision.[ch]: split freeing of revs->commit into a function\n    @@ revision.c: int setup_revisions(int argc, const char **argv, struct rev_info *re\n      \n     +static void release_revisions_commit_list(struct rev_info *revs)\n     +{\n    -+\tstruct commit_list *commits = revs->commits;\n    -+\n    -+\tif (!commits)\n    -+\t\treturn;\n    -+\tfree_commit_list(commits);\n    -+\trevs->commits = NULL;\n    ++\tif (revs->commits) {\n    ++\t\tfree_commit_list(revs->commits);\n    ++\t\trevs->commits = NULL;\n    ++\t}\n     +}\n     +\n      static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n 6:  7194aac88e1 !  6:  1e93a60d81a revision.[ch]: provide and start using a release_revisions()\n    @@ range-diff.c: int is_range_diff_range(const char *arg)\n     \n      ## revision.c ##\n     @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)\n    - \trevs->commits = NULL;\n    + \t}\n      }\n      \n     +void release_revisions(struct rev_info *revs)\n     +{\n    -+\tif (!revs)\n    -+\t\treturn;\n     +\tobject_array_clear(&revs->pending);\n     +}\n     +\n 7:  3cb36d358d6 =  7:  c4fc1c98e7b revisions API users: add straightforward release_revisions()\n 8:  42ad1208934 !  8:  f8a9443fe6f revisions API users: use release_revisions() needing \"{ 0 }\" init\n    @@ Metadata\n     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## Commit message ##\n    -    revisions API users: use release_revisions() needing \"{ 0 }\" init\n    +    revisions API users: add \"goto cleanup\" for \"rev_info\" early exit\n     \n    -    Use release_revisions() to various users of \"struct rev_list\" which\n    -    need to have their \"struct rev_info\" zero-initialized before we can\n    -    start using it. In all of these cases we might \"goto cleanup\" (or equivalent),\n    +    Add release_revisions() in various users of \"struct rev_info\" that can\n    +    mostly use a \"goto cleanup\" pattern, but also have an early \"return\"\n    +    before we've called repo_init_revisions(). We need to avoid calling\n    +    release_revisions() with uninitialized memory.\n    +\n    +    It would be a lot cleaner to be able to initialize \"struct rev_info\"\n    +    with \"{ 0 }\" here, or if a \"REV_INFO_INIT\" existed, we'll hopefully\n    +    get around to making the initialization easier in the future (now it\n    +    can't be done via a macro).\n    +\n    +    Until then let's leave a \"cleanup_no_rev[s]\" in place to document the\n    +    intention here. Only status_submodule() in builtin/submodule--helper.c\n    +    strictly speaking needs this, the other ones could keep their \"return\"\n    +    for the early exit. But let's have them also use the \"goto\n    +    cleanup[...]\" for consistency, and for the eventual migration to\n    +    simpler initialization.\n     \n         For the bundle.c code see the early exit case added in\n         3bbbe467f29 (bundle verify: error out if called without an object\n    @@ Commit message\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## bisect.c ##\n    -@@ bisect.c: void read_bisect_terms(const char **read_bad, const char **read_good)\n    -  */\n    - enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n    - {\n    --\tstruct rev_info revs;\n    -+\tstruct rev_info revs = { 0 };\n    - \tstruct commit_list *tried;\n    - \tint reaches = 0, all = 0, nr, steps;\n    - \tenum bisect_error res = BISECT_OK;\n     @@ bisect.c: enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n      \n      \tres = check_good_are_ancestors_of_bad(r, prefix, no_checkout);\n      \tif (res)\n     -\t\treturn res;\n    -+\t\tgoto cleanup;\n    ++\t\tgoto cleanup_no_revs;\n      \n      \tbisect_rev_setup(r, &revs, prefix, \"%s\", \"^%s\", 1);\n      \n    @@ bisect.c: enum bisect_error bisect_next_all(struct repository *r, const char *pr\n     +\tres = bisect_checkout(bisect_rev, no_checkout);\n     +cleanup:\n     +\trelease_revisions(&revs);\n    ++cleanup_no_revs:\n     +\treturn res;\n      }\n      \n    @@ bisect.c: enum bisect_error bisect_next_all(struct repository *r, const char *pr\n     \n      ## builtin/submodule--helper.c ##\n     @@ builtin/submodule--helper.c: static void status_submodule(const char *path, const struct object_id *ce_oid,\n    - {\n    - \tchar *displaypath;\n    - \tstruct strvec diff_files_args = STRVEC_INIT;\n    --\tstruct rev_info rev;\n    -+\tstruct rev_info rev = { 0 };\n    - \tint diff_files_result;\n    - \tstruct strbuf buf = STRBUF_INIT;\n    - \tconst char *git_dir;\n    + \n    + \tif ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) {\n    + \t\tprint_status(flags, 'U', path, null_oid(), displaypath);\n    +-\t\tgoto cleanup;\n    ++\t\tgoto cleanup_no_rev;\n    + \t}\n    + \n    + \tstrbuf_addf(&buf, \"%s/.git\", path);\n     @@ builtin/submodule--helper.c: static void status_submodule(const char *path, const struct object_id *ce_oid,\n    + \t    !is_git_directory(git_dir)) {\n    + \t\tprint_status(flags, '-', path, ce_oid, displaypath);\n    + \t\tstrbuf_release(&buf);\n    +-\t\tgoto cleanup;\n    ++\t\tgoto cleanup_no_rev;\n    + \t}\n    + \tstrbuf_release(&buf);\n    + \n    +@@ builtin/submodule--helper.c: static void status_submodule(const char *path, const struct object_id *ce_oid,\n    + \t}\n    + \n      cleanup:\n    ++\trelease_revisions(&rev);\n    ++cleanup_no_rev:\n      \tstrvec_clear(&diff_files_args);\n      \tfree(displaypath);\n    -+\trelease_revisions(&rev);\n      }\n    - \n    - static void status_submodule_cb(const struct cache_entry *list_item,\n     \n      ## bundle.c ##\n     @@ bundle.c: int verify_bundle(struct repository *r,\n    - \t * to be verbose about the errors\n    - \t */\n    - \tstruct string_list *p = &header->prerequisites;\n    --\tstruct rev_info revs;\n    -+\tstruct rev_info revs = { 0 };\n    - \tconst char *argv[] = {NULL, \"--all\", NULL};\n    - \tstruct commit *commit;\n      \tint i, ret = 0, req_nr;\n      \tconst char *message = _(\"Repository lacks these prerequisite commits:\");\n      \n    @@ bundle.c: int verify_bundle(struct repository *r,\n     -\t\treturn error(_(\"need a repository to verify a bundle\"));\n     +\tif (!r || !r->objects || !r->objects->odb) {\n     +\t\tret = error(_(\"need a repository to verify a bundle\"));\n    -+\t\tgoto cleanup;\n    ++\t\tgoto cleanup_no_revs;\n     +\t}\n      \n      \trepo_init_revisions(r, &revs, NULL);\n    @@ bundle.c: int verify_bundle(struct repository *r,\n      \t\t\tlist_refs(r, 0, NULL);\n      \t\t}\n      \t}\n    ++\n     +cleanup:\n     +\trelease_revisions(&revs);\n    ++cleanup_no_revs:\n      \treturn ret;\n      }\n      \n14:  38f4f1c4ecb !  9:  5f5c0d26395 revisions API users: use release_revisions() in submodule.c edge case\n    @@ Commit message\n         revisions API users: use release_revisions() in submodule.c edge case\n     \n         Use release_revisions() on the the \"struct rev_info\" in\n    -    show_submodule_diff_summary() where we need to pre-initialize with \"{\n    -    0 }\" to make sure that we won't segfault when calling\n    -    release_revisions().\n    -\n    -    Most functions that use \"struct rev_info\" will call\n    -    repo_init_revisions() early, which will do an equivalent memset(). In\n    -    this case we might \"goto out\" before doing the equivalent of that\n    -    operation, see the \"goto\" on \"(!left || !right || !sub)\" added in\n    +    show_submodule_diff_summary() where we'd otherwise need to do the\n    +    equivalent of pre-initializing the \"struct rev_info\" with \"{ 0 }\" if\n    +    we were going to add it to the cleanup being performed in the \"out\"\n    +    part of the function, let's instead introduce an \"out_no_rev\" for the\n    +    reasons discussed in the preceding commit.\n    +\n    +    Doing so for the \"goto\" on \"(!left || !right || !sub)\" added in\n         8e6df65015f (submodule: refactor show_submodule_summary with helper\n    -    function, 2016-08-31).\n    +    function, 2016-08-31) would have been straightforward, as in the\n    +    preceding commit.\n    +\n    +    But for the case of prepare_submodule_diff_summary() failing it's less\n    +    straightforward. Reading the pre-image we could simply retain the\n    +    \"goto out\" if it fails, because we can see that the first thing it\n    +    does is call repo_init_revisions().\n    +\n    +    But having a hard reliance on that would be a bit nasty, as we'd\n    +    potentially introduce a segfault if the function did some other\n    +    initialization first, and early aborted if that failed.\n    +\n    +    Let's just fold that helper function away into\n    +    show_submodule_diff_summary() itself, which was its only user. Now\n    +    following the flow of initialization is more obvious, and it's\n    +    immediately clear that the \"goto out\" if prepare_revision_walk()\n    +    returns non-zero is safe.\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## submodule.c ##\n    -@@ submodule.c: void show_submodule_diff_summary(struct diff_options *o, const char *path,\n    - \t\tstruct object_id *one, struct object_id *two,\n    - \t\tunsigned dirty_submodule)\n    +@@ submodule.c: void handle_ignore_submodules_arg(struct diff_options *diffopt,\n    + \t */\n    + }\n    + \n    +-static int prepare_submodule_diff_summary(struct repository *r, struct rev_info *rev,\n    +-\t\t\t\t\t  const char *path,\n    +-\t\t\t\t\t  struct commit *left, struct commit *right,\n    +-\t\t\t\t\t  struct commit_list *merge_bases)\n    +-{\n    +-\tstruct commit_list *list;\n    +-\n    +-\trepo_init_revisions(r, rev, NULL);\n    +-\tsetup_revisions(0, NULL, rev, NULL);\n    +-\trev->left_right = 1;\n    +-\trev->first_parent_only = 1;\n    +-\tleft->object.flags |= SYMMETRIC_LEFT;\n    +-\tadd_pending_object(rev, &left->object, path);\n    +-\tadd_pending_object(rev, &right->object, path);\n    +-\tfor (list = merge_bases; list; list = list->next) {\n    +-\t\tlist->item->object.flags |= UNINTERESTING;\n    +-\t\tadd_pending_object(rev, &list->item->object,\n    +-\t\t\toid_to_hex(&list->item->object.oid));\n    +-\t}\n    +-\treturn prepare_revision_walk(rev);\n    +-}\n    +-\n    + static void print_submodule_diff_summary(struct repository *r, struct rev_info *rev, struct diff_options *o)\n      {\n    --\tstruct rev_info rev;\n    -+\tstruct rev_info rev = { 0 };\n    + \tstatic const char format[] = \"  %m %s\";\n    +@@ submodule.c: void show_submodule_diff_summary(struct diff_options *o, const char *path,\n      \tstruct commit *left = NULL, *right = NULL;\n      \tstruct commit_list *merge_bases = NULL;\n      \tstruct repository *sub;\n    ++\tstruct commit_list *list;\n    + \n    + \tsub = open_submodule(path);\n    + \tshow_submodule_header(o, path, one, two, dirty_submodule,\n    +@@ submodule.c: void show_submodule_diff_summary(struct diff_options *o, const char *path,\n    + \t * all the information the user needs.\n    + \t */\n    + \tif (!left || !right || !sub)\n    +-\t\tgoto out;\n    ++\t\tgoto out_no_rev;\n    + \n    ++\trepo_init_revisions(sub, &rev, NULL);\n    ++\tsetup_revisions(0, NULL, &rev, NULL);\n    ++\trev.left_right = 1;\n    ++\trev.first_parent_only = 1;\n    ++\tleft->object.flags |= SYMMETRIC_LEFT;\n    ++\tadd_pending_object(&rev, &left->object, path);\n    ++\tadd_pending_object(&rev, &right->object, path);\n    ++\tfor (list = merge_bases; list; list = list->next) {\n    ++\t\tlist->item->object.flags |= UNINTERESTING;\n    ++\t\tadd_pending_object(&rev, &list->item->object,\n    ++\t\t\toid_to_hex(&list->item->object.oid));\n    ++\t}\n    + \t/* Treat revision walker failure the same as missing commits */\n    +-\tif (prepare_submodule_diff_summary(sub, &rev, path, left, right, merge_bases)) {\n    ++\tif (prepare_revision_walk(&rev)) {\n    + \t\tdiff_emit_submodule_error(o, \"(revision walker failed)\\n\");\n    + \t\tgoto out;\n    + \t}\n     @@ submodule.c: void show_submodule_diff_summary(struct diff_options *o, const char *path,\n      \tprint_submodule_diff_summary(sub, &rev, o);\n      \n      out:\n     +\trelease_revisions(&rev);\n    ++out_no_rev:\n      \tif (merge_bases)\n      \t\tfree_commit_list(merge_bases);\n      \tclear_commit_marks(left, ~0);\n 9:  88289157533 ! 10:  9da5c5f5f5d stash: always have the owner of \"stash_info\" free it\n    @@ Commit message\n     \n         Change the initialization of the \"revision\" member of \"struct\n         stash_info\" to be initialized vi a macro, and more importantly that\n    -    that initializing function be tasked to free it, usually via \"goto\n    +    that initializing function be tasked to free it, usually via a \"goto\n         cleanup\" pattern.\n     \n         Despite the \"revision\" name (and the topic of the series containing\n10:  eafe57663fc ! 11:  da4fcd04345 revisions API users: add \"goto cleanup\" for release_revisions()\n    @@ Metadata\n      ## Commit message ##\n         revisions API users: add \"goto cleanup\" for release_revisions()\n     \n    -    Add a release_revisions() to various users of \"struct rev_list\" which\n    +    Add a release_revisions() to various users of \"struct rev_info\" which\n         requires a minor refactoring to a \"goto cleanup\" pattern to use that\n         function.\n     \n11:  6fb2caf4d17 = 12:  b6acc61fb61 revisions API users: use release_revisions() in http-push.c\n12:  74818bc372f ! 13:  2eae0e809e5 revisions API users: use release_revisions() in builtin/log.c\n    @@ Commit message\n         \"format-patch\", etc. commands, all of which live in this file.\n     \n         The release_revisions() API still only frees the \"pending\" member, but\n    -    will learn to more members of \"struct rev_info\" in subsequent commits.\n    +    will learn to release more members of \"struct rev_info\" in subsequent\n    +    commits.\n     \n         In the case of \"format-patch\" revert the addition of UNLEAK() in\n    -    dee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16), and\n    +    dee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16),\n         which will cause several tests that previously passed under\n         \"TEST_PASSES_SANITIZE_LEAK=true\" to start failing.\n     \n    @@ Commit message\n         incremental progress in that direction. See [1] for further details\n         about this approach.\n     \n    +    Note that the release_revisions() will not be sufficient to deal with\n    +    the code in cmd_show() added in 5d7eeee2ac6 (git-show: grok blobs,\n    +    trees and tags, too, 2006-12-14) which clobbers the \"pending\" array in\n    +    the case of \"OBJ_COMMIT\". That will need to be dealt with by some\n    +    future follow-up work.\n    +\n         1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n13:  8717e6e8471 = 14:  216eb3b41f7 revisions API users: use release_revisions() with UNLEAK()\n15:  566b739ac6c = 15:  f8e0eb52957 revisions API users: use release_revisions() for \"prune_data\" users\n16:  bc36810e56e ! 16:  0a5662a9f67 revisions API: have release_revisions() release \"commits\"\n    @@ Commit message\n         revisions API: have release_revisions() release \"commits\"\n     \n         Extend the the release_revisions() function so that it frees the\n    -    \"commits\" in the \"struct ref_info\".\n    +    \"commits\" in the \"struct rev_info\".\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## revision.c ##\n    -@@ revision.c: void release_revisions(struct rev_info *revs)\n    +@@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)\n    + \n    + void release_revisions(struct rev_info *revs)\n      {\n    - \tif (!revs)\n    - \t\treturn;\n     +\trelease_revisions_commit_list(revs);\n      \tobject_array_clear(&revs->pending);\n      }\n17:  a93251edf85 ! 17:  65803961ee2 revisions API: have release_revisions() release \"mailmap\"\n    @@ Commit message\n         revisions API: have release_revisions() release \"mailmap\"\n     \n         Extend the the release_revisions() function so that it frees the\n    -    \"prune_data\" in the \"struct ref_info\".\n    +    \"mailmap\" in the \"struct rev_info\".\n     \n         The log family of functions now calls the clear_mailmap() function\n         added in fa8afd18e5a (revisions API: provide and use a\n    @@ builtin/commit.c: static const char *find_author_by_nickname(const char *name)\n     \n      ## revision.c ##\n     @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)\n    - \trevs->commits = NULL;\n    + \t}\n      }\n      \n     +static void release_revisions_mailmap(struct string_list *mailmap)\n    @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)\n     +\n      void release_revisions(struct rev_info *revs)\n      {\n    - \tif (!revs)\n    - \t\treturn;\n      \trelease_revisions_commit_list(revs);\n      \tobject_array_clear(&revs->pending);\n     +\trelease_revisions_mailmap(revs->mailmap);\n18:  424fac7ab95 ! 18:  ac68630dbf6 revisions API: have release_revisions() release \"cmdline\"\n    @@ Commit message\n         revisions API: have release_revisions() release \"cmdline\"\n     \n         Extend the the release_revisions() function so that it frees the\n    -    \"cmdline\" in the \"struct ref_info\". This in combination with a\n    +    \"cmdline\" in the \"struct rev_info\". This in combination with a\n         preceding change to free \"commits\" and \"mailmap\" means that we can\n         whitelist another test under \"TEST_PASSES_SANITIZE_LEAK=true\".\n     \n    @@ Commit message\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n    -    fixup with bc0e0439040?\n    -\n      ## revision.c ##\n     @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)\n    - \trevs->commits = NULL;\n    + \t}\n      }\n      \n     +static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)\n    @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)\n      {\n      \tif (!mailmap)\n     @@ revision.c: void release_revisions(struct rev_info *revs)\n    - \t\treturn;\n    + {\n      \trelease_revisions_commit_list(revs);\n      \tobject_array_clear(&revs->pending);\n     +\trelease_revisions_cmdline(&revs->cmdline);\n19:  d5b7a8a690e ! 19:  740abb80c57 revisions API: have release_revisions() release \"filter\"\n    @@ Commit message\n         revisions API: have release_revisions() release \"filter\"\n     \n         Extend the the release_revisions() function so that it frees the\n    -    \"cmdline\" in the \"struct ref_info\". This in combination with a\n    +    \"filter\" in the \"struct rev_info\". This in combination with a\n         preceding change to free \"cmdline\" means that we can mark another set\n         of tests as passing under \"TEST_PASSES_SANITIZE_LEAK=true\".\n     \n20:  857880bb4b6 ! 20:  26163e5e754 revisions API: have release_revisions() release \"grep_filter\"\n    @@ Commit message\n         revisions API: have release_revisions() release \"grep_filter\"\n     \n         Extend the the release_revisions() function so that it frees the\n    -    \"grep_filter\" in the \"struct ref_info\".This allows us to mark a test\n    +    \"grep_filter\" in the \"struct rev_info\".This allows us to mark a test\n         as passing under \"TEST_PASSES_SANITIZE_LEAK=true\".\n     \n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n21:  f98d473f08e ! 21:  842faebddc4 revisions API: have release_revisions() release \"prune_data\"\n    @@ Commit message\n         revisions API: have release_revisions() release \"prune_data\"\n     \n         Extend the the release_revisions() function so that it frees the\n    -    \"prune_data\" in the \"struct ref_info\". This means that any code that\n    +    \"prune_data\" in the \"struct rev_info\". This means that any code that\n         calls \"release_revisions()\" already can get rid of adjacent calls to\n         clear_pathspec().\n     \n22:  827430315fa ! 22:  bf93197993c revisions API: clear \"boundary_commits\" in release_revisions()\n    @@ Commit message\n     \n      ## revision.c ##\n     @@ revision.c: void release_revisions(struct rev_info *revs)\n    - \t\treturn;\n    + {\n      \trelease_revisions_commit_list(revs);\n      \tobject_array_clear(&revs->pending);\n     +\tobject_array_clear(&revs->boundary_commits);\n23:  c559b7d538b = 23:  af874c17fbd revisions API: release \"reflog_info\" in release revisions()\n24:  c3a22f5b474 = 24:  006fa1fcc34 revisions API: call diff_free(&revs->pruning) in revisions_release()\n25:  dabbb430ece = 25:  0eacbd66711 revisions API: have release_revisions() release \"date_mode\"\n26:  644d1b54afe ! 26:  5efc0ae58ed revisions API: have release_revisions() release \"topo_walk_info\"\n    @@ revision.c: static void release_revisions_mailmap(struct string_list *mailmap)\n     +\n      void release_revisions(struct rev_info *revs)\n      {\n    - \tif (!revs)\n    + \trelease_revisions_commit_list(revs);\n     @@ revision.c: void release_revisions(struct rev_info *revs)\n      \tfree_grep_patterns(&revs->grep_filter);\n      \tdiff_free(&revs->pruning);\n27:  2a4d50a0ec8 = 27:  6c6fdf696af revisions API: add a TODO for diff_free(&revs->diffopt)\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452292","messageId":"patch-v3-17.27-65803961ee2-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 17/27] revisions API: have release_revisions() release \"mailmap\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:41Z","receivedAt":"2022-03-25T17:30:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"mailmap\" in the \"struct rev_info\".\n\nThe log family of functions now calls the clear_mailmap() function\nadded in fa8afd18e5a (revisions API: provide and use a\nrelease_revisions(), 2021-09-19), allowing us to whitelist some tests\nwith \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nUnfortunately having a pointer to a mailmap in \"struct rev_info\"\ninstead of an embedded member that we \"own\" get a bit messy, as can be\nseen in the change to builtin/commit.c.\n\nWhen we free() this data we won't be able to tell apart a pointer to a\n\"mailmap\" on the heap from one on the stack. As seen in\nea57bc0d41b (log: add --use-mailmap option, 2013-01-05) the \"log\"\nfamily allocates it on the heap, but in the find_author_by_nickname()\ncode added in ea16794e430 (commit: search author pattern against\nmailmap, 2013-08-23) we allocated it on the stack instead.\n\nIdeally we'd simply change that member to a \"struct string_list\nmailmap\" and never free() the \"mailmap\" itself, but that would be a\nmuch larger change to the revisions API.\n\nWe have code that needs to hand an existing \"mailmap\" to a \"struct\nrev_info\", while we could change all of that, let's not go there\nnow.\n\nThe complexity isn't in the ownership of the \"mailmap\" per-se, but\nthat various things assume a \"rev_info.mailmap == NULL\" means \"doesn't\nwant mailmap\", if we changed that to an init'd \"struct string_list\nwe'd need to carefully refactor things to change those assumptions.\n\nLet's instead always free() it, and simply declare that if you add\nsuch a \"mailmap\" it must be allocated on the heap. Any modern libc\nwill correctly panic if we free() a stack variable, so this should be\nsafe going forward.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/commit.c                   | 5 ++---\n revision.c                         | 9 +++++++++\n t/t0056-git-C.sh                   | 1 +\n t/t3302-notes-index-expensive.sh   | 1 +\n t/t4055-diff-context.sh            | 1 +\n t/t4066-diff-emit-delay.sh         | 1 +\n t/t7008-filter-branch-null-sha1.sh | 1 +\n 7 files changed, 16 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex c7eda9bbb72..cd6cebcf8c8 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1100,7 +1100,6 @@ static const char *find_author_by_nickname(const char *name)\n \tstruct rev_info revs;\n \tstruct commit *commit;\n \tstruct strbuf buf = STRBUF_INIT;\n-\tstruct string_list mailmap = STRING_LIST_INIT_NODUP;\n \tconst char *av[20];\n \tint ac = 0;\n \n@@ -1111,7 +1110,8 @@ static const char *find_author_by_nickname(const char *name)\n \tav[++ac] = buf.buf;\n \tav[++ac] = NULL;\n \tsetup_revisions(ac, av, &revs, NULL);\n-\trevs.mailmap = &mailmap;\n+\trevs.mailmap = xmalloc(sizeof(struct string_list));\n+\tstring_list_init_nodup(revs.mailmap);\n \tread_mailmap(revs.mailmap);\n \n \tif (prepare_revision_walk(&revs))\n@@ -1122,7 +1122,6 @@ static const char *find_author_by_nickname(const char *name)\n \t\tctx.date_mode.type = DATE_NORMAL;\n \t\tstrbuf_release(&buf);\n \t\tformat_commit_message(commit, \"%aN <%aE>\", &buf, &ctx);\n-\t\tclear_mailmap(&mailmap);\n \t\trelease_revisions(&revs);\n \t\treturn strbuf_detach(&buf, NULL);\n \t}\ndiff --git a/revision.c b/revision.c\nindex 84b47552a3e..8520823d503 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2931,10 +2931,19 @@ static void release_revisions_commit_list(struct rev_info *revs)\n \t}\n }\n \n+static void release_revisions_mailmap(struct string_list *mailmap)\n+{\n+\tif (!mailmap)\n+\t\treturn;\n+\tclear_mailmap(mailmap);\n+\tfree(mailmap);\n+}\n+\n void release_revisions(struct rev_info *revs)\n {\n \trelease_revisions_commit_list(revs);\n \tobject_array_clear(&revs->pending);\n+\trelease_revisions_mailmap(revs->mailmap);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\ndiff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh\nindex 2630e756dab..752aa8c9454 100755\n--- a/t/t0056-git-C.sh\n+++ b/t/t0056-git-C.sh\n@@ -2,6 +2,7 @@\n \n test_description='\"-C <path>\" option and its effects on other path-related options'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success '\"git -C <path>\" runs git from the directory <path>' '\ndiff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh\nindex bb5fea02a03..d0c4d38b4d4 100755\n--- a/t/t3302-notes-index-expensive.sh\n+++ b/t/t3302-notes-index-expensive.sh\n@@ -8,6 +8,7 @@ test_description='Test commit notes index (expensive!)'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n create_repo () {\ndiff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh\nindex 741e0803c1a..73048d0a526 100755\n--- a/t/t4055-diff-context.sh\n+++ b/t/t4055-diff-context.sh\n@@ -5,6 +5,7 @@\n \n test_description='diff.context configuration'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t4066-diff-emit-delay.sh b/t/t4066-diff-emit-delay.sh\nindex a1de63b77f8..0ecb3915412 100755\n--- a/t/t4066-diff-emit-delay.sh\n+++ b/t/t4066-diff-emit-delay.sh\n@@ -4,6 +4,7 @@ test_description='test combined/stat/moved interaction'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # This test covers a weird 3-way interaction between \"--cc -p\", which will run\ndiff --git a/t/t7008-filter-branch-null-sha1.sh b/t/t7008-filter-branch-null-sha1.sh\nindex 9ba9f24ad2f..93fbc92b8db 100755\n--- a/t/t7008-filter-branch-null-sha1.sh\n+++ b/t/t7008-filter-branch-null-sha1.sh\n@@ -1,6 +1,7 @@\n #!/bin/sh\n \n test_description='filter-branch removal of trees with null sha1'\n+\n . ./test-lib.sh\n \n test_expect_success 'setup: base commits' '\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452293","messageId":"patch-v3-22.27-bf93197993c-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 22/27] revisions API: clear \"boundary_commits\" in release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:46Z","receivedAt":"2022-03-25T17:31:43Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Clear the \"boundary_commits\" object_array in release_revisions(). This\nmakes a few more tests pass under SANITIZE=leak, including\n\"t/t4126-apply-empty.sh\" which started failed as an UNLEAK() in\ncmd_format_patch() was removed in a preceding commit.\n\nThis also re-marks the various tests relying on \"git format-patch\" as\npassing under \"SANITIZE=leak\", in the preceding \"revisions API users:\nuse release_revisions() in builtin/log.c\" commit those were marked as\nfailing as we removed the UNLEAK(rev) from cmd_format_patch() in\n\"builtin/log.c\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c                           | 1 +\n t/t4021-format-patch-numbered.sh     | 1 +\n t/t4028-format-patch-mime-headers.sh | 2 ++\n t/t4036-format-patch-signer-mime.sh  | 1 +\n t/t4122-apply-symlink-inside.sh      | 1 +\n t/t4126-apply-empty.sh               | 1 +\n t/t6110-rev-list-sparse.sh           | 1 +\n t/t9001-send-email.sh                | 1 +\n t/t9116-git-svn-log.sh               | 1 -\n 9 files changed, 9 insertions(+), 1 deletion(-)\n\ndiff --git a/revision.c b/revision.c\nindex 6fd582819fe..53b3ab6dc2d 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2954,6 +2954,7 @@ void release_revisions(struct rev_info *revs)\n {\n \trelease_revisions_commit_list(revs);\n \tobject_array_clear(&revs->pending);\n+\tobject_array_clear(&revs->boundary_commits);\n \trelease_revisions_cmdline(&revs->cmdline);\n \tlist_objects_filter_release(&revs->filter);\n \tclear_pathspec(&revs->prune_data);\ndiff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh\nindex 9be65fd4440..1219aa226dc 100755\n--- a/t/t4021-format-patch-numbered.sh\n+++ b/t/t4021-format-patch-numbered.sh\n@@ -5,6 +5,7 @@\n \n test_description='Format-patch numbering options'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4028-format-patch-mime-headers.sh b/t/t4028-format-patch-mime-headers.sh\nindex 204ba673cb5..60cb819c42e 100755\n--- a/t/t4028-format-patch-mime-headers.sh\n+++ b/t/t4028-format-patch-mime-headers.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='format-patch mime headers and extra headers do not conflict'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'create commit with utf-8 body' '\ndiff --git a/t/t4036-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh\nindex 98d9713d8b2..48655bcc789 100755\n--- a/t/t4036-format-patch-signer-mime.sh\n+++ b/t/t4036-format-patch-signer-mime.sh\n@@ -2,6 +2,7 @@\n \n test_description='format-patch -s should force MIME encoding as needed'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh\nindex aa52de401b9..96965373036 100755\n--- a/t/t4122-apply-symlink-inside.sh\n+++ b/t/t4122-apply-symlink-inside.sh\n@@ -4,6 +4,7 @@ test_description='apply to deeper directory without getting fooled with symlink'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh\nindex 66a7ba8ab8f..ece9fae207d 100755\n--- a/t/t4126-apply-empty.sh\n+++ b/t/t4126-apply-empty.sh\n@@ -2,6 +2,7 @@\n \n test_description='apply empty'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t6110-rev-list-sparse.sh b/t/t6110-rev-list-sparse.sh\nindex 13c1da53528..ddefc7f24ee 100755\n--- a/t/t6110-rev-list-sparse.sh\n+++ b/t/t6110-rev-list-sparse.sh\n@@ -4,6 +4,7 @@ test_description='operations that cull histories in unusual ways'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh\nindex 84d0f40d76a..dfa6b20f7a6 100755\n--- a/t/t9001-send-email.sh\n+++ b/t/t9001-send-email.sh\n@@ -4,6 +4,7 @@ test_description='git send-email'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # May be altered later in the test\ndiff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh\nindex 34f6c80dea3..d74d7b2de68 100755\n--- a/t/t9116-git-svn-log.sh\n+++ b/t/t9116-git-svn-log.sh\n@@ -5,7 +5,6 @@\n \n test_description='git svn log tests'\n \n-TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup repository and import' '\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452294","messageId":"patch-v3-18.27-ac68630dbf6-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 18/27] revisions API: have release_revisions() release \"cmdline\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:42Z","receivedAt":"2022-03-25T17:31:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"cmdline\" in the \"struct rev_info\". This in combination with a\npreceding change to free \"commits\" and \"mailmap\" means that we can\nwhitelist another test under \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nThere was a proposal in [1] to do away with xstrdup()-ing this\nadd_rev_cmdline(), perhaps that would be worthwhile, but for now let's\njust free() it.\n\nWe could also make that a \"char *\" in \"struct rev_cmdline_entry\"\nitself, but since we own it let's expose it as a constant to outside\ncallers. I proposed that in [2] but have since changed my mind. See\n14d30cdfc04 (ref-filter: fix memory leak in `free_array_item()`,\n2019-07-10), c514c62a4fd (checkout: fix leak of non-existent branch\nnames, 2020-08-14) and other log history hits for \"free((char *)\" for\nprior art.\n\nThis includes the tests we had false-positive passes on before my\n6798b08e848 (perl Git.pm: don't ignore signalled failure in\n_cmd_close(), 2022-02-01), now they pass for real.\n\nSince there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to\nlist those that don't pass than to touch most of those 66. So let's\nintroduce a \"TEST_FAILS_SANITIZE_LEAK=true\", which if set in the tests\nwon't cause lib-git-svn.sh to set \"TEST_PASSES_SANITIZE_LEAK=true.\n\nThis change also marks all the tests that we removed\n\"TEST_FAILS_SANITIZE_LEAK=true\" from in an earlier commit due to\nremoving the UNLEAK() from cmd_format_patch(), we can now assert that\nits API use doesn't leak any \"struct rev_info\" memory.\n\nThis change also made commit \"t5503-tagfollow.sh\" pass on current\nmaster, but that would regress when combined with\nps/fetch-atomic-fixup's de004e848a9 (t5503: simplify setup of test\nwhich exercises failure of backfill, 2022-03-03) (through no fault of\nthat topic, that change started using \"git clone\" in the test, which\nhas an outstanding leak). Let's leave that test out for now to avoid\nin-flight semantic conflicts.\n\n1. https://lore.kernel.org/git/YUj%2FgFRh6pwrZalY@carlos-mbp.lan/\n2. https://lore.kernel.org/git/87o88obkb1.fsf@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c                                 | 12 ++++++++++++\n t/lib-git-svn.sh                           |  4 ++++\n t/t0062-revision-walking.sh                |  1 +\n t/t0101-at-syntax.sh                       |  2 ++\n t/t1060-object-corruption.sh               |  1 +\n t/t3303-notes-subtrees.sh                  |  1 +\n t/t3305-notes-fanout.sh                    |  1 +\n t/t3408-rebase-multi-line.sh               |  1 +\n t/t4027-diff-submodule.sh                  |  1 +\n t/t4128-apply-root.sh                      |  1 +\n t/t4212-log-corrupt.sh                     |  1 +\n t/t5515-fetch-merge-logic.sh               |  1 +\n t/t5518-fetch-exit-status.sh               |  1 +\n t/t6002-rev-list-bisect.sh                 |  1 +\n t/t6003-rev-list-topo-order.sh             |  1 +\n t/t6005-rev-list-count.sh                  |  1 +\n t/t6018-rev-list-glob.sh                   |  1 +\n t/t6100-rev-list-in-order.sh               |  1 +\n t/t9100-git-svn-basic.sh                   |  1 +\n t/t9101-git-svn-props.sh                   |  2 ++\n t/t9104-git-svn-follow-parent.sh           |  2 ++\n t/t9106-git-svn-commit-diff-clobber.sh     |  2 ++\n t/t9115-git-svn-dcommit-funky-renames.sh   |  1 +\n t/t9116-git-svn-log.sh                     |  2 ++\n t/t9122-git-svn-author.sh                  |  2 ++\n t/t9127-git-svn-partial-rebuild.sh         |  2 ++\n t/t9129-git-svn-i18n-commitencoding.sh     |  1 +\n t/t9132-git-svn-broken-symlink.sh          |  1 +\n t/t9139-git-svn-non-utf8-commitencoding.sh |  1 +\n t/t9146-git-svn-empty-dirs.sh              |  2 ++\n t/t9148-git-svn-propset.sh                 |  1 +\n t/t9151-svn-mergeinfo.sh                   |  1 +\n t/t9160-git-svn-preserve-empty-dirs.sh     |  1 +\n t/t9162-git-svn-dcommit-interactive.sh     |  2 ++\n t/t9164-git-svn-dcommit-concurrent.sh      |  2 ++\n t/t9501-gitweb-standalone-http-status.sh   |  1 +\n 36 files changed, 60 insertions(+)\n\ndiff --git a/revision.c b/revision.c\nindex 8520823d503..d864b9e2064 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2931,6 +2931,17 @@ static void release_revisions_commit_list(struct rev_info *revs)\n \t}\n }\n \n+static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)\n+{\n+\tunsigned int i;\n+\n+\tif (!cmdline)\n+\t\treturn;\n+\tfor (i = 0; i < cmdline->nr; i++)\n+\t\tfree((char *)cmdline->rev[i].name);\n+\tFREE_AND_NULL(cmdline->rev);\n+}\n+\n static void release_revisions_mailmap(struct string_list *mailmap)\n {\n \tif (!mailmap)\n@@ -2943,6 +2954,7 @@ void release_revisions(struct rev_info *revs)\n {\n \trelease_revisions_commit_list(revs);\n \tobject_array_clear(&revs->pending);\n+\trelease_revisions_cmdline(&revs->cmdline);\n \trelease_revisions_mailmap(revs->mailmap);\n }\n \ndiff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh\nindex 2fde2353fd3..ea28971e8ee 100644\n--- a/t/lib-git-svn.sh\n+++ b/t/lib-git-svn.sh\n@@ -1,3 +1,7 @@\n+if test -z \"$TEST_FAILS_SANITIZE_LEAK\"\n+then\n+\tTEST_PASSES_SANITIZE_LEAK=true\n+fi\n . ./test-lib.sh\n \n if test -n \"$NO_SVN_TESTS\"\ndiff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh\nindex 8e215867b8c..b9480c81781 100755\n--- a/t/t0062-revision-walking.sh\n+++ b/t/t0062-revision-walking.sh\n@@ -5,6 +5,7 @@\n \n test_description='Test revision walking api'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n cat >run_twice_expected <<-EOF\ndiff --git a/t/t0101-at-syntax.sh b/t/t0101-at-syntax.sh\nindex a1998b558f9..878aadd64c9 100755\n--- a/t/t0101-at-syntax.sh\n+++ b/t/t0101-at-syntax.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='various @{whatever} syntax tests'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh\nindex bc89371f534..e8a58b15897 100755\n--- a/t/t1060-object-corruption.sh\n+++ b/t/t1060-object-corruption.sh\n@@ -1,6 +1,7 @@\n #!/bin/sh\n \n test_description='see how we handle various forms of corruption'\n+\n . ./test-lib.sh\n \n # convert \"1234abcd\" to \".git/objects/12/34abcd\"\ndiff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh\nindex eac193757bf..bc9b791d1b9 100755\n--- a/t/t3303-notes-subtrees.sh\n+++ b/t/t3303-notes-subtrees.sh\n@@ -5,6 +5,7 @@ test_description='Test commit notes organized in subtrees'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n number_of_commits=100\ndiff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh\nindex 9976d787f47..64a9915761a 100755\n--- a/t/t3305-notes-fanout.sh\n+++ b/t/t3305-notes-fanout.sh\n@@ -2,6 +2,7 @@\n \n test_description='Test that adding/removing many notes triggers automatic fanout restructuring'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n path_has_fanout() {\ndiff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh\nindex cde3562e3a6..7b4607d72f2 100755\n--- a/t/t3408-rebase-multi-line.sh\n+++ b/t/t3408-rebase-multi-line.sh\n@@ -5,6 +5,7 @@ test_description='rebasing a commit with multi-line first paragraph.'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh\nindex 295da987cce..40164ae07d2 100755\n--- a/t/t4027-diff-submodule.sh\n+++ b/t/t4027-diff-submodule.sh\n@@ -2,6 +2,7 @@\n \n test_description='difference in submodules'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-diff.sh\n \ndiff --git a/t/t4128-apply-root.sh b/t/t4128-apply-root.sh\nindex f6db5a79dd9..ed94c90204e 100755\n--- a/t/t4128-apply-root.sh\n+++ b/t/t4128-apply-root.sh\n@@ -2,6 +2,7 @@\n \n test_description='apply same filename'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh\nindex 0244888a5a7..30a219894bb 100755\n--- a/t/t4212-log-corrupt.sh\n+++ b/t/t4212-log-corrupt.sh\n@@ -2,6 +2,7 @@\n \n test_description='git log with invalid commit headers'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh\nindex 320d26796d2..c100a809c5e 100755\n--- a/t/t5515-fetch-merge-logic.sh\n+++ b/t/t5515-fetch-merge-logic.sh\n@@ -14,6 +14,7 @@ export GIT_TEST_PROTOCOL_VERSION\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n build_script () {\ndiff --git a/t/t5518-fetch-exit-status.sh b/t/t5518-fetch-exit-status.sh\nindex 5c4ac2556e7..c13120088fa 100755\n--- a/t/t5518-fetch-exit-status.sh\n+++ b/t/t5518-fetch-exit-status.sh\n@@ -8,6 +8,7 @@ test_description='fetch exit status test'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh\nindex b95a0212adf..162cf50778d 100755\n--- a/t/t6002-rev-list-bisect.sh\n+++ b/t/t6002-rev-list-bisect.sh\n@@ -4,6 +4,7 @@\n #\n test_description='Tests git rev-list --bisect functionality'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-t6000.sh # t6xxx specific functions\n \ndiff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh\nindex 24d1836f417..1f7d7dd20c1 100755\n--- a/t/t6003-rev-list-topo-order.sh\n+++ b/t/t6003-rev-list-topo-order.sh\n@@ -5,6 +5,7 @@\n \n test_description='Tests git rev-list --topo-order functionality'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-t6000.sh # t6xxx specific functions\n \ndiff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh\nindex e960049f647..0729f800c3c 100755\n--- a/t/t6005-rev-list-count.sh\n+++ b/t/t6005-rev-list-count.sh\n@@ -2,6 +2,7 @@\n \n test_description='git rev-list --max-count and --skip test'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh\nindex 24b34add833..e1abc5c2b32 100755\n--- a/t/t6018-rev-list-glob.sh\n+++ b/t/t6018-rev-list-glob.sh\n@@ -5,6 +5,7 @@ test_description='rev-list/rev-parse --glob'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n commit () {\ndiff --git a/t/t6100-rev-list-in-order.sh b/t/t6100-rev-list-in-order.sh\nindex e934bc239c5..88ed7bd75a7 100755\n--- a/t/t6100-rev-list-in-order.sh\n+++ b/t/t6100-rev-list-in-order.sh\n@@ -2,6 +2,7 @@\n \n test_description='rev-list testing in-commit-order'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup a commit history with trees, blobs' '\ndiff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh\nindex fea41b3c360..7c5b847f584 100755\n--- a/t/t9100-git-svn-basic.sh\n+++ b/t/t9100-git-svn-basic.sh\n@@ -8,6 +8,7 @@ test_description='git svn basic tests'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n prepare_utf8_locale\ndiff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh\nindex 8b5681dd68a..d043e80fc34 100755\n--- a/t/t9101-git-svn-props.sh\n+++ b/t/t9101-git-svn-props.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn property tests'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n mkdir import\ndiff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh\nindex c7d8e0bf00f..5cf2ef4b8b0 100755\n--- a/t/t9104-git-svn-follow-parent.sh\n+++ b/t/t9104-git-svn-follow-parent.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn fetching'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh\nindex aec45bca3b7..3cab0b9720a 100755\n--- a/t/t9106-git-svn-commit-diff-clobber.sh\n+++ b/t/t9106-git-svn-commit-diff-clobber.sh\n@@ -2,6 +2,8 @@\n #\n # Copyright (c) 2006 Eric Wong\n test_description='git svn commit-diff clobber'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh\nindex 743fbe1fe46..419f055721d 100755\n--- a/t/t9115-git-svn-dcommit-funky-renames.sh\n+++ b/t/t9115-git-svn-dcommit-funky-renames.sh\n@@ -5,6 +5,7 @@\n \n test_description='git svn dcommit can commit renames of files with ugly names'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'load repository with strange names' '\ndiff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh\nindex 0a9f1ef366d..34f6c80dea3 100755\n--- a/t/t9116-git-svn-log.sh\n+++ b/t/t9116-git-svn-log.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn log tests'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup repository and import' '\ndiff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh\nindex 9e8fe38e7ef..527ba3d2932 100755\n--- a/t/t9122-git-svn-author.sh\n+++ b/t/t9122-git-svn-author.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='git svn authorship'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup svn repository' '\ndiff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh\nindex 2e4789d061f..90b1b30dde5 100755\n--- a/t/t9127-git-svn-partial-rebuild.sh\n+++ b/t/t9127-git-svn-partial-rebuild.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn partial-rebuild tests'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize svnrepo' '\ndiff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh\nindex 01e1e8a8f76..185248a4cd7 100755\n--- a/t/t9129-git-svn-i18n-commitencoding.sh\n+++ b/t/t9129-git-svn-i18n-commitencoding.sh\n@@ -4,6 +4,7 @@\n \n test_description='git svn honors i18n.commitEncoding in config'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n compare_git_head_with () {\ndiff --git a/t/t9132-git-svn-broken-symlink.sh b/t/t9132-git-svn-broken-symlink.sh\nindex aeceffaf7b0..4d8d0584b79 100755\n--- a/t/t9132-git-svn-broken-symlink.sh\n+++ b/t/t9132-git-svn-broken-symlink.sh\n@@ -2,6 +2,7 @@\n \n test_description='test that git handles an svn repository with empty symlinks'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n test_expect_success 'load svn dumpfile' '\n \tsvnadmin load \"$rawsvnrepo\" <<EOF\ndiff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh\nindex 22d80b0be2b..b7f756b2b7f 100755\n--- a/t/t9139-git-svn-non-utf8-commitencoding.sh\n+++ b/t/t9139-git-svn-non-utf8-commitencoding.sh\n@@ -4,6 +4,7 @@\n \n test_description='git svn refuses to dcommit non-UTF8 messages'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n # ISO-2022-JP can pass for valid UTF-8, so skipping that in this test\ndiff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh\nindex 80cb55fee70..79c26ed69c1 100755\n--- a/t/t9146-git-svn-empty-dirs.sh\n+++ b/t/t9146-git-svn-empty-dirs.sh\n@@ -3,6 +3,8 @@\n # Copyright (c) 2009 Eric Wong\n \n test_description='git svn creates empty directories'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9148-git-svn-propset.sh b/t/t9148-git-svn-propset.sh\nindex aebb28995e5..6cc76a07b39 100755\n--- a/t/t9148-git-svn-propset.sh\n+++ b/t/t9148-git-svn-propset.sh\n@@ -5,6 +5,7 @@\n \n test_description='git svn propset tests'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup propset via import' '\ndiff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh\nindex c93a5beab25..85221d439bd 100755\n--- a/t/t9151-svn-mergeinfo.sh\n+++ b/t/t9151-svn-mergeinfo.sh\n@@ -5,6 +5,7 @@\n \n test_description='git-svn svn mergeinfo properties'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'load svn dump' \"\ndiff --git a/t/t9160-git-svn-preserve-empty-dirs.sh b/t/t9160-git-svn-preserve-empty-dirs.sh\nindex 36c6b1a12ff..9cf7a1427ab 100755\n--- a/t/t9160-git-svn-preserve-empty-dirs.sh\n+++ b/t/t9160-git-svn-preserve-empty-dirs.sh\n@@ -9,6 +9,7 @@ This test uses git to clone a Subversion repository that contains empty\n directories, and checks that corresponding directories are created in the\n local Git repository with placeholder files.'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n GIT_REPO=git-svn-repo\ndiff --git a/t/t9162-git-svn-dcommit-interactive.sh b/t/t9162-git-svn-dcommit-interactive.sh\nindex e38d9fa37b5..e2aa8ed88a9 100755\n--- a/t/t9162-git-svn-dcommit-interactive.sh\n+++ b/t/t9162-git-svn-dcommit-interactive.sh\n@@ -3,6 +3,8 @@\n # Copyright (c) 2011 Frédéric Heitzmann\n \n test_description='git svn dcommit --interactive series'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9164-git-svn-dcommit-concurrent.sh b/t/t9164-git-svn-dcommit-concurrent.sh\nindex 8466269bf50..1465156072e 100755\n--- a/t/t9164-git-svn-dcommit-concurrent.sh\n+++ b/t/t9164-git-svn-dcommit-concurrent.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='concurrent git svn dcommit'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n \ndiff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh\nindex 32814e75df5..c900231079c 100755\n--- a/t/t9501-gitweb-standalone-http-status.sh\n+++ b/t/t9501-gitweb-standalone-http-status.sh\n@@ -13,6 +13,7 @@ code and message.'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./lib-gitweb.sh\n \n #\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452295","messageId":"patch-v3-16.27-0a5662a9f67-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 16/27] revisions API: have release_revisions() release \"commits\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:40Z","receivedAt":"2022-03-25T17:31:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"commits\" in the \"struct rev_info\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/revision.c b/revision.c\nindex 895bba9ae57..84b47552a3e 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2933,6 +2933,7 @@ static void release_revisions_commit_list(struct rev_info *revs)\n \n void release_revisions(struct rev_info *revs)\n {\n+\trelease_revisions_commit_list(revs);\n \tobject_array_clear(&revs->pending);\n }\n \n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452296","messageId":"patch-v3-27.27-6c6fdf696af-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 27/27] revisions API: add a TODO for diff_free(&revs->diffopt)","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:51Z","receivedAt":"2022-03-25T17:32:09Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a TODO comment indicating that we should release \"diffopt\" in\nrelease_revisions(). In a preceding commit we started releasing the\n\"pruning\" member of the same type, but handling \"diffopt\" will require\nus to untangle the \"no_free\" conditions I added in e900d494dcf (diff:\nadd an API for deferred freeing, 2021-02-11).\n\nLet's leave a TODO comment to that effect, and so that we don't forget\nrefactor code that was changed to use release_revisions() in earlier\ncommits to stop using the \"diffopt\" member after a call to\nrelease_revisions(). This works currently, but would become a logic\nerror as soon as we started freeing \"diffopt\". Doing that change now\ndoesn't harm anything, and future-proofs us against a later change to\nrelease_revisions().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n diff-lib.c  | 4 +++-\n revision.c  | 1 +\n wt-status.c | 6 ++++--\n 3 files changed, 8 insertions(+), 3 deletions(-)\n\ndiff --git a/diff-lib.c b/diff-lib.c\nindex 298265e5b54..7eb66a417aa 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -651,6 +651,7 @@ int index_differs_from(struct repository *r,\n {\n \tstruct rev_info rev;\n \tstruct setup_revision_opt opt;\n+\tunsigned has_changes;\n \n \trepo_init_revisions(r, &rev, NULL);\n \tmemset(&opt, 0, sizeof(opt));\n@@ -662,8 +663,9 @@ int index_differs_from(struct repository *r,\n \t\tdiff_flags_or(&rev.diffopt.flags, flags);\n \trev.diffopt.ita_invisible_in_index = ita_invisible_in_index;\n \trun_diff_index(&rev, 1);\n+\thas_changes = rev.diffopt.flags.has_changes;\n \trelease_revisions(&rev);\n-\treturn (rev.diffopt.flags.has_changes != 0);\n+\treturn (has_changes != 0);\n }\n \n static struct strbuf *idiff_prefix_cb(struct diff_options *opt, void *data)\ndiff --git a/revision.c b/revision.c\nindex cff1c2c6021..fa7036e4d5a 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2963,6 +2963,7 @@ void release_revisions(struct rev_info *revs)\n \tdate_mode_release(&revs->date_mode);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n+\t/* TODO (need to handle \"no_free\"): diff_free(&revs->diffopt) */\n \tdiff_free(&revs->pruning);\n \treflog_walk_info_release(revs->reflog_info);\n \trelease_revisions_topo_walk_info(revs->topo_walk_info);\ndiff --git a/wt-status.c b/wt-status.c\nindex 61e0c1022f5..102d904adcb 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -2545,8 +2545,9 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)\n \trev_info.diffopt.flags.quick = 1;\n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_files(&rev_info, 0);\n+\tresult = diff_result_code(&rev_info.diffopt, result);\n \trelease_revisions(&rev_info);\n-\treturn diff_result_code(&rev_info.diffopt, result);\n+\treturn result;\n }\n \n /**\n@@ -2578,8 +2579,9 @@ int has_uncommitted_changes(struct repository *r,\n \n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_index(&rev_info, 1);\n+\tresult = diff_result_code(&rev_info.diffopt, result);\n \trelease_revisions(&rev_info);\n-\treturn diff_result_code(&rev_info.diffopt, result);\n+\treturn result;\n }\n \n /**\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452297","messageId":"patch-v3-19.27-740abb80c57-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 19/27] revisions API: have release_revisions() release \"filter\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:43Z","receivedAt":"2022-03-25T17:32:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"filter\" in the \"struct rev_info\". This in combination with a\npreceding change to free \"cmdline\" means that we can mark another set\nof tests as passing under \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nThe \"filter\" member was added recently in ffaa137f646 (revision: put\nobject filter into struct rev_info, 2022-03-09), and this fixes leaks\nintruded in the subsequent leak 7940941de1f (pack-objects: use\nrev.filter when possible, 2022-03-09) and 105c6f14ad3 (bundle: parse\nfilter capability, 2022-03-09).\n\nThe \"builtin/pack-objects.c\" leak in 7940941de1f was effectively with\nus already, but the variable was referred to by a \"static\" file-scoped\nvariable. The \"bundle.c \" leak in 105c6f14ad3 was newly introduced\nwith the new \"filter\" feature for bundles.\n\nThe \"t5600-clone-fail-cleanup.sh\" change here to add\n\"TEST_PASSES_SANITIZE_LEAK=true\" is one of the cases where\nrun-command.c in not carrying the abort() exit code upwards would have\nhad that test passing before, but now it *actually* passes[1]. We\nshould fix the lack of 1=1 mapping of SANITIZE=leak testing to actual\nleaks some other time, but it's an existing edge case, let's just mark\nthe really-passing test as passing for now.\n\n1. https://lore.kernel.org/git/220303.86fsnz5o9w.gmgdl@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c                         | 1 +\n t/t1060-object-corruption.sh       | 1 +\n t/t2015-checkout-unborn.sh         | 1 +\n t/t4207-log-decoration-colors.sh   | 1 +\n t/t5301-sliding-window.sh          | 2 ++\n t/t5313-pack-bounds-checks.sh      | 2 ++\n t/t5316-pack-delta-depth.sh        | 2 ++\n t/t5320-delta-islands.sh           | 2 ++\n t/t5322-pack-objects-sparse.sh     | 1 +\n t/t5506-remote-groups.sh           | 1 +\n t/t5513-fetch-track.sh             | 1 +\n t/t5532-fetch-proxy.sh             | 2 ++\n t/t5600-clone-fail-cleanup.sh      | 1 +\n t/t5900-repo-selection.sh          | 2 ++\n t/t6101-rev-parse-parents.sh       | 1 +\n t/t6114-keep-packs.sh              | 2 ++\n t/t7702-repack-cyclic-alternate.sh | 2 ++\n t/t9127-git-svn-partial-rebuild.sh | 1 -\n 18 files changed, 25 insertions(+), 1 deletion(-)\n\ndiff --git a/revision.c b/revision.c\nindex d864b9e2064..55f2f4291f2 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2955,6 +2955,7 @@ void release_revisions(struct rev_info *revs)\n \trelease_revisions_commit_list(revs);\n \tobject_array_clear(&revs->pending);\n \trelease_revisions_cmdline(&revs->cmdline);\n+\tlist_objects_filter_release(&revs->filter);\n \trelease_revisions_mailmap(revs->mailmap);\n }\n \ndiff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh\nindex e8a58b15897..5b8e47e346c 100755\n--- a/t/t1060-object-corruption.sh\n+++ b/t/t1060-object-corruption.sh\n@@ -2,6 +2,7 @@\n \n test_description='see how we handle various forms of corruption'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # convert \"1234abcd\" to \".git/objects/12/34abcd\"\ndiff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh\nindex a9721215fab..9425aae6395 100755\n--- a/t/t2015-checkout-unborn.sh\n+++ b/t/t2015-checkout-unborn.sh\n@@ -4,6 +4,7 @@ test_description='checkout from unborn branch'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh\nindex b8709424981..36ac6aff1e4 100755\n--- a/t/t4207-log-decoration-colors.sh\n+++ b/t/t4207-log-decoration-colors.sh\n@@ -8,6 +8,7 @@ test_description='Test for \"git log --decorate\" colors'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t5301-sliding-window.sh b/t/t5301-sliding-window.sh\nindex 76f9798ab95..3ccaaeb3977 100755\n--- a/t/t5301-sliding-window.sh\n+++ b/t/t5301-sliding-window.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='mmap sliding window tests'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success \\\ndiff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh\nindex 535313e4dc8..cc4cfaa9d37 100755\n--- a/t/t5313-pack-bounds-checks.sh\n+++ b/t/t5313-pack-bounds-checks.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='bounds-checking of access to mmapped on-disk file formats'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n clear_base () {\ndiff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh\nindex e9045009a11..eb4ef3dda4d 100755\n--- a/t/t5316-pack-delta-depth.sh\n+++ b/t/t5316-pack-delta-depth.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='pack-objects breaks long cross-pack delta chains'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # This mirrors a repeated push setup:\ndiff --git a/t/t5320-delta-islands.sh b/t/t5320-delta-islands.sh\nindex fea92a5777f..124d47603df 100755\n--- a/t/t5320-delta-islands.sh\n+++ b/t/t5320-delta-islands.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='exercise delta islands'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # returns true iff $1 is a delta based on $2\ndiff --git a/t/t5322-pack-objects-sparse.sh b/t/t5322-pack-objects-sparse.sh\nindex d39958c066d..770695c9278 100755\n--- a/t/t5322-pack-objects-sparse.sh\n+++ b/t/t5322-pack-objects-sparse.sh\n@@ -4,6 +4,7 @@ test_description='pack-objects object selection using sparse algorithm'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup repo' '\ndiff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh\nindex 8f150c0793e..5bac03ede81 100755\n--- a/t/t5506-remote-groups.sh\n+++ b/t/t5506-remote-groups.sh\n@@ -4,6 +4,7 @@ test_description='git remote group handling'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n mark() {\ndiff --git a/t/t5513-fetch-track.sh b/t/t5513-fetch-track.sh\nindex 65d1e05bd62..c46c4dbaefc 100755\n--- a/t/t5513-fetch-track.sh\n+++ b/t/t5513-fetch-track.sh\n@@ -2,6 +2,7 @@\n \n test_description='fetch follows remote-tracking branches correctly'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh\nindex 9c2798603b4..d664912799b 100755\n--- a/t/t5532-fetch-proxy.sh\n+++ b/t/t5532-fetch-proxy.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='fetching via git:// using core.gitproxy'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup remote repo' '\ndiff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh\nindex 34b3df40275..c814afa5656 100755\n--- a/t/t5600-clone-fail-cleanup.sh\n+++ b/t/t5600-clone-fail-cleanup.sh\n@@ -13,6 +13,7 @@ Unless the directory already exists, in which case we clean up only what we\n wrote.\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n corrupt_repo () {\ndiff --git a/t/t5900-repo-selection.sh b/t/t5900-repo-selection.sh\nindex 14e59c5b3e4..a84faac242d 100755\n--- a/t/t5900-repo-selection.sh\n+++ b/t/t5900-repo-selection.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='selecting remote repo in ambiguous cases'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n reset() {\ndiff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh\nindex c571fa51797..a3a41c7a3e4 100755\n--- a/t/t6101-rev-parse-parents.sh\n+++ b/t/t6101-rev-parse-parents.sh\n@@ -8,6 +8,7 @@ test_description='Test git rev-parse with different parent options'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_cmp_rev_output () {\ndiff --git a/t/t6114-keep-packs.sh b/t/t6114-keep-packs.sh\nindex 9239d8aa46d..44246f8a63e 100755\n--- a/t/t6114-keep-packs.sh\n+++ b/t/t6114-keep-packs.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='rev-list with .keep packs'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t7702-repack-cyclic-alternate.sh b/t/t7702-repack-cyclic-alternate.sh\nindex 93b74867ac8..f3cdb98eec2 100755\n--- a/t/t7702-repack-cyclic-alternate.sh\n+++ b/t/t7702-repack-cyclic-alternate.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='repack involving cyclic alternate'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh\nindex 90b1b30dde5..97f495bd49b 100755\n--- a/t/t9127-git-svn-partial-rebuild.sh\n+++ b/t/t9127-git-svn-partial-rebuild.sh\n@@ -5,7 +5,6 @@\n \n test_description='git svn partial-rebuild tests'\n \n-TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize svnrepo' '\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452298","messageId":"patch-v3-12.27-b6acc61fb61-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 12/27] revisions API users: use release_revisions() in http-push.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:36Z","receivedAt":"2022-03-25T17:32:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In the case of cmd_main() in http-push.c we need to move the\ndeceleration of the \"struct rev-list\" into the loop over the\n\"remote_refs\" when adding a release_revisions().\n\nWe'd previously set up the \"revs\" for each remote, but would\npotentially leak memory on each one.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n http-push.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/http-push.c b/http-push.c\nindex f0c044dcf76..01e7c2ac5c8 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -1689,7 +1689,6 @@ int cmd_main(int argc, const char **argv)\n \tstruct refspec rs = REFSPEC_INIT_PUSH;\n \tstruct remote_lock *ref_lock = NULL;\n \tstruct remote_lock *info_ref_lock = NULL;\n-\tstruct rev_info revs;\n \tint delete_branch = 0;\n \tint force_delete = 0;\n \tint objects_to_send;\n@@ -1825,6 +1824,7 @@ int cmd_main(int argc, const char **argv)\n \n \tnew_refs = 0;\n \tfor (ref = remote_refs; ref; ref = ref->next) {\n+\t\tstruct rev_info revs;\n \t\tstruct strvec commit_argv = STRVEC_INIT;\n \n \t\tif (!ref->peer_ref)\n@@ -1941,6 +1941,7 @@ int cmd_main(int argc, const char **argv)\n \t\tunlock_remote(ref_lock);\n \t\tcheck_locks();\n \t\tstrvec_clear(&commit_argv);\n+\t\trelease_revisions(&revs);\n \t}\n \n \t/* Update remote server info if appropriate */\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452299","messageId":"patch-v3-03.27-58b59542c2b-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 03/27] string_list API users: use string_list_init_{no,}dup","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:27Z","receivedAt":"2022-03-25T17:34:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Follow-up on the introduction of string_list_init_nodup() and\nstring_list_init_dup() in the series merged in bd4232fac33 (Merge\nbranch 'ab/struct-init', 2021-07-16) and convert code that implicitly\nrelied on xcalloc() being equivalent to the initializer to use\nxmalloc() and string_list_init_{no,}dup() instead.\n\nIn the case of get_unmerged() in merge-recursive.c we used the\ncombination of xcalloc() and assigning \"1\" to \"strdup_strings\" to get\nwhat we'd get via string_list_init_dup(), let's use that instead.\n\nAdjacent code in cmd_format_patch() will be changed in a subsequent\ncommit, since we're changing that let's change the other in-tree\npatterns that do the same. Let's also convert a \"x == NULL\" to \"!x\"\nper our CodingGuidelines, as we need to change the \"if\" line anyway.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/log.c      | 9 ++++++---\n builtin/shortlog.c | 6 ++++--\n merge-recursive.c  | 4 ++--\n 3 files changed, 12 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex c211d66d1d0..634dc782cce 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -231,7 +231,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,\n \t}\n \n \tif (mailmap) {\n-\t\trev->mailmap = xcalloc(1, sizeof(struct string_list));\n+\t\trev->mailmap = xmalloc(sizeof(struct string_list));\n+\t\tstring_list_init_nodup(rev->mailmap);\n \t\tread_mailmap(rev->mailmap);\n \t}\n \n@@ -2173,8 +2174,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tprepare_bases(&bases, base, list, nr);\n \t}\n \n-\tif (in_reply_to || thread || cover_letter)\n-\t\trev.ref_message_ids = xcalloc(1, sizeof(struct string_list));\n+\tif (in_reply_to || thread || cover_letter) {\n+\t\trev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));\n+\t\tstring_list_init_nodup(rev.ref_message_ids);\n+\t}\n \tif (in_reply_to) {\n \t\tconst char *msgid = clean_message_id(in_reply_to);\n \t\tstring_list_append(rev.ref_message_ids, msgid);\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex 26c5c0cf935..fcde07c9367 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -81,8 +81,10 @@ static void insert_one_record(struct shortlog *log,\n \t\tformat_subject(&subject, oneline, \" \");\n \t\tbuffer = strbuf_detach(&subject, NULL);\n \n-\t\tif (item->util == NULL)\n-\t\t\titem->util = xcalloc(1, sizeof(struct string_list));\n+\t\tif (!item->util) {\n+\t\t\titem->util = xmalloc(sizeof(struct string_list));\n+\t\t\tstring_list_init_nodup(item->util);\n+\t\t}\n \t\tstring_list_append(item->util, buffer);\n \t}\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 1ee6364e8b1..32bbba5fbb1 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -522,10 +522,10 @@ static struct stage_data *insert_stage_data(struct repository *r,\n  */\n static struct string_list *get_unmerged(struct index_state *istate)\n {\n-\tstruct string_list *unmerged = xcalloc(1, sizeof(struct string_list));\n+\tstruct string_list *unmerged = xmalloc(sizeof(struct string_list));\n \tint i;\n \n-\tunmerged->strdup_strings = 1;\n+\tstring_list_init_dup(unmerged);\n \n \t/* TODO: audit for interaction with sparse-index. */\n \tensure_full_index(istate);\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452300","messageId":"patch-v3-05.27-12fb45f02c1-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 05/27] revision.[ch]: split freeing of revs->commit into a function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:29Z","receivedAt":"2022-03-25T17:35:00Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Move the existing code for invoking free_commit_list() and setting\nrevs->commits to NULL into a new release_revisions_commit_list()\nfunction. This will be used as part of a general free()-ing mechanism\nfor \"struct rev_info\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 13 +++++++++----\n 1 file changed, 9 insertions(+), 4 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex 2646b78990e..ef45762bf21 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2923,6 +2923,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \treturn left;\n }\n \n+static void release_revisions_commit_list(struct rev_info *revs)\n+{\n+\tif (revs->commits) {\n+\t\tfree_commit_list(revs->commits);\n+\t\trevs->commits = NULL;\n+\t}\n+}\n+\n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n {\n \tstruct commit_list *l = xcalloc(1, sizeof(*l));\n@@ -4080,10 +4088,7 @@ static void create_boundary_commit_list(struct rev_info *revs)\n \t * boundary commits anyway.  (This is what the code has always\n \t * done.)\n \t */\n-\tif (revs->commits) {\n-\t\tfree_commit_list(revs->commits);\n-\t\trevs->commits = NULL;\n-\t}\n+\trelease_revisions_commit_list(revs);\n \n \t/*\n \t * Put all of the actual boundary commits from revs->boundary_commits\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452301","messageId":"patch-v3-10.27-9da5c5f5f5d-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 10/27] stash: always have the owner of \"stash_info\" free it","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:34Z","receivedAt":"2022-03-25T17:37:21Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the initialization of the \"revision\" member of \"struct\nstash_info\" to be initialized vi a macro, and more importantly that\nthat initializing function be tasked to free it, usually via a \"goto\ncleanup\" pattern.\n\nDespite the \"revision\" name (and the topic of the series containing\nthis commit) the \"stash info\" has nothing to do with the \"struct\nrev_info\". I'm making this change because in the subsequent commit\nwhen we do want to free the \"struct rev_info\" via a \"goto cleanup\"\npattern we'd otherwise free() uninitialized memory in some cases, as\nwe only strbuf_init() the string in get_stash_info().\n\nSo while it's the smallest possible change, let's convert all users of\nthis pattern in the file while we're at it.\n\nA good follow-up to this change would be to change all the \"ret = -1;\ngoto done;\" in this file to instead use a \"goto cleanup\", and\ninitialize \"int ret = -1\" at the start of the relevant functions. That\nwould allow us to drop a lot of needless brace verbosity on two-line\n\"if\" statements, but let's leave that alone for now.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/stash.c | 70 ++++++++++++++++++++++++++-----------------------\n 1 file changed, 37 insertions(+), 33 deletions(-)\n\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex ad74624c2f7..891572d807c 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -116,6 +116,10 @@ struct stash_info {\n \tint has_u;\n };\n \n+#define STASH_INFO_INIT { \\\n+\t.revision = STRBUF_INIT, \\\n+}\n+\n static void free_stash_info(struct stash_info *info)\n {\n \tstrbuf_release(&info->revision);\n@@ -157,10 +161,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)\n \tif (argc == 1)\n \t\tcommit = argv[0];\n \n-\tstrbuf_init(&info->revision, 0);\n \tif (!commit) {\n \t\tif (!ref_exists(ref_stash)) {\n-\t\t\tfree_stash_info(info);\n \t\t\tfprintf_ln(stderr, _(\"No stash entries found.\"));\n \t\t\treturn -1;\n \t\t}\n@@ -174,11 +176,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)\n \n \trevision = info->revision.buf;\n \n-\tif (get_oid(revision, &info->w_commit)) {\n-\t\terror(_(\"%s is not a valid reference\"), revision);\n-\t\tfree_stash_info(info);\n-\t\treturn -1;\n-\t}\n+\tif (get_oid(revision, &info->w_commit))\n+\t\treturn error(_(\"%s is not a valid reference\"), revision);\n \n \tassert_stash_like(info, revision);\n \n@@ -197,7 +196,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)\n \t\tinfo->is_stash_ref = !strcmp(expanded_ref, ref_stash);\n \t\tbreak;\n \tdefault: /* Invalid or ambiguous */\n-\t\tfree_stash_info(info);\n+\t\tbreak;\n \t}\n \n \tfree(expanded_ref);\n@@ -598,10 +597,10 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,\n \n static int apply_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret;\n+\tint ret = -1;\n \tint quiet = 0;\n \tint index = 0;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n \t\tOPT_BOOL(0, \"index\", &index,\n@@ -613,9 +612,10 @@ static int apply_stash(int argc, const char **argv, const char *prefix)\n \t\t\t     git_stash_apply_usage, 0);\n \n \tif (get_stash_info(&info, argc, argv))\n-\t\treturn -1;\n+\t\tgoto cleanup;\n \n \tret = do_apply_stash(prefix, &info, index, quiet);\n+cleanup:\n \tfree_stash_info(&info);\n \treturn ret;\n }\n@@ -662,9 +662,9 @@ static void assert_stash_ref(struct stash_info *info)\n \n static int drop_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret;\n+\tint ret = -1;\n \tint quiet = 0;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n \t\tOPT_END()\n@@ -674,21 +674,22 @@ static int drop_stash(int argc, const char **argv, const char *prefix)\n \t\t\t     git_stash_drop_usage, 0);\n \n \tif (get_stash_info(&info, argc, argv))\n-\t\treturn -1;\n+\t\tgoto cleanup;\n \n \tassert_stash_ref(&info);\n \n \tret = do_drop_stash(&info, quiet);\n+cleanup:\n \tfree_stash_info(&info);\n \treturn ret;\n }\n \n static int pop_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret;\n+\tint ret = -1;\n \tint index = 0;\n \tint quiet = 0;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n \t\tOPT_BOOL(0, \"index\", &index,\n@@ -700,7 +701,7 @@ static int pop_stash(int argc, const char **argv, const char *prefix)\n \t\t\t     git_stash_pop_usage, 0);\n \n \tif (get_stash_info(&info, argc, argv))\n-\t\treturn -1;\n+\t\tgoto cleanup;\n \n \tassert_stash_ref(&info);\n \tif ((ret = do_apply_stash(prefix, &info, index, quiet)))\n@@ -709,15 +710,16 @@ static int pop_stash(int argc, const char **argv, const char *prefix)\n \telse\n \t\tret = do_drop_stash(&info, quiet);\n \n+cleanup:\n \tfree_stash_info(&info);\n \treturn ret;\n }\n \n static int branch_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret;\n+\tint ret = -1;\n \tconst char *branch = NULL;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tstruct option options[] = {\n \t\tOPT_END()\n@@ -734,7 +736,7 @@ static int branch_stash(int argc, const char **argv, const char *prefix)\n \tbranch = argv[0];\n \n \tif (get_stash_info(&info, argc - 1, argv + 1))\n-\t\treturn -1;\n+\t\tgoto cleanup;\n \n \tcp.git_cmd = 1;\n \tstrvec_pushl(&cp.args, \"checkout\", \"-b\", NULL);\n@@ -746,8 +748,8 @@ static int branch_stash(int argc, const char **argv, const char *prefix)\n \tif (!ret && info.is_stash_ref)\n \t\tret = do_drop_stash(&info, 0);\n \n+cleanup:\n \tfree_stash_info(&info);\n-\n \treturn ret;\n }\n \n@@ -825,8 +827,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op\n static int show_stash(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n-\tint ret = 0;\n-\tstruct stash_info info;\n+\tint ret = -1;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct rev_info rev;\n \tstruct strvec stash_args = STRVEC_INIT;\n \tstruct strvec revision_args = STRVEC_INIT;\n@@ -861,10 +863,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \t\t\tstrvec_push(&revision_args, argv[i]);\n \t}\n \n-\tret = get_stash_info(&info, stash_args.nr, stash_args.v);\n-\tstrvec_clear(&stash_args);\n-\tif (ret)\n-\t\treturn -1;\n+\tif (get_stash_info(&info, stash_args.nr, stash_args.v))\n+\t\tgoto cleanup;\n \n \t/*\n \t * The config settings are applied only if there are not passed\n@@ -878,8 +878,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \t\t\trev.diffopt.output_format |= DIFF_FORMAT_PATCH;\n \n \t\tif (!show_stat && !show_patch) {\n-\t\t\tfree_stash_info(&info);\n-\t\t\treturn 0;\n+\t\t\tret = 0;\n+\t\t\tgoto cleanup;\n \t\t}\n \t}\n \n@@ -912,8 +912,11 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \t}\n \tlog_tree_diff_flush(&rev);\n \n+\tret = diff_result_code(&rev.diffopt, 0);;\n+cleanup:\n+\tstrvec_clear(&stash_args);\n \tfree_stash_info(&info);\n-\treturn diff_result_code(&rev.diffopt, 0);\n+\treturn ret;\n }\n \n static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,\n@@ -1409,9 +1412,9 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b\n \n static int create_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret = 0;\n+\tint ret;\n \tstruct strbuf stash_msg_buf = STRBUF_INIT;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct pathspec ps;\n \n \t/* Starting with argv[1], since argv[0] is \"create\" */\n@@ -1426,6 +1429,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)\n \tif (!ret)\n \t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n \n+\tfree_stash_info(&info);\n \tstrbuf_release(&stash_msg_buf);\n \treturn ret;\n }\n@@ -1434,7 +1438,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q\n \t\t\t int keep_index, int patch_mode, int include_untracked, int only_staged)\n {\n \tint ret = 0;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct strbuf patch = STRBUF_INIT;\n \tstruct strbuf stash_msg_buf = STRBUF_INIT;\n \tstruct strbuf untracked_files = STRBUF_INIT;\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452352","messageId":"patch-v3-02.27-515bacc8f27-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 02/27] blame: use \"goto cleanup\" for cleanup_scoreboard()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:26Z","receivedAt":"2022-03-25T19:34:45Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Amend a freeing pattern added in 0906ac2b54b (blame: use changed-path\nBloom filters, 2020-04-16) to use a \"goto cleanup\", so that we can be\nsure that we call cleanup_scoreboard().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/blame.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 8d15b68afc9..885b381ab83 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1167,7 +1167,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tif (!incremental)\n \t\tsetup_pager();\n \telse\n-\t\treturn 0;\n+\t\tgoto cleanup;\n \n \tblame_sort_final(&sb);\n \n@@ -1201,6 +1201,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\tprintf(\"num commits: %d\\n\", sb.num_commits);\n \t}\n \n+cleanup:\n \tcleanup_scoreboard(&sb);\n \treturn 0;\n }\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452365","messageId":"patch-v3-15.27-f8e0eb52957-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 15/27] revisions API users: use release_revisions() for \"prune_data\" users","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:39Z","receivedAt":"2022-03-25T19:39:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use release_revisions() for users of \"struct rev_list\" that reach into\nthe \"struct rev_info\" and clear the \"prune_data\" already.\n\nIn a subsequent commit we'll teach release_revisions() to clear this\nitself, but in the meantime let's invoke release_revisions() here to\nclear anything else we may have missed, and for reasons of having\nconsistent boilerplate.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/add.c   | 1 +\n builtin/stash.c | 1 +\n diff-lib.c      | 1 +\n wt-status.c     | 1 +\n 4 files changed, 4 insertions(+)\n\ndiff --git a/builtin/add.c b/builtin/add.c\nindex f507d2191cd..115a26ea633 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -143,6 +143,7 @@ int add_files_to_cache(const char *prefix,\n \trev.max_count = 0; /* do not compare unmerged paths with stage #2 */\n \trun_diff_files(&rev, DIFF_RACY_IS_MODIFIED);\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n \treturn !!data.add_errors;\n }\n \ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 76d162387e2..a6ee030d4bd 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1060,6 +1060,7 @@ static int check_changes_tracked_files(const struct pathspec *ps)\n \n done:\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n \treturn ret;\n }\n \ndiff --git a/diff-lib.c b/diff-lib.c\nindex d6800274bd5..0f16281253f 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -642,6 +642,7 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)\n \tif (diff_cache(&revs, tree_oid, NULL, 1))\n \t\texit(128);\n \tclear_pathspec(&revs.prune_data);\n+\trelease_revisions(&revs);\n \treturn 0;\n }\n \ndiff --git a/wt-status.c b/wt-status.c\nindex f9100621375..a14fad1e03a 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -617,6 +617,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)\n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_files(&rev, 0);\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n }\n \n static void wt_status_collect_changes_index(struct wt_status *s)\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452366","messageId":"patch-v3-24.27-006fa1fcc34-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:48Z","receivedAt":"2022-03-25T19:39:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Call diff_free() on the \"pruning\" member of \"struct rev_info\".  Doing\nso makes several tests pass under SANITIZE=leak.\n\nThis was also the last missing piece that allows us to remove the\nUNLEAK() in \"cmd_diff\" and \"cmd_diff_index\", which allows us to use\nthose commands as a canary for general leaks in the revisions API. See\n[1] for further rationale, and 886e1084d78 (builtin/: add UNLEAKs,\n2017-10-01) for the commit that added the UNLEAK() there.\n\n1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/diff-index.c                | 1 -\n builtin/diff.c                      | 1 -\n revision.c                          | 1 +\n t/t1001-read-tree-m-2way.sh         | 1 +\n t/t1002-read-tree-m-u-2way.sh       | 1 +\n t/t2200-add-update.sh               | 1 +\n t/t4039-diff-assume-unchanged.sh    | 1 +\n t/t4206-log-follow-harder-copies.sh | 2 ++\n t/t6131-pathspec-icase.sh           | 2 ++\n 9 files changed, 9 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/diff-index.c b/builtin/diff-index.c\nindex 3a83183c312..7d158af6b6d 100644\n--- a/builtin/diff-index.c\n+++ b/builtin/diff-index.c\n@@ -70,7 +70,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)\n \t\treturn -1;\n \t}\n \tresult = run_diff_index(&rev, option);\n-\tUNLEAK(rev);\n \tresult = diff_result_code(&rev.diffopt, result);\n \trelease_revisions(&rev);\n \treturn result;\ndiff --git a/builtin/diff.c b/builtin/diff.c\nindex dd48336da56..f539132ac68 100644\n--- a/builtin/diff.c\n+++ b/builtin/diff.c\n@@ -594,7 +594,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \tresult = diff_result_code(&rev.diffopt, result);\n \tif (1 < rev.diffopt.skip_stat_unmatch)\n \t\trefresh_index_quietly();\n-\tUNLEAK(rev);\n \trelease_revisions(&rev);\n \tUNLEAK(ent);\n \tUNLEAK(blob);\ndiff --git a/revision.c b/revision.c\nindex 380e6d9fe43..9ac06e4d285 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2960,6 +2960,7 @@ void release_revisions(struct rev_info *revs)\n \tclear_pathspec(&revs->prune_data);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n+\tdiff_free(&revs->pruning);\n \treflog_walk_info_release(revs->reflog_info);\n }\n \ndiff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh\nindex 0710b1fb1e9..516a6112fdc 100755\n--- a/t/t1001-read-tree-m-2way.sh\n+++ b/t/t1001-read-tree-m-2way.sh\n@@ -21,6 +21,7 @@ In the test, these paths are used:\n \tyomin   - not in H or M\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-read-tree.sh\n \ndiff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh\nindex 46cbd5514a6..bd5313caec9 100755\n--- a/t/t1002-read-tree-m-u-2way.sh\n+++ b/t/t1002-read-tree-m-u-2way.sh\n@@ -9,6 +9,7 @@ This is identical to t1001, but uses -u to update the work tree as well.\n \n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-read-tree.sh\n \ndiff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh\nindex 0c38f8e3569..be394f1131a 100755\n--- a/t/t2200-add-update.sh\n+++ b/t/t2200-add-update.sh\n@@ -14,6 +14,7 @@ only the updates to dir/sub.\n Also tested are \"git add -u\" without limiting, and \"git add -u\"\n without contents changes, and other conditions'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh\nindex 0eb0314a8b3..78090e6852d 100755\n--- a/t/t4039-diff-assume-unchanged.sh\n+++ b/t/t4039-diff-assume-unchanged.sh\n@@ -2,6 +2,7 @@\n \n test_description='diff with assume-unchanged entries'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # external diff has been tested in t4020-diff-external.sh\ndiff --git a/t/t4206-log-follow-harder-copies.sh b/t/t4206-log-follow-harder-copies.sh\nindex 4871a5dc92f..33ecf82c7f9 100755\n--- a/t/t4206-log-follow-harder-copies.sh\n+++ b/t/t4206-log-follow-harder-copies.sh\n@@ -6,6 +6,8 @@\n test_description='Test --follow should always find copies hard in git log.\n \n '\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-diff.sh\n \ndiff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh\nindex 39fc3f6769b..770cce026cb 100755\n--- a/t/t6131-pathspec-icase.sh\n+++ b/t/t6131-pathspec-icase.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='test case insensitive pathspec limiting'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n if test_have_prereq CASE_INSENSITIVE_FS\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452384","messageId":"patch-v3-04.27-71495dfd073-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 04/27] format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:28Z","receivedAt":"2022-03-25T19:51:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix two memory leaks in \"struct rev_info\" by freeing that memory in\ncmd_format_patch(). These two are unusual special-cases in being in\nthe \"struct rev_info\", but not being \"owned\" by the code in\nrevision.c. I.e. they're members of the struct so that this code in\n\"builtin/log.c\" can pass information code in log-tree.c.\n\nSee 20ff06805c6 (format-patch: resurrect extra headers from config,\n2006-06-02) and d1566f7883f (git-format-patch: Make the second and\nsubsequent mails replies to the first, 2006-07-14) for the initial\nintroduction of \"extra_headers\" and \"ref_message_ids\".\n\nWe can count on repo_init_revisions() memset()-ing this data to 0\nhowever, so we can count on it being either NULL or something we\nallocated. In the case of \"extra_headers\" let's add a local \"char *\"\nvariable to hold it, to avoid the eventual cast from \"const char *\"\nwhen we free() it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/log.c | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 634dc782cce..6f9928fabfe 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1747,6 +1747,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tstruct commit *commit;\n \tstruct commit **list = NULL;\n \tstruct rev_info rev;\n+\tchar *to_free = NULL;\n \tstruct setup_revision_opt s_r_opt;\n \tint nr = 0, total, i;\n \tint use_stdout = 0;\n@@ -1947,7 +1948,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tstrbuf_addch(&buf, '\\n');\n \t}\n \n-\trev.extra_headers = strbuf_detach(&buf, NULL);\n+\trev.extra_headers = to_free = strbuf_detach(&buf, NULL);\n \n \tif (from) {\n \t\tif (split_ident_line(&rev.from_ident, from, strlen(from)))\n@@ -2284,6 +2285,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tstrbuf_release(&rdiff1);\n \tstrbuf_release(&rdiff2);\n \tstrbuf_release(&rdiff_title);\n+\tfree(to_free);\n+\tif (rev.ref_message_ids)\n+\t\tstring_list_clear(rev.ref_message_ids, 0);\n+\tfree(rev.ref_message_ids);\n \tUNLEAK(rev);\n \treturn 0;\n }\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452385","messageId":"patch-v3-11.27-da4fcd04345-20220325T171340Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v3 11/27] revisions API users: add \"goto cleanup\" for release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-25T17:18:35Z","receivedAt":"2022-03-25T19:54:27Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a release_revisions() to various users of \"struct rev_info\" which\nrequires a minor refactoring to a \"goto cleanup\" pattern to use that\nfunction.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/diff-files.c        |  8 ++++++--\n builtin/rev-list.c          | 19 ++++++++++++-------\n builtin/stash.c             |  1 +\n builtin/submodule--helper.c | 10 +++++++---\n sequencer.c                 | 23 ++++++++++++++++-------\n t/helper/test-fast-rebase.c | 18 +++++++++++++-----\n 6 files changed, 55 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin/diff-files.c b/builtin/diff-files.c\nindex 70103c40952..2bfaf9ba7ae 100644\n--- a/builtin/diff-files.c\n+++ b/builtin/diff-files.c\n@@ -77,8 +77,12 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)\n \n \tif (read_cache_preload(&rev.diffopt.pathspec) < 0) {\n \t\tperror(\"read_cache_preload\");\n-\t\treturn -1;\n+\t\tresult = -1;\n+\t\tgoto cleanup;\n \t}\n+cleanup:\n \tresult = run_diff_files(&rev, options);\n-\treturn diff_result_code(&rev.diffopt, result);\n+\tresult = diff_result_code(&rev.diffopt, result);\n+\trelease_revisions(&rev);\n+\treturn result;\n }\ndiff --git a/builtin/rev-list.c b/builtin/rev-list.c\nindex 572da1472e5..2f0b6c759e4 100644\n--- a/builtin/rev-list.c\n+++ b/builtin/rev-list.c\n@@ -502,6 +502,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \tint use_bitmap_index = 0;\n \tint filter_provided_objects = 0;\n \tconst char *show_progress = NULL;\n+\tint ret = 0;\n \n \tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n \t\tusage(rev_list_usage);\n@@ -585,7 +586,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \t\t}\n \t\tif (!strcmp(arg, \"--test-bitmap\")) {\n \t\t\ttest_bitmap_walk(&revs);\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t\t}\n \t\tif (skip_prefix(arg, \"--progress=\", &arg)) {\n \t\t\tshow_progress = arg;\n@@ -674,11 +675,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \n \tif (use_bitmap_index) {\n \t\tif (!try_bitmap_count(&revs, filter_provided_objects))\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t\tif (!try_bitmap_disk_usage(&revs, filter_provided_objects))\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t\tif (!try_bitmap_traversal(&revs, filter_provided_objects))\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t}\n \n \tif (prepare_revision_walk(&revs))\n@@ -698,8 +699,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \n \t\tfind_bisection(&revs.commits, &reaches, &all, bisect_flags);\n \n-\t\tif (bisect_show_vars)\n-\t\t\treturn show_bisect_vars(&info, reaches, all);\n+\t\tif (bisect_show_vars) {\n+\t\t\tret = show_bisect_vars(&info, reaches, all);\n+\t\t\tgoto cleanup;\n+\t\t}\n \t}\n \n \tif (filter_provided_objects) {\n@@ -754,5 +757,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \tif (show_disk_usage)\n \t\tprintf(\"%\"PRIuMAX\"\\n\", (uintmax_t)total_disk_usage);\n \n-\treturn 0;\n+cleanup:\n+\trelease_revisions(&revs);\n+\treturn ret;\n }\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 891572d807c..76d162387e2 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -916,6 +916,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n cleanup:\n \tstrvec_clear(&stash_args);\n \tfree_stash_info(&info);\n+\trelease_revisions(&rev);\n \treturn ret;\n }\n \ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex d1b656c0909..5d4c0b62ba9 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1233,6 +1233,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \tstruct strvec diff_args = STRVEC_INIT;\n \tstruct rev_info rev;\n \tstruct module_cb_list list = MODULE_CB_LIST_INIT;\n+\tint ret = 0;\n \n \tstrvec_push(&diff_args, get_diff_cmd(diff_cmd));\n \tif (info->cached)\n@@ -1258,11 +1259,13 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \t\t\tsetup_work_tree();\n \t\tif (read_cache_preload(&rev.diffopt.pathspec) < 0) {\n \t\t\tperror(\"read_cache_preload\");\n-\t\t\treturn -1;\n+\t\t\tret = -1;\n+\t\t\tgoto cleanup;\n \t\t}\n \t} else if (read_cache() < 0) {\n \t\tperror(\"read_cache\");\n-\t\treturn -1;\n+\t\tret = -1;\n+\t\tgoto cleanup;\n \t}\n \n \tif (diff_cmd == DIFF_INDEX)\n@@ -1270,9 +1273,10 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \telse\n \t\trun_diff_files(&rev, 0);\n \tprepare_submodule_summary(info, &list);\n+cleanup:\n \tstrvec_clear(&diff_args);\n \trelease_revisions(&rev);\n-\treturn 0;\n+\treturn ret;\n }\n \n static int module_summary(int argc, const char **argv, const char *prefix)\ndiff --git a/sequencer.c b/sequencer.c\nindex f9d7acd1065..41ae5e25278 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -5354,6 +5354,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \tint rebase_merges = flags & TODO_LIST_REBASE_MERGES;\n \tint reapply_cherry_picks = flags & TODO_LIST_REAPPLY_CHERRY_PICKS;\n \tint skipped_commit = 0;\n+\tint ret = 0;\n \n \trepo_init_revisions(r, &revs, NULL);\n \trevs.verbose_header = 1;\n@@ -5377,14 +5378,20 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \tpp.fmt = revs.commit_format;\n \tpp.output_encoding = get_log_output_encoding();\n \n-\tif (setup_revisions(argc, argv, &revs, NULL) > 1)\n-\t\treturn error(_(\"make_script: unhandled options\"));\n+\tif (setup_revisions(argc, argv, &revs, NULL) > 1) {\n+\t\tret = error(_(\"make_script: unhandled options\"));\n+\t\tgoto cleanup;\n+\t}\n \n-\tif (prepare_revision_walk(&revs) < 0)\n-\t\treturn error(_(\"make_script: error preparing revisions\"));\n+\tif (prepare_revision_walk(&revs) < 0) {\n+\t\tret = error(_(\"make_script: error preparing revisions\"));\n+\t\tgoto cleanup;\n+\t}\n \n-\tif (rebase_merges)\n-\t\treturn make_script_with_merges(&pp, &revs, out, flags);\n+\tif (rebase_merges) {\n+\t\tret = make_script_with_merges(&pp, &revs, out, flags);\n+\t\tgoto cleanup;\n+\t}\n \n \twhile ((commit = get_revision(&revs))) {\n \t\tint is_empty = is_original_commit_empty(commit);\n@@ -5408,7 +5415,9 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \tif (skipped_commit)\n \t\tadvise_if_enabled(ADVICE_SKIPPED_CHERRY_PICKS,\n \t\t\t\t  _(\"use --reapply-cherry-picks to include skipped commits\"));\n-\treturn 0;\n+cleanup:\n+\trelease_revisions(&revs);\n+\treturn ret;\n }\n \n /*\ndiff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c\nindex 993b90eaedd..4e5553e2024 100644\n--- a/t/helper/test-fast-rebase.c\n+++ b/t/helper/test-fast-rebase.c\n@@ -99,6 +99,7 @@ int cmd__fast_rebase(int argc, const char **argv)\n \tstruct merge_result result;\n \tstruct strbuf reflog_msg = STRBUF_INIT;\n \tstruct strbuf branch_name = STRBUF_INIT;\n+\tint ret = 0;\n \n \t/*\n \t * test-tool stuff doesn't set up the git directory by default; need to\n@@ -137,13 +138,17 @@ int cmd__fast_rebase(int argc, const char **argv)\n \trevs.topo_order = 1;\n \tstrvec_pushl(&rev_walk_args, \"\", argv[4], \"--not\", argv[3], NULL);\n \n-\tif (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1)\n-\t\treturn error(_(\"unhandled options\"));\n+\tif (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) {\n+\t\tret = error(_(\"unhandled options\"));\n+\t\tgoto cleanup;\n+\t}\n \n \tstrvec_clear(&rev_walk_args);\n \n-\tif (prepare_revision_walk(&revs) < 0)\n-\t\treturn error(_(\"error preparing revisions\"));\n+\tif (prepare_revision_walk(&revs) < 0) {\n+\t\tret = error(_(\"error preparing revisions\"));\n+\t\tgoto cleanup;\n+\t}\n \n \tinit_merge_options(&merge_opt, the_repository);\n \tmemset(&result, 0, sizeof(result));\n@@ -220,7 +225,10 @@ int cmd__fast_rebase(int argc, const char **argv)\n \t\t\t       COMMIT_LOCK | SKIP_IF_UNCHANGED))\n \t\tdie(_(\"unable to write %s\"), get_index_file());\n \n+\tret = (result.clean == 0);\n+cleanup:\n \tstrbuf_release(&reflog_msg);\n \tstrbuf_release(&branch_name);\n-\treturn (result.clean == 0);\n+\trelease_revisions(&revs);\n+\treturn ret;\n }\n-- \n2.35.1.1509.ge4eeb5bd39e\n\n"},{"id":"452388","messageId":"xmqqtubl93n3.fsf@gitster.g","threadId":"57525","inReplyTo":"patch-v3-08.27-f8a9443fe6f-20220325T171340Z-avarab@gmail.com","subject":"Re: [PATCH v3 08/27] revisions API users: add \"goto cleanup\" for \"rev_info\" early exit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-25T20:30:08Z","receivedAt":"2022-03-25T20:30:15Z","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> It would be a lot cleaner to be able to initialize \"struct rev_info\"\n> with \"{ 0 }\" here, or if a \"REV_INFO_INIT\" existed, we'll hopefully\n> get around to making the initialization easier in the future (now it\n> can't be done via a macro).\n\nIf \"struct rev_info\" can be initialized with \"{ 0 }\" here, i.e.\n\n-\tstruct rev_info rev;\n+\tstruct rev_info rev = { 0 };\n\nto give us a valid solution, why wouldn't you be able to do\n\n+#define REV_INFO_INIT { 0 }\n\nelsewhere in a common *.h file, and then\n\n-\tstruct rev_info rev;\n+\tstruct rev_info rev = REV_INFO_INIT;\n\nto make the fact that \"rev\" is initialized (and ready to be handed\nto the releaser) even more explicit?  It's like arguing against\nfixing a code like this:\n\n\tstruct char *pointer;\n\t...\n\tif (condition)\n\t\tpointer = malloc(...);\n\t...\n        /* pointer leaks */\n \nby initializing\n\n\tstruct char *pointer = NULL;\n\t...\n\tif (condition)\n\t\tpointer = malloc(...);\n\t...\n        free(pointer);\n\nbecause for some reason you are against the macro NULL but you are\nwilling to spell it out as \"0\" (without double-quotes)?\n\nPuzzled.\n"},{"id":"452397","messageId":"220326.86ee2pleua.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"xmqqtubl93n3.fsf@gitster.g","subject":"Re: [PATCH v3 08/27] revisions API users: add \"goto cleanup\" for \"rev_info\" early exit","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-26T00:37:50Z","receivedAt":"2022-03-26T00:47:32Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Mar 25 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>\n>> It would be a lot cleaner to be able to initialize \"struct rev_info\"\n>> with \"{ 0 }\" here, or if a \"REV_INFO_INIT\" existed, we'll hopefully\n>> get around to making the initialization easier in the future (now it\n>> can't be done via a macro).\n>\n> If \"struct rev_info\" can be initialized with \"{ 0 }\" here, i.e.\n>\n> -\tstruct rev_info rev;\n> +\tstruct rev_info rev = { 0 };\n>\n> to give us a valid solution, why wouldn't you be able to do\n>\n> +#define REV_INFO_INIT { 0 }\n>\n> elsewhere in a common *.h file, and then\n>\n> -\tstruct rev_info rev;\n> +\tstruct rev_info rev = REV_INFO_INIT;\n>\n> to make the fact that \"rev\" is initialized (and ready to be handed\n> to the releaser) even more explicit?  It's like arguing against\n> fixing a code like this:\n>\n> \tstruct char *pointer;\n> \t...\n> \tif (condition)\n> \t\tpointer = malloc(...);\n> \t...\n>         /* pointer leaks */\n>  \n> by initializing\n>\n> \tstruct char *pointer = NULL;\n> \t...\n> \tif (condition)\n> \t\tpointer = malloc(...);\n> \t...\n>         free(pointer);\n>\n> because for some reason you are against the macro NULL but you are\n> willing to spell it out as \"0\" (without double-quotes)?\n\nI was fine with having it be { 0 } and have \"release\" functions\neverywhere assume that memset-ing structures to zero makes them safe to\npass to release(), but my reading of your v2 feedback was that you\ndidn't like making that assumption, so I changed it in the v3 so we\nwouldn't assume that.\n\nI then took that REV_INFO_INIT suggestion to mean that this series\ncould/should be predicated on some cross-codebase refactoring to change\nall of the \"struct rev_info\" initialization, which would be a rather\nlarge digression.\n\nBecause I don't see how it makes any sense to have a REV_INFO_INIT if it\ndoesn't actually give you an init'd \"struct rev_info\" that's ready for\nuse. I.e. if you still need to call repo_init_revisions() it's just a\nmisleading interface.\n\nI also think the cosmetic issues of initialization here really aren't\nimportant at all, fixing these leaks is. So whatever idioms you're OK\nwith us using here are fine by me, as long as I can be made to clearly\nunderstand what you want for a re-roll :)\n\nVery preferably something that doesn't require refactoring the entirety\nof \"struct rev_info\" init across the codebase...\n"},{"id":"452405","messageId":"xmqqee2p70c1.fsf@gitster.g","threadId":"57525","inReplyTo":"220326.86ee2pleua.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v3 08/27] revisions API users: add \"goto cleanup\" for \"rev_info\" early exit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-26T05:24:30Z","receivedAt":"2022-03-26T05:45:36Z","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> Because I don't see how it makes any sense to have a REV_INFO_INIT if it\n> doesn't actually give you an init'd \"struct rev_info\" that's ready for\n> use. I.e. if you still need to call repo_init_revisions() it's just a\n> misleading interface.\n\nYou can say\n\n\tstruct something *foo = NULL;\n\n\tif (some condition)\n\t\tfoo = alloc_and_init_foo();\n\n\t...\n\n\tfree_and_destruct(foo);\n\nand it is correct that \"initialize with NULL\" alone would not let\nyou use the thing as full fledged 'foo', but you can still safely\npass it to free_and_destruct() (or if \"something\" does not hold\nexternal resources, it could just be free()).  A flow like this:\n\n\tstruct rev_info revs = REV_INFO_INIT;\n\n\tif (!some condition)\n\t\tgoto leave;\n\tinit(&revs);\n\t... use revs ...\nleave:\n        release(&revs);\n\nwould exactly be the same thing.\n\nIn other words, you say \"I do not see how it makes any sense\" but to\nme it looks more like what does not make sense is your argument\nagainst what was suggested.\n\n"},{"id":"452489","messageId":"3bb95e8b-4977-ddca-225c-5afe28d8ac20@github.com","threadId":"57525","inReplyTo":"xmqqee2p70c1.fsf@gitster.g","subject":"Re: [PATCH v3 08/27] revisions API users: add \"goto cleanup\" for \"rev_info\" early exit","fromName":"Derrick Stolee","fromEmail":"derrickstolee@github.com","sentAt":"2022-03-28T17:55:07Z","receivedAt":"2022-03-28T17:56:06Z","isPatch":true,"sender":{"key":"stolee@gmail.com","avatar":"https://avatars.githubusercontent.com/u/570044?v=4"},"body":"On 3/26/2022 1:24 AM, Junio C Hamano wrote:\n> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n> \n>> Because I don't see how it makes any sense to have a REV_INFO_INIT if it\n>> doesn't actually give you an init'd \"struct rev_info\" that's ready for\n>> use. I.e. if you still need to call repo_init_revisions() it's just a\n>> misleading interface.\n> \n> You can say\n> \n> \tstruct something *foo = NULL;\n> \n> \tif (some condition)\n> \t\tfoo = alloc_and_init_foo();\n> \n> \t...\n> \n> \tfree_and_destruct(foo);\n> \n> and it is correct that \"initialize with NULL\" alone would not let\n> you use the thing as full fledged 'foo', but you can still safely\n> pass it to free_and_destruct() (or if \"something\" does not hold\n> external resources, it could just be free()).  A flow like this:\n> \n> \tstruct rev_info revs = REV_INFO_INIT;\n> \n> \tif (!some condition)\n> \t\tgoto leave;\n> \tinit(&revs);\n> \t... use revs ...\n> leave:\n>         release(&revs);\n> \n> would exactly be the same thing.\n> \n> In other words, you say \"I do not see how it makes any sense\" but to\n> me it looks more like what does not make sense is your argument\n> against what was suggested.\n\nÆvar has stated in multiple threads that he prefers to not\ninitialize data so that static analysis tools can detect a\nuse-before-initialization of specific members.\n\nHowever, now that we are intending to free rev_info structs,\nwe need them to be initialized with NULL pointers because\notherwise the release_revisions() method won't know which\nportions were legitimately initialized and which ones were\nnot.\n\nMaybe this NULL assignment happens as part of\nrepo_init_revisions(), but that also assumes that there is no\ncode path that would jump to a \"leave\" or \"cleanup\" tag before\nrunning that initialization method (which is the broken case\nthat Junio mentions above).\n\nMaybe there are tools that would identify that Junio's example\nwould be bad, but it is also likely that a compiler doesn't\ncatch that issue and tests don't cover that error condition.\n\nIt's my preference to initialize things to all-zeroes whenever\nthere is any chance of complexity, which is why this topic has\ncome to my attention on multiple threads.\n\nThanks,\n-Stolee\n \n"},{"id":"452493","messageId":"220328.86tubh3nmx.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"3bb95e8b-4977-ddca-225c-5afe28d8ac20@github.com","subject":"Re: [PATCH v3 08/27] revisions API users: add \"goto cleanup\" for \"rev_info\" early exit","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-28T18:55:31Z","receivedAt":"2022-03-28T19:03:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Mar 28 2022, Derrick Stolee wrote:\n\n> On 3/26/2022 1:24 AM, Junio C Hamano wrote:\n>> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:\n>> \n>>> Because I don't see how it makes any sense to have a REV_INFO_INIT if it\n>>> doesn't actually give you an init'd \"struct rev_info\" that's ready for\n>>> use. I.e. if you still need to call repo_init_revisions() it's just a\n>>> misleading interface.\n>> \n>> You can say\n>> \n>> \tstruct something *foo = NULL;\n>> \n>> \tif (some condition)\n>> \t\tfoo = alloc_and_init_foo();\n>> \n>> \t...\n>> \n>> \tfree_and_destruct(foo);\n>> \n>> and it is correct that \"initialize with NULL\" alone would not let\n>> you use the thing as full fledged 'foo', but you can still safely\n>> pass it to free_and_destruct() (or if \"something\" does not hold\n>> external resources, it could just be free()).  A flow like this:\n>> \n>> \tstruct rev_info revs = REV_INFO_INIT;\n>> \n>> \tif (!some condition)\n>> \t\tgoto leave;\n>> \tinit(&revs);\n>> \t... use revs ...\n>> leave:\n>>         release(&revs);\n>> \n>> would exactly be the same thing.\n>> \n>> In other words, you say \"I do not see how it makes any sense\" but to\n>> me it looks more like what does not make sense is your argument\n>> against what was suggested.\n>\n\n[re-arranging a bit]\n\n> However, now that we are intending to free rev_info structs,\n> we need them to be initialized with NULL pointers because\n> otherwise the release_revisions() method won't know which\n> portions were legitimately initialized and which ones were\n> not.\n>\n> Maybe this NULL assignment happens as part of\n> repo_init_revisions(), but that also assumes that there is no\n> code path that would jump to a \"leave\" or \"cleanup\" tag before\n> running that initialization method (which is the broken case\n> that Junio mentions above).\n>\n> Maybe there are tools that would identify that Junio's example\n> would be bad, but it is also likely that a compiler doesn't\n> catch that issue and tests don't cover that error condition.\n>\n> It's my preference to initialize things to all-zeroes whenever\n> there is any chance of complexity, which is why this topic has\n> come to my attention on multiple threads.\n\nMine too, IOW I really don't like the version in my own v3 here, but\nwanted to just use an initialization to { 0 } as in the v2.\n\nBut I read Junio's reply to\nhttps://lore.kernel.org/git/220324.868rszmga6.gmgdl@evledraar.gmail.com/\nas objecting to that general assumption, as we do e.g. in some of this\ncode:\n\n    git grep 'struct.*=.*\\{ 0 \\}'\n\nSo the v3 rewrite of those was seeking a way around that which didn't\nrequire implementing a full init-from-macro of REV_INFO_INIT.\n\nJunio, would you be OK/prefer to basically have the v2 verison, with\njust a dummy macro like this in revision.h?:\n\n    #define REV_INFO_INIT { 0 }\n\nWhich we'd then do something more useful with down the line?\n\n> Ævar has stated in multiple threads that he prefers to not\n> initialize data so that static analysis tools can detect a\n> use-before-initialization of specific members.\n\nJust to be clear, I prefer not doing initialization in cases where the\ncompiler is guaranteed to help you by skipping them, i.e. init to NULL\nwithin a function where say 2 branchesh both init the value, but if you\nwere to change that the init to NULL would hide the bug.\n\n(And I'm sure it's not something I came up with, but got pointed out to\nme repeatedly in review (by Jeff King or Junio?), until I got it...)\n\nBut skipping zero-ing out a struct like \"struct rev_info\" is not such a\ncase where the compiler can really help, and we often use that struct at\na far distance from where it was init'd. So I'd really like to see us\nnot do that as a habit, even if say valgrind can sometimes catch it (but\nonly if we have 100% test coverage!).\n\nYou're probably thinking more of the case of [1]. I consider that a\npretty clear case of running with scissors, I just figured that one was\nnarrow enough to be OK, but it's good to have [2] instead.\n\n1. https://lore.kernel.org/git/patch-1.1-193534b0f07-20220325T121715Z-avarab@gmail.com/\n2. https://lore.kernel.org/git/patch-v2-1.1-9951d92176e-20220328T154049Z-avarab@gmail.com/\n"},{"id":"452505","messageId":"xmqq5ynx3kut.fsf@gitster.g","threadId":"57525","inReplyTo":"220328.86tubh3nmx.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v3 08/27] revisions API users: add \"goto cleanup\" for \"rev_info\" early exit","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-28T20:03:54Z","receivedAt":"2022-03-28T20:03:59Z","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> Junio, would you be OK/prefer to basically have the v2 verison, with\n> just a dummy macro like this in revision.h?:\n>\n>     #define REV_INFO_INIT { 0 }\n\nFWIW, I do not see that as \"dummy\" at all.  We may further extend it\nin the future, but it is what is minimally required to safely pass\nrevs to init and release, and what I have been saying since the\nprevious round of the review,\nhttps://lore.kernel.org/git/xmqqwngji72a.fsf@gitster.g/\n\n\n"},{"id":"452727","messageId":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com","subject":"[PATCH v4 00/27] revision.[ch]: add and use release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:05Z","receivedAt":"2022-03-31T01:12:14Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This series reduces the memory leaks spotted by our tests by at least\nhalf, see v3[1] for a more general overview.\n\nChanges since v3:\n\n * Now based on top of ds/partial-bundle-more, since it's in \"next\"\n   and there's a direct (but relatively minor) semantic conflict with\n   it.\n\n * A new 8/27 moves some code in revisions.[ch] around, to make it\n  more straightforward to add a REV_INFO_INIT in 9/27.\n\n * 9/27 and related is now the old { 0 }-using commit from v2, except\n   that { 0 } is now REV_INFO_INIT. The goto patten to avoid assuming\n   that we can call release_revisions() on a \"struct rev_info\" without\n   having gone through repo_init_revisions() is gone.\n\n * A minor change in diff context from rebasing on \"master\".\n\n1. https://lore.kernel.org/git/cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com/\n\nÆvar Arnfjörð Bjarmason (27):\n  t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"\n  blame: use \"goto cleanup\" for cleanup_scoreboard()\n  string_list API users: use string_list_init_{no,}dup\n  format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"\n  revision.[ch]: split freeing of revs->commit into a function\n  revision.[ch]: provide and start using a release_revisions()\n  revisions API users: add straightforward release_revisions()\n  revision.[ch]: document and move code declared around \"init\"\n  revisions API users: use release_revisions() needing REV_INFO_INIT\n  stash: always have the owner of \"stash_info\" free it\n  revisions API users: add \"goto cleanup\" for release_revisions()\n  revisions API users: use release_revisions() in http-push.c\n  revisions API users: use release_revisions() in builtin/log.c\n  revisions API users: use release_revisions() with UNLEAK()\n  revisions API users: use release_revisions() for \"prune_data\" users\n  revisions API: have release_revisions() release \"commits\"\n  revisions API: have release_revisions() release \"mailmap\"\n  revisions API: have release_revisions() release \"cmdline\"\n  revisions API: have release_revisions() release \"filter\"\n  revisions API: have release_revisions() release \"grep_filter\"\n  revisions API: have release_revisions() release \"prune_data\"\n  revisions API: clear \"boundary_commits\" in release_revisions()\n  revisions API: release \"reflog_info\" in release revisions()\n  revisions API: call diff_free(&revs->pruning) in revisions_release()\n  revisions API: have release_revisions() release \"date_mode\"\n  revisions API: have release_revisions() release \"topo_walk_info\"\n  revisions API: add a TODO for diff_free(&revs->diffopt)\n\n add-interactive.c                          |  3 +-\n bisect.c                                   | 20 ++++--\n builtin/add.c                              |  3 +-\n builtin/am.c                               |  3 +\n builtin/bisect--helper.c                   |  2 +\n builtin/blame.c                            |  4 +-\n builtin/checkout.c                         |  3 +-\n builtin/commit.c                           |  6 +-\n builtin/describe.c                         |  2 +\n builtin/diff-files.c                       |  8 ++-\n builtin/diff-index.c                       |  5 +-\n builtin/diff.c                             |  2 +-\n builtin/fast-export.c                      |  1 +\n builtin/log.c                              | 36 ++++++----\n builtin/merge.c                            |  2 +\n builtin/pack-objects.c                     |  2 +\n builtin/prune.c                            |  1 +\n builtin/reflog.c                           |  1 +\n builtin/rev-list.c                         | 19 ++++--\n builtin/shortlog.c                         |  8 ++-\n builtin/stash.c                            | 77 +++++++++++-----------\n builtin/submodule--helper.c                | 14 ++--\n bundle.c                                   | 12 ++--\n diff-lib.c                                 |  8 ++-\n fmt-merge-msg.c                            |  1 +\n http-push.c                                |  3 +-\n merge-ort.c                                |  1 +\n merge-recursive.c                          |  5 +-\n midx.c                                     |  1 +\n pack-bitmap-write.c                        |  1 +\n range-diff.c                               |  2 +-\n ref-filter.c                               |  1 +\n reflog-walk.c                              | 24 ++++++-\n reflog-walk.h                              |  1 +\n remote.c                                   |  1 +\n revision.c                                 | 68 ++++++++++++++++---\n revision.h                                 | 65 +++++++++++-------\n sequencer.c                                | 26 ++++++--\n shallow.c                                  |  1 +\n submodule.c                                |  5 +-\n t/helper/test-fast-rebase.c                | 23 +++++--\n t/helper/test-revision-walking.c           |  1 +\n t/lib-git-svn.sh                           |  4 ++\n t/t0056-git-C.sh                           |  1 +\n t/t0062-revision-walking.sh                |  1 +\n t/t0100-previous.sh                        |  1 +\n t/t0101-at-syntax.sh                       |  2 +\n t/t1001-read-tree-m-2way.sh                |  1 +\n t/t1002-read-tree-m-u-2way.sh              |  1 +\n t/t1060-object-corruption.sh               |  2 +\n t/t1401-symbolic-ref.sh                    |  2 +\n t/t1411-reflog-show.sh                     |  1 +\n t/t1412-reflog-loop.sh                     |  2 +\n t/t1415-worktree-refs.sh                   |  1 +\n t/t2015-checkout-unborn.sh                 |  1 +\n t/t2200-add-update.sh                      |  1 +\n t/t3302-notes-index-expensive.sh           |  1 +\n t/t3303-notes-subtrees.sh                  |  1 +\n t/t3305-notes-fanout.sh                    |  1 +\n t/t3408-rebase-multi-line.sh               |  1 +\n t/t4021-format-patch-numbered.sh           |  1 +\n t/t4027-diff-submodule.sh                  |  1 +\n t/t4028-format-patch-mime-headers.sh       |  2 +\n t/t4036-format-patch-signer-mime.sh        |  1 +\n t/t4039-diff-assume-unchanged.sh           |  1 +\n t/t4055-diff-context.sh                    |  1 +\n t/t4066-diff-emit-delay.sh                 |  1 +\n t/t4122-apply-symlink-inside.sh            |  1 +\n t/t4126-apply-empty.sh                     |  1 -\n t/t4128-apply-root.sh                      |  1 +\n t/t4206-log-follow-harder-copies.sh        |  2 +\n t/t4207-log-decoration-colors.sh           |  1 +\n t/t4212-log-corrupt.sh                     |  1 +\n t/t5301-sliding-window.sh                  |  2 +\n t/t5313-pack-bounds-checks.sh              |  2 +\n t/t5316-pack-delta-depth.sh                |  2 +\n t/t5320-delta-islands.sh                   |  2 +\n t/t5322-pack-objects-sparse.sh             |  1 +\n t/t5506-remote-groups.sh                   |  1 +\n t/t5513-fetch-track.sh                     |  1 +\n t/t5515-fetch-merge-logic.sh               |  1 +\n t/t5518-fetch-exit-status.sh               |  1 +\n t/t5532-fetch-proxy.sh                     |  2 +\n t/t5600-clone-fail-cleanup.sh              |  1 +\n t/t5900-repo-selection.sh                  |  2 +\n t/t6002-rev-list-bisect.sh                 |  1 +\n t/t6003-rev-list-topo-order.sh             |  1 +\n t/t6005-rev-list-count.sh                  |  1 +\n t/t6018-rev-list-glob.sh                   |  1 +\n t/t6100-rev-list-in-order.sh               |  1 +\n t/t6101-rev-parse-parents.sh               |  1 +\n t/t6110-rev-list-sparse.sh                 |  1 +\n t/t6114-keep-packs.sh                      |  2 +\n t/t6131-pathspec-icase.sh                  |  2 +\n t/t7008-filter-branch-null-sha1.sh         |  1 +\n t/t7702-repack-cyclic-alternate.sh         |  2 +\n t/t9001-send-email.sh                      |  1 +\n t/t9100-git-svn-basic.sh                   |  1 +\n t/t9101-git-svn-props.sh                   |  2 +\n t/t9104-git-svn-follow-parent.sh           |  2 +\n t/t9106-git-svn-commit-diff-clobber.sh     |  2 +\n t/t9115-git-svn-dcommit-funky-renames.sh   |  1 +\n t/t9116-git-svn-log.sh                     |  1 +\n t/t9122-git-svn-author.sh                  |  2 +\n t/t9127-git-svn-partial-rebuild.sh         |  1 +\n t/t9129-git-svn-i18n-commitencoding.sh     |  1 +\n t/t9132-git-svn-broken-symlink.sh          |  1 +\n t/t9139-git-svn-non-utf8-commitencoding.sh |  1 +\n t/t9146-git-svn-empty-dirs.sh              |  2 +\n t/t9148-git-svn-propset.sh                 |  1 +\n t/t9160-git-svn-preserve-empty-dirs.sh     |  1 +\n t/t9162-git-svn-dcommit-interactive.sh     |  2 +\n t/t9164-git-svn-dcommit-concurrent.sh      |  2 +\n t/t9501-gitweb-standalone-http-status.sh   |  1 +\n wt-status.c                                | 15 +++--\n 115 files changed, 436 insertions(+), 148 deletions(-)\n\nRange-diff against v3:\n 1:  7c8298f564f =  1:  b99a07f98ee t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"\n 2:  515bacc8f27 =  2:  da5be507c0f blame: use \"goto cleanup\" for cleanup_scoreboard()\n 3:  58b59542c2b =  3:  243f7c8df96 string_list API users: use string_list_init_{no,}dup\n 4:  71495dfd073 =  4:  69f0aabe38f format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"\n 5:  12fb45f02c1 =  5:  2d0ed57ec2e revision.[ch]: split freeing of revs->commit into a function\n 6:  1e93a60d81a !  6:  b9beaba16bf revision.[ch]: provide and start using a release_revisions()\n    @@ revision.h: void repo_init_revisions(struct repository *r,\n      \t\t    struct setup_revision_opt *);\n      \n     +/**\n    -+ * Free data allocated in a \"struct rev_info\" after it's been setup\n    -+ * with repo_init_revisions() and setup_revisions().\n    ++ * Free data allocated in a \"struct rev_info\" after it's been\n    ++ * initialized with repo_init_revisions().\n     + */\n     +void release_revisions(struct rev_info *revs);\n     +\n 7:  c4fc1c98e7b !  7:  80ec9a131c0 revisions API users: add straightforward release_revisions()\n    @@ builtin/merge.c: static int evaluate_result(void)\n      \n     \n      ## builtin/pack-objects.c ##\n    -@@ builtin/pack-objects.c: static void read_packs_list_from_stdin(void)\n    - \tstrbuf_release(&buf);\n    - \tstring_list_clear(&include_packs, 0);\n    - \tstring_list_clear(&exclude_packs, 0);\n    -+\trelease_revisions(&revs);\n    - }\n    - \n    - static void read_object_list_from_stdin(void)\n    -@@ builtin/pack-objects.c: static void get_object_list(int ac, const char **av)\n    - \tif (unpack_unreachable)\n    - \t\tloosen_unused_packed_objects();\n    - \n    -+\trelease_revisions(&revs);\n    - \toid_array_clear(&recent_objects);\n    - }\n    - \n    +@@ builtin/pack-objects.c: int cmd_pack_objects(int argc, const char **argv, const char *prefix)\n    + \t\tread_object_list_from_stdin();\n    + \t} else if (pfd.have_revs) {\n    + \t\tget_object_list(&pfd.revs, rp.nr, rp.v);\n    ++\t\trelease_revisions(&pfd.revs);\n    + \t} else {\n    + \t\tstruct rev_info revs;\n    + \n    + \t\trepo_init_revisions(the_repository, &revs, NULL);\n    + \t\tget_object_list(&revs, rp.nr, rp.v);\n    ++\t\trelease_revisions(&revs);\n    + \t}\n    + \tcleanup_preferred_base();\n    + \tif (include_tag && nr_result)\n     \n      ## builtin/prune.c ##\n     @@ builtin/prune.c: int cmd_prune(int argc, const char **argv, const char *prefix)\n    @@ submodule.c: static void collect_changed_submodules(struct repository *r,\n     +\trelease_revisions(&rev);\n      }\n      \n    - static void free_submodules_oids(struct string_list *submodules)\n    + static void free_submodules_data(struct string_list *submodules)\n     \n      ## t/helper/test-revision-walking.c ##\n     @@ t/helper/test-revision-walking.c: static int run_revision_walk(void)\n 8:  f8a9443fe6f <  -:  ----------- revisions API users: add \"goto cleanup\" for \"rev_info\" early exit\n 9:  5f5c0d26395 <  -:  ----------- revisions API users: use release_revisions() in submodule.c edge case\n -:  ----------- >  8:  086cec742b4 revision.[ch]: document and move code declared around \"init\"\n -:  ----------- >  9:  2f4e65fb534 revisions API users: use release_revisions() needing REV_INFO_INIT\n10:  9da5c5f5f5d = 10:  145a0f74b6a stash: always have the owner of \"stash_info\" free it\n11:  da4fcd04345 = 11:  c9898a5a96b revisions API users: add \"goto cleanup\" for release_revisions()\n12:  b6acc61fb61 = 12:  6e41d666223 revisions API users: use release_revisions() in http-push.c\n13:  2eae0e809e5 = 13:  02ca92660af revisions API users: use release_revisions() in builtin/log.c\n14:  216eb3b41f7 = 14:  1aeb1127f34 revisions API users: use release_revisions() with UNLEAK()\n15:  f8e0eb52957 = 15:  c12b3d5a035 revisions API users: use release_revisions() for \"prune_data\" users\n16:  0a5662a9f67 = 16:  2e351702677 revisions API: have release_revisions() release \"commits\"\n17:  65803961ee2 = 17:  f67f1f5a575 revisions API: have release_revisions() release \"mailmap\"\n18:  ac68630dbf6 = 18:  1df61550e5a revisions API: have release_revisions() release \"cmdline\"\n19:  740abb80c57 = 19:  065c3cc91b2 revisions API: have release_revisions() release \"filter\"\n20:  26163e5e754 = 20:  806fef06b87 revisions API: have release_revisions() release \"grep_filter\"\n21:  842faebddc4 = 21:  fabbe452a14 revisions API: have release_revisions() release \"prune_data\"\n22:  bf93197993c = 22:  cb201fa1914 revisions API: clear \"boundary_commits\" in release_revisions()\n23:  af874c17fbd = 23:  0363a69073c revisions API: release \"reflog_info\" in release revisions()\n24:  006fa1fcc34 = 24:  bad359b2983 revisions API: call diff_free(&revs->pruning) in revisions_release()\n25:  0eacbd66711 = 25:  9a51c1ae1d1 revisions API: have release_revisions() release \"date_mode\"\n26:  5efc0ae58ed = 26:  b56db37940d revisions API: have release_revisions() release \"topo_walk_info\"\n27:  6c6fdf696af = 27:  b02cb8fcded revisions API: add a TODO for diff_free(&revs->diffopt)\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452728","messageId":"patch-v4-01.27-b99a07f98ee-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 01/27] t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:06Z","receivedAt":"2022-03-31T01:12:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak that's been with us since f9500261e0a (fast-rebase:\nwrite conflict state to working tree, index, and HEAD, 2021-05-20)\nchanged this code to move these strbuf_release() into an if/else\nblock.\n\nWe'll also add to \"reflog_msg\" in the \"else\" arm of the \"if\" block\nbeing modified here, and we'll append to \"branch_msg\" in both\ncases. But after f9500261e0a only the \"if\" block would free these two\n\"struct strbuf\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/helper/test-fast-rebase.c | 5 +++--\n 1 file changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c\nindex fc2d4609043..993b90eaedd 100644\n--- a/t/helper/test-fast-rebase.c\n+++ b/t/helper/test-fast-rebase.c\n@@ -201,8 +201,6 @@ int cmd__fast_rebase(int argc, const char **argv)\n \t\t}\n \t\tif (create_symref(\"HEAD\", branch_name.buf, reflog_msg.buf) < 0)\n \t\t\tdie(_(\"unable to update HEAD\"));\n-\t\tstrbuf_release(&reflog_msg);\n-\t\tstrbuf_release(&branch_name);\n \n \t\tprime_cache_tree(the_repository, the_repository->index,\n \t\t\t\t result.tree);\n@@ -221,5 +219,8 @@ int cmd__fast_rebase(int argc, const char **argv)\n \tif (write_locked_index(&the_index, &lock,\n \t\t\t       COMMIT_LOCK | SKIP_IF_UNCHANGED))\n \t\tdie(_(\"unable to write %s\"), get_index_file());\n+\n+\tstrbuf_release(&reflog_msg);\n+\tstrbuf_release(&branch_name);\n \treturn (result.clean == 0);\n }\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452729","messageId":"patch-v4-02.27-da5be507c0f-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 02/27] blame: use \"goto cleanup\" for cleanup_scoreboard()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:07Z","receivedAt":"2022-03-31T01:12:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Amend a freeing pattern added in 0906ac2b54b (blame: use changed-path\nBloom filters, 2020-04-16) to use a \"goto cleanup\", so that we can be\nsure that we call cleanup_scoreboard().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/blame.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 8d15b68afc9..885b381ab83 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1167,7 +1167,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tif (!incremental)\n \t\tsetup_pager();\n \telse\n-\t\treturn 0;\n+\t\tgoto cleanup;\n \n \tblame_sort_final(&sb);\n \n@@ -1201,6 +1201,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\tprintf(\"num commits: %d\\n\", sb.num_commits);\n \t}\n \n+cleanup:\n \tcleanup_scoreboard(&sb);\n \treturn 0;\n }\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452730","messageId":"patch-v4-03.27-243f7c8df96-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 03/27] string_list API users: use string_list_init_{no,}dup","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:08Z","receivedAt":"2022-03-31T01:12:21Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Follow-up on the introduction of string_list_init_nodup() and\nstring_list_init_dup() in the series merged in bd4232fac33 (Merge\nbranch 'ab/struct-init', 2021-07-16) and convert code that implicitly\nrelied on xcalloc() being equivalent to the initializer to use\nxmalloc() and string_list_init_{no,}dup() instead.\n\nIn the case of get_unmerged() in merge-recursive.c we used the\ncombination of xcalloc() and assigning \"1\" to \"strdup_strings\" to get\nwhat we'd get via string_list_init_dup(), let's use that instead.\n\nAdjacent code in cmd_format_patch() will be changed in a subsequent\ncommit, since we're changing that let's change the other in-tree\npatterns that do the same. Let's also convert a \"x == NULL\" to \"!x\"\nper our CodingGuidelines, as we need to change the \"if\" line anyway.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/log.c      | 9 ++++++---\n builtin/shortlog.c | 6 ++++--\n merge-recursive.c  | 4 ++--\n 3 files changed, 12 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex c211d66d1d0..634dc782cce 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -231,7 +231,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,\n \t}\n \n \tif (mailmap) {\n-\t\trev->mailmap = xcalloc(1, sizeof(struct string_list));\n+\t\trev->mailmap = xmalloc(sizeof(struct string_list));\n+\t\tstring_list_init_nodup(rev->mailmap);\n \t\tread_mailmap(rev->mailmap);\n \t}\n \n@@ -2173,8 +2174,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tprepare_bases(&bases, base, list, nr);\n \t}\n \n-\tif (in_reply_to || thread || cover_letter)\n-\t\trev.ref_message_ids = xcalloc(1, sizeof(struct string_list));\n+\tif (in_reply_to || thread || cover_letter) {\n+\t\trev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));\n+\t\tstring_list_init_nodup(rev.ref_message_ids);\n+\t}\n \tif (in_reply_to) {\n \t\tconst char *msgid = clean_message_id(in_reply_to);\n \t\tstring_list_append(rev.ref_message_ids, msgid);\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex 26c5c0cf935..fcde07c9367 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -81,8 +81,10 @@ static void insert_one_record(struct shortlog *log,\n \t\tformat_subject(&subject, oneline, \" \");\n \t\tbuffer = strbuf_detach(&subject, NULL);\n \n-\t\tif (item->util == NULL)\n-\t\t\titem->util = xcalloc(1, sizeof(struct string_list));\n+\t\tif (!item->util) {\n+\t\t\titem->util = xmalloc(sizeof(struct string_list));\n+\t\t\tstring_list_init_nodup(item->util);\n+\t\t}\n \t\tstring_list_append(item->util, buffer);\n \t}\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 1ee6364e8b1..32bbba5fbb1 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -522,10 +522,10 @@ static struct stage_data *insert_stage_data(struct repository *r,\n  */\n static struct string_list *get_unmerged(struct index_state *istate)\n {\n-\tstruct string_list *unmerged = xcalloc(1, sizeof(struct string_list));\n+\tstruct string_list *unmerged = xmalloc(sizeof(struct string_list));\n \tint i;\n \n-\tunmerged->strdup_strings = 1;\n+\tstring_list_init_dup(unmerged);\n \n \t/* TODO: audit for interaction with sparse-index. */\n \tensure_full_index(istate);\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452731","messageId":"patch-v4-04.27-69f0aabe38f-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 04/27] format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:09Z","receivedAt":"2022-03-31T01:12:23Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix two memory leaks in \"struct rev_info\" by freeing that memory in\ncmd_format_patch(). These two are unusual special-cases in being in\nthe \"struct rev_info\", but not being \"owned\" by the code in\nrevision.c. I.e. they're members of the struct so that this code in\n\"builtin/log.c\" can pass information code in log-tree.c.\n\nSee 20ff06805c6 (format-patch: resurrect extra headers from config,\n2006-06-02) and d1566f7883f (git-format-patch: Make the second and\nsubsequent mails replies to the first, 2006-07-14) for the initial\nintroduction of \"extra_headers\" and \"ref_message_ids\".\n\nWe can count on repo_init_revisions() memset()-ing this data to 0\nhowever, so we can count on it being either NULL or something we\nallocated. In the case of \"extra_headers\" let's add a local \"char *\"\nvariable to hold it, to avoid the eventual cast from \"const char *\"\nwhen we free() it.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/log.c | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 634dc782cce..6f9928fabfe 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1747,6 +1747,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tstruct commit *commit;\n \tstruct commit **list = NULL;\n \tstruct rev_info rev;\n+\tchar *to_free = NULL;\n \tstruct setup_revision_opt s_r_opt;\n \tint nr = 0, total, i;\n \tint use_stdout = 0;\n@@ -1947,7 +1948,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tstrbuf_addch(&buf, '\\n');\n \t}\n \n-\trev.extra_headers = strbuf_detach(&buf, NULL);\n+\trev.extra_headers = to_free = strbuf_detach(&buf, NULL);\n \n \tif (from) {\n \t\tif (split_ident_line(&rev.from_ident, from, strlen(from)))\n@@ -2284,6 +2285,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tstrbuf_release(&rdiff1);\n \tstrbuf_release(&rdiff2);\n \tstrbuf_release(&rdiff_title);\n+\tfree(to_free);\n+\tif (rev.ref_message_ids)\n+\t\tstring_list_clear(rev.ref_message_ids, 0);\n+\tfree(rev.ref_message_ids);\n \tUNLEAK(rev);\n \treturn 0;\n }\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452732","messageId":"patch-v4-05.27-2d0ed57ec2e-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 05/27] revision.[ch]: split freeing of revs->commit into a function","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:10Z","receivedAt":"2022-03-31T01:12:25Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Move the existing code for invoking free_commit_list() and setting\nrevs->commits to NULL into a new release_revisions_commit_list()\nfunction. This will be used as part of a general free()-ing mechanism\nfor \"struct rev_info\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 13 +++++++++----\n 1 file changed, 9 insertions(+), 4 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex 7d435f80480..29f102cb83d 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2923,6 +2923,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \treturn left;\n }\n \n+static void release_revisions_commit_list(struct rev_info *revs)\n+{\n+\tif (revs->commits) {\n+\t\tfree_commit_list(revs->commits);\n+\t\trevs->commits = NULL;\n+\t}\n+}\n+\n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n {\n \tstruct commit_list *l = xcalloc(1, sizeof(*l));\n@@ -4080,10 +4088,7 @@ static void create_boundary_commit_list(struct rev_info *revs)\n \t * boundary commits anyway.  (This is what the code has always\n \t * done.)\n \t */\n-\tif (revs->commits) {\n-\t\tfree_commit_list(revs->commits);\n-\t\trevs->commits = NULL;\n-\t}\n+\trelease_revisions_commit_list(revs);\n \n \t/*\n \t * Put all of the actual boundary commits from revs->boundary_commits\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452733","messageId":"patch-v4-06.27-b9beaba16bf-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 06/27] revision.[ch]: provide and start using a release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:11Z","receivedAt":"2022-03-31T01:12:28Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The users of the revision.[ch] API's \"struct rev_info\" are a major\nsource of memory leaks in the test suite under SANITIZE=leak, which in\nturn adds a lot of noise when trying to mark up tests with\n\"TEST_PASSES_SANITIZE_LEAK=true\".\n\nThe users of that API are largely one-shot, e.g. \"git log\", so\narguably freeing the memory is a waste of time, but in many cases\nthey've actually been trying to free the memory, and just doing that\nin a buggy manner.\n\nLet's provide a release_revisions() function for these users, and\nstart migrating them over per the plan outlined in [1]. Right now this\nonly handles the \"pending\" member of the struct, but more will be\nadded in subsequent commits.\n\nEven though we only clear the \"pending\" member now, let's not leave a\ntrap in code like the pre-image of index_differs_from(), where we'd\nstart doing the wrong thing as soon as the release_revisions() learned\nto clear its \"diffopt\". I.e. we need to call release_revisions() after\nwe've inspected any state in \"struct rev_info\".\n\nThis leaves in place e.g. clear_pathspec(&rev.prune_data) in\nstash_working_tree() in builtin/stash.c, subsequent commits will teach\nrelease_revisions() to free \"prune_data\" and other members that in\nsome cases are individually cleared by users of \"struct rev_info\" by\nreaching into its members. Those subsequent commits will remove the\nrelevant calls to e.g. clear_pathspec().\n\nWe avoid amending code in index_differs_from() in diff-lib.c as well\nas wt_status_collect_changes_index(), has_unstaged_changes() and\nhas_uncommitted_changes() in wt-status.c in a way that assumes that we\nare already clearing the \"diffopt\" member. That will be handled in a\nsubsequent commit.\n\n1. https://lore.kernel.org/git/87a6k8daeu.fsf@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/checkout.c | 2 +-\n builtin/stash.c    | 3 +--\n diff-lib.c         | 2 +-\n range-diff.c       | 2 +-\n revision.c         | 5 +++++\n revision.h         | 6 ++++++\n wt-status.c        | 5 +++--\n 7 files changed, 18 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 797681481d1..4d9e0bd3ac1 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -629,7 +629,7 @@ static void show_local_changes(struct object *head,\n \tdiff_setup_done(&rev.diffopt);\n \tadd_pending_object(&rev, head, NULL);\n \trun_diff_index(&rev, 0);\n-\tobject_array_clear(&rev.pending);\n+\trelease_revisions(&rev);\n }\n \n static void describe_detached_head(const char *msg, struct commit *commit)\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex ccdfdab44be..ad74624c2f7 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1266,9 +1266,8 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps\n \n done:\n \tdiscard_index(&istate);\n-\tUNLEAK(rev);\n-\tobject_array_clear(&rev.pending);\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n \tstrbuf_release(&diff_output);\n \tremove_path(stash_index_path.buf);\n \treturn ret;\ndiff --git a/diff-lib.c b/diff-lib.c\nindex ca085a03efc..d6800274bd5 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -662,7 +662,7 @@ int index_differs_from(struct repository *r,\n \t\tdiff_flags_or(&rev.diffopt.flags, flags);\n \trev.diffopt.ita_invisible_in_index = ita_invisible_in_index;\n \trun_diff_index(&rev, 1);\n-\tobject_array_clear(&rev.pending);\n+\trelease_revisions(&rev);\n \treturn (rev.diffopt.flags.has_changes != 0);\n }\n \ndiff --git a/range-diff.c b/range-diff.c\nindex b72eb9fdbee..39cc010c628 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -596,6 +596,6 @@ int is_range_diff_range(const char *arg)\n \t}\n \n \tfree(copy);\n-\tobject_array_clear(&revs.pending);\n+\trelease_revisions(&revs);\n \treturn negative > 0 && positive > 0;\n }\ndiff --git a/revision.c b/revision.c\nindex 29f102cb83d..9a3c05b69a5 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2931,6 +2931,11 @@ static void release_revisions_commit_list(struct rev_info *revs)\n \t}\n }\n \n+void release_revisions(struct rev_info *revs)\n+{\n+\tobject_array_clear(&revs->pending);\n+}\n+\n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n {\n \tstruct commit_list *l = xcalloc(1, sizeof(*l));\ndiff --git a/revision.h b/revision.h\nindex 5bc59c7bfe1..61c780fc4cd 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -377,6 +377,12 @@ void repo_init_revisions(struct repository *r,\n int setup_revisions(int argc, const char **argv, struct rev_info *revs,\n \t\t    struct setup_revision_opt *);\n \n+/**\n+ * Free data allocated in a \"struct rev_info\" after it's been\n+ * initialized with repo_init_revisions().\n+ */\n+void release_revisions(struct rev_info *revs);\n+\n void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,\n \t\t\tconst struct option *options,\n \t\t\tconst char * const usagestr[]);\ndiff --git a/wt-status.c b/wt-status.c\nindex d33f9272b72..922cf787f95 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -662,7 +662,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)\n \n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_index(&rev, 1);\n-\tobject_array_clear(&rev.pending);\n+\trelease_revisions(&rev);\n \tclear_pathspec(&rev.prune_data);\n }\n \n@@ -2545,6 +2545,7 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)\n \trev_info.diffopt.flags.quick = 1;\n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_files(&rev_info, 0);\n+\trelease_revisions(&rev_info);\n \treturn diff_result_code(&rev_info.diffopt, result);\n }\n \n@@ -2577,7 +2578,7 @@ int has_uncommitted_changes(struct repository *r,\n \n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_index(&rev_info, 1);\n-\tobject_array_clear(&rev_info.pending);\n+\trelease_revisions(&rev_info);\n \treturn diff_result_code(&rev_info.diffopt, result);\n }\n \n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452734","messageId":"patch-v4-09.27-2f4e65fb534-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 09/27] revisions API users: use release_revisions() needing REV_INFO_INIT","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:14Z","receivedAt":"2022-03-31T01:12:32Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use release_revisions() to various users of \"struct rev_list\" which\nneed to have their \"struct rev_info\" zero-initialized before we can\nstart using it.\n\nTo do this add a stub \"REV_INFO_INIT\" macro, ideally macro would be\nable to fully initialize a \"struct rev_info\", but all it does is the\nequivalent of assigning \"{ 0 }\" to the struct, the API user will still\nneed to use repo_init_revisions(). In some future follow-up work we'll\nhopefully make REV_INFO_INIT be a \"stand-alone\" init likke STRBUF_INIT\nand other similar macros.\n\nFor the bundle.c code see the early exit case added in\n3bbbe467f29 (bundle verify: error out if called without an object\ndatabase, 2019-05-27).\n\nFor the relevant bisect.c code see 45b6370812c (bisect: libify\n`check_good_are_ancestors_of_bad` and its dependents, 2020-02-17).\n\nFor the submodule.c code see the \"goto\" on \"(!left || !right || !sub)\"\nadded in 8e6df65015f (submodule: refactor show_submodule_summary with\nhelper function, 2016-08-31).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n bisect.c                    | 18 ++++++++++++------\n builtin/submodule--helper.c |  3 ++-\n bundle.c                    | 12 ++++++++----\n revision.h                  | 13 ++++++++++++-\n submodule.c                 |  3 ++-\n 5 files changed, 36 insertions(+), 13 deletions(-)\n\ndiff --git a/bisect.c b/bisect.c\nindex cc6b8b6230d..b63669cc9d7 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -1010,7 +1010,7 @@ void read_bisect_terms(const char **read_bad, const char **read_good)\n  */\n enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n {\n-\tstruct rev_info revs;\n+\tstruct rev_info revs = REV_INFO_INIT;\n \tstruct commit_list *tried;\n \tint reaches = 0, all = 0, nr, steps;\n \tenum bisect_error res = BISECT_OK;\n@@ -1035,7 +1035,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \n \tres = check_good_are_ancestors_of_bad(r, prefix, no_checkout);\n \tif (res)\n-\t\treturn res;\n+\t\tgoto cleanup;\n \n \tbisect_rev_setup(r, &revs, prefix, \"%s\", \"^%s\", 1);\n \n@@ -1060,14 +1060,16 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \t\t       term_good,\n \t\t       term_bad);\n \n-\t\treturn BISECT_FAILED;\n+\t\tres = BISECT_FAILED;\n+\t\tgoto cleanup;\n \t}\n \n \tif (!all) {\n \t\tfprintf(stderr, _(\"No testable commit found.\\n\"\n \t\t\t\"Maybe you started with bad path arguments?\\n\"));\n \n-\t\treturn BISECT_NO_TESTABLE_COMMIT;\n+\t\tres = BISECT_NO_TESTABLE_COMMIT;\n+\t\tgoto cleanup;\n \t}\n \n \tbisect_rev = &revs.commits->item->object.oid;\n@@ -1087,7 +1089,8 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \t\t * for negative return values for early returns up\n \t\t * until the cmd_bisect__helper() caller.\n \t\t */\n-\t\treturn BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;\n+\t\tres = BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;\n+\t\tgoto cleanup;\n \t}\n \n \tnr = all - reaches - 1;\n@@ -1106,7 +1109,10 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \t/* Clean up objects used, as they will be reused. */\n \trepo_clear_commit_marks(r, ALL_REV_FLAGS);\n \n-\treturn bisect_checkout(bisect_rev, no_checkout);\n+\tres = bisect_checkout(bisect_rev, no_checkout);\n+cleanup:\n+\trelease_revisions(&revs);\n+\treturn res;\n }\n \n static inline int log2i(int n)\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 24980863f68..cda33ee4d2b 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -766,7 +766,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,\n {\n \tchar *displaypath;\n \tstruct strvec diff_files_args = STRVEC_INIT;\n-\tstruct rev_info rev;\n+\tstruct rev_info rev = REV_INFO_INIT;\n \tint diff_files_result;\n \tstruct strbuf buf = STRBUF_INIT;\n \tconst char *git_dir;\n@@ -853,6 +853,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,\n cleanup:\n \tstrvec_clear(&diff_files_args);\n \tfree(displaypath);\n+\trelease_revisions(&rev);\n }\n \n static void status_submodule_cb(const struct cache_entry *list_item,\ndiff --git a/bundle.c b/bundle.c\nindex d50cfb5aa7e..6a870a6edb7 100644\n--- a/bundle.c\n+++ b/bundle.c\n@@ -196,14 +196,16 @@ int verify_bundle(struct repository *r,\n \t * to be verbose about the errors\n \t */\n \tstruct string_list *p = &header->prerequisites;\n-\tstruct rev_info revs;\n+\tstruct rev_info revs = REV_INFO_INIT;\n \tconst char *argv[] = {NULL, \"--all\", NULL};\n \tstruct commit *commit;\n \tint i, ret = 0, req_nr;\n \tconst char *message = _(\"Repository lacks these prerequisite commits:\");\n \n-\tif (!r || !r->objects || !r->objects->odb)\n-\t\treturn error(_(\"need a repository to verify a bundle\"));\n+\tif (!r || !r->objects || !r->objects->odb) {\n+\t\tret = error(_(\"need a repository to verify a bundle\"));\n+\t\tgoto cleanup;\n+\t}\n \n \trepo_init_revisions(r, &revs, NULL);\n \tfor (i = 0; i < p->nr; i++) {\n@@ -221,7 +223,7 @@ int verify_bundle(struct repository *r,\n \t\terror(\"%s %s\", oid_to_hex(oid), name);\n \t}\n \tif (revs.pending.nr != p->nr)\n-\t\treturn ret;\n+\t\tgoto cleanup;\n \treq_nr = revs.pending.nr;\n \tsetup_revisions(2, argv, &revs, NULL);\n \n@@ -284,6 +286,8 @@ int verify_bundle(struct repository *r,\n \t\t\tprintf_ln(\"The bundle uses this filter: %s\",\n \t\t\t\t  list_objects_filter_spec(&header->filter));\n \t}\n+cleanup:\n+\trelease_revisions(&revs);\n \treturn ret;\n }\n \ndiff --git a/revision.h b/revision.h\nindex 19f886472aa..c920753e8b9 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -329,6 +329,17 @@ struct rev_info {\n \tstruct tmp_objdir *remerge_objdir;\n };\n \n+/**\n+ * Initialize the \"struct rev_info\" structure with a macro.\n+ *\n+ * This is not sufficient (yet!) to initialize a \"struct rev_info\",\n+ * but it's OK (but redundant) to use it before a call to\n+ * repo_init_revisions(), which does the real initialization. By using\n+ * this it's safe to call release_revisions() on the \"struct rev_info\"\n+ * without having called repo_init_revisions().\n+ */\n+#define REV_INFO_INIT { 0 }\n+\n /**\n  * Initialize a rev_info structure with default values. The third parameter may\n  * be NULL or can be prefix path, and then the `.prefix` variable will be set\n@@ -363,7 +374,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs,\n \n /**\n  * Free data allocated in a \"struct rev_info\" after it's been\n- * initialized with repo_init_revisions().\n+ * initialized with repo_init_revisions() or REV_INFO_INIT.\n  */\n void release_revisions(struct rev_info *revs);\n \ndiff --git a/submodule.c b/submodule.c\nindex 4df04ae14e4..e796151ac6f 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -619,7 +619,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,\n \t\tstruct object_id *one, struct object_id *two,\n \t\tunsigned dirty_submodule)\n {\n-\tstruct rev_info rev;\n+\tstruct rev_info rev = REV_INFO_INIT;\n \tstruct commit *left = NULL, *right = NULL;\n \tstruct commit_list *merge_bases = NULL;\n \tstruct repository *sub;\n@@ -645,6 +645,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,\n \tprint_submodule_diff_summary(sub, &rev, o);\n \n out:\n+\trelease_revisions(&rev);\n \tif (merge_bases)\n \t\tfree_commit_list(merge_bases);\n \tclear_commit_marks(left, ~0);\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452735","messageId":"patch-v4-08.27-086cec742b4-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 08/27] revision.[ch]: document and move code declared around \"init\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:13Z","receivedAt":"2022-03-31T01:12:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"A subsequent commit will add \"REV_INFO_INIT\" macro adjacent to\nrepo_init_revisions(), unfortunately between the \"struct rev_info\"\nitself and that function we've added various miscellaneous code\nbetween the two over the years.\n\nLet's move that code either lower in revision.h, giving it API docs\nwhile we're at it, or in cases where it wasn't public API at all move\nit into revision.c No lines of code are changed here, only moved\naround. The only changes are the addition of new API comments.\n\nThe \"tree_difference\" variable could also be declared like this, which\nI think would be a lot clearer, but let's leave that for now to keep\nthis a move-only change:\n\n\tstatic enum {\n\t\tREV_TREE_SAME,\n\t\tREV_TREE_NEW, /* Only new files */\n\t\tREV_TREE_OLD, /* Only files removed */\n\t\tREV_TREE_DIFFERENT, /* Mixed changes */\n\t} tree_difference = REV_TREE_SAME;\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c |  4 ++++\n revision.h | 50 ++++++++++++++++++++++++--------------------------\n 2 files changed, 28 insertions(+), 26 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex 9a3c05b69a5..8fc157e9ee7 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -606,6 +606,10 @@ static struct commit *one_relevant_parent(const struct rev_info *revs,\n  *\n  *   2. We saw anything except REV_TREE_NEW.\n  */\n+#define REV_TREE_SAME\t\t0\n+#define REV_TREE_NEW\t\t1\t/* Only new files */\n+#define REV_TREE_OLD\t\t2\t/* Only files removed */\n+#define REV_TREE_DIFFERENT\t3\t/* Mixed changes */\n static int tree_difference = REV_TREE_SAME;\n \n static void file_add_remove(struct diff_options *options,\ndiff --git a/revision.h b/revision.h\nindex 61c780fc4cd..19f886472aa 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -329,32 +329,6 @@ struct rev_info {\n \tstruct tmp_objdir *remerge_objdir;\n };\n \n-int ref_excluded(struct string_list *, const char *path);\n-void clear_ref_exclusion(struct string_list **);\n-void add_ref_exclusion(struct string_list **, const char *exclude);\n-\n-\n-#define REV_TREE_SAME\t\t0\n-#define REV_TREE_NEW\t\t1\t/* Only new files */\n-#define REV_TREE_OLD\t\t2\t/* Only files removed */\n-#define REV_TREE_DIFFERENT\t3\t/* Mixed changes */\n-\n-/* revision.c */\n-typedef void (*show_early_output_fn_t)(struct rev_info *, struct commit_list *);\n-extern volatile show_early_output_fn_t show_early_output;\n-\n-struct setup_revision_opt {\n-\tconst char *def;\n-\tvoid (*tweak)(struct rev_info *, struct setup_revision_opt *);\n-\tunsigned int\tassume_dashdash:1,\n-\t\t\tallow_exclude_promisor_objects:1;\n-\tunsigned revarg_opt;\n-};\n-\n-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS\n-#define init_revisions(revs, prefix) repo_init_revisions(the_repository, revs, prefix)\n-#endif\n-\n /**\n  * Initialize a rev_info structure with default values. The third parameter may\n  * be NULL or can be prefix path, and then the `.prefix` variable will be set\n@@ -366,6 +340,9 @@ struct setup_revision_opt {\n void repo_init_revisions(struct repository *r,\n \t\t\t struct rev_info *revs,\n \t\t\t const char *prefix);\n+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS\n+#define init_revisions(revs, prefix) repo_init_revisions(the_repository, revs, prefix)\n+#endif\n \n /**\n  * Parse revision information, filling in the `rev_info` structure, and\n@@ -374,6 +351,13 @@ void repo_init_revisions(struct repository *r,\n  * head of the argument list. The last parameter is used in case no\n  * parameter given by the first two arguments.\n  */\n+struct setup_revision_opt {\n+\tconst char *def;\n+\tvoid (*tweak)(struct rev_info *, struct setup_revision_opt *);\n+\tunsigned int\tassume_dashdash:1,\n+\t\t\tallow_exclude_promisor_objects:1;\n+\tunsigned revarg_opt;\n+};\n int setup_revisions(int argc, const char **argv, struct rev_info *revs,\n \t\t    struct setup_revision_opt *);\n \n@@ -423,6 +407,14 @@ void mark_trees_uninteresting_sparse(struct repository *r, struct oidset *trees)\n \n void show_object_with_name(FILE *, struct object *, const char *);\n \n+/**\n+ * Helpers to check if a \"struct string_list\" item matches wild\n+ * wildmatch().\n+ */\n+int ref_excluded(struct string_list *, const char *path);\n+void clear_ref_exclusion(struct string_list **);\n+void add_ref_exclusion(struct string_list **, const char *exclude);\n+\n /**\n  * This function can be used if you want to add commit objects as revision\n  * information. You can use the `UNINTERESTING` object flag to indicate if\n@@ -478,4 +470,10 @@ int rewrite_parents(struct rev_info *revs,\n  */\n struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit);\n \n+/**\n+ * Global for the (undocumented) \"--early-output\" flag for \"git log\".\n+ */\n+typedef void (*show_early_output_fn_t)(struct rev_info *, struct commit_list *);\n+extern volatile show_early_output_fn_t show_early_output;\n+\n #endif\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452736","messageId":"patch-v4-07.27-80ec9a131c0-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 07/27] revisions API users: add straightforward release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:12Z","receivedAt":"2022-03-31T01:12:36Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a release_revisions() to various users of \"struct rev_list\" in\nthose straightforward cases where we only need to add the\nrelease_revisions() call to the end of a block, and don't need to\ne.g. refactor anything to use a \"goto cleanup\" pattern.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n add-interactive.c                | 1 +\n bisect.c                         | 2 ++\n builtin/add.c                    | 1 +\n builtin/am.c                     | 3 +++\n builtin/bisect--helper.c         | 2 ++\n builtin/blame.c                  | 1 +\n builtin/checkout.c               | 1 +\n builtin/commit.c                 | 1 +\n builtin/describe.c               | 2 ++\n builtin/fast-export.c            | 1 +\n builtin/merge.c                  | 2 ++\n builtin/pack-objects.c           | 2 ++\n builtin/prune.c                  | 1 +\n builtin/reflog.c                 | 1 +\n builtin/shortlog.c               | 2 ++\n builtin/submodule--helper.c      | 1 +\n fmt-merge-msg.c                  | 1 +\n merge-ort.c                      | 1 +\n merge-recursive.c                | 1 +\n midx.c                           | 1 +\n pack-bitmap-write.c              | 1 +\n ref-filter.c                     | 1 +\n remote.c                         | 1 +\n sequencer.c                      | 3 +++\n shallow.c                        | 1 +\n submodule.c                      | 2 ++\n t/helper/test-revision-walking.c | 1 +\n wt-status.c                      | 1 +\n 28 files changed, 39 insertions(+)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex 72472103017..54cdfc82017 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -570,6 +570,7 @@ static int get_modified_files(struct repository *r,\n \n \t\tif (ps)\n \t\t\tclear_pathspec(&rev.prune_data);\n+\t\trelease_revisions(&rev);\n \t}\n \thashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);\n \tif (unmerged_count)\ndiff --git a/bisect.c b/bisect.c\nindex 9e6a2b7f201..cc6b8b6230d 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -884,6 +884,7 @@ static int check_ancestors(struct repository *r, int rev_nr,\n \t/* Clean up objects used, as they will be reused. */\n \tclear_commit_marks_many(rev_nr, rev, ALL_REV_FLAGS);\n \n+\trelease_revisions(&revs);\n \treturn res;\n }\n \n@@ -964,6 +965,7 @@ static void show_diff_tree(struct repository *r,\n \n \tsetup_revisions(ARRAY_SIZE(argv) - 1, argv, &opt, NULL);\n \tlog_tree_commit(&opt, commit);\n+\trelease_revisions(&opt);\n }\n \n /*\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 3ffb86a4338..f507d2191cd 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -340,6 +340,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)\n \n \tunlink(file);\n \tfree(file);\n+\trelease_revisions(&rev);\n \treturn 0;\n }\n \ndiff --git a/builtin/am.c b/builtin/am.c\nindex 0f4111bafa0..93bec62afa9 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -1397,6 +1397,7 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm\n \tadd_pending_object(&rev_info, &commit->object, \"\");\n \tdiff_setup_done(&rev_info.diffopt);\n \tlog_tree_commit(&rev_info, commit);\n+\trelease_revisions(&rev_info);\n }\n \n /**\n@@ -1429,6 +1430,7 @@ static void write_index_patch(const struct am_state *state)\n \tadd_pending_object(&rev_info, &tree->object, \"\");\n \tdiff_setup_done(&rev_info.diffopt);\n \trun_diff_index(&rev_info, 1);\n+\trelease_revisions(&rev_info);\n }\n \n /**\n@@ -1582,6 +1584,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \t\tadd_pending_oid(&rev_info, \"HEAD\", &our_tree, 0);\n \t\tdiff_setup_done(&rev_info.diffopt);\n \t\trun_diff_index(&rev_info, 1);\n+\t\trelease_revisions(&rev_info);\n \t}\n \n \tif (run_apply(state, index_path))\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex 8b2b259ff0d..e4d7b6779ae 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -555,6 +555,7 @@ static int bisect_skipped_commits(struct bisect_terms *terms)\n \treset_revision_walk();\n \n \tstrbuf_release(&commit_name);\n+\trelease_revisions(&revs);\n \tfclose(fp);\n \treturn 0;\n }\n@@ -1041,6 +1042,7 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar\n \t\t\t\t\t\toid_to_hex(&commit->object.oid));\n \n \t\t\treset_revision_walk();\n+\t\t\trelease_revisions(&revs);\n \t\t} else {\n \t\t\tstrvec_push(&argv_state, argv[i]);\n \t\t}\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 885b381ab83..24bac822c56 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1203,5 +1203,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \n cleanup:\n \tcleanup_scoreboard(&sb);\n+\trelease_revisions(&revs);\n \treturn 0;\n }\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 4d9e0bd3ac1..7ad4a7113c9 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1082,6 +1082,7 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne\n \n \t/* Clean up objects used, as they will be reused. */\n \trepo_clear_commit_marks(the_repository, ALL_REV_FLAGS);\n+\trelease_revisions(&revs);\n }\n \n static int switch_branches(const struct checkout_opts *opts,\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 009a1de0a3d..c7eda9bbb72 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1123,6 +1123,7 @@ static const char *find_author_by_nickname(const char *name)\n \t\tstrbuf_release(&buf);\n \t\tformat_commit_message(commit, \"%aN <%aE>\", &buf, &ctx);\n \t\tclear_mailmap(&mailmap);\n+\t\trelease_revisions(&revs);\n \t\treturn strbuf_detach(&buf, NULL);\n \t}\n \tdie(_(\"--author '%s' is not 'Name <email>' and matches no existing author\"), name);\ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex 42159cd26bd..a76f1a1a7a7 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -517,6 +517,7 @@ static void describe_blob(struct object_id oid, struct strbuf *dst)\n \n \ttraverse_commit_list(&revs, process_commit, process_object, &pcd);\n \treset_revision_walk();\n+\trelease_revisions(&revs);\n }\n \n static void describe(const char *arg, int last_one)\n@@ -667,6 +668,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)\n \t\t\t\tsuffix = NULL;\n \t\t\telse\n \t\t\t\tsuffix = dirty;\n+\t\t\trelease_revisions(&revs);\n \t\t}\n \t\tdescribe(\"HEAD\", 1);\n \t} else if (dirty) {\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex a7d72697fba..f34ae451ee3 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -1275,6 +1275,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)\n \t\tprintf(\"done\\n\");\n \n \trefspec_clear(&refspecs);\n+\trelease_revisions(&revs);\n \n \treturn 0;\n }\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex f178f5a3ee1..d9784d4891c 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -443,6 +443,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead\n \t}\n \twrite_file_buf(git_path_squash_msg(the_repository), out.buf, out.len);\n \tstrbuf_release(&out);\n+\trelease_revisions(&rev);\n }\n \n static void finish(struct commit *head_commit,\n@@ -998,6 +999,7 @@ static int evaluate_result(void)\n \t */\n \tcnt += count_unmerged_entries();\n \n+\trelease_revisions(&rev);\n \treturn cnt;\n }\n \ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 014dcd4bc98..180fff03569 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -4177,11 +4177,13 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)\n \t\tread_object_list_from_stdin();\n \t} else if (pfd.have_revs) {\n \t\tget_object_list(&pfd.revs, rp.nr, rp.v);\n+\t\trelease_revisions(&pfd.revs);\n \t} else {\n \t\tstruct rev_info revs;\n \n \t\trepo_init_revisions(the_repository, &revs, NULL);\n \t\tget_object_list(&revs, rp.nr, rp.v);\n+\t\trelease_revisions(&revs);\n \t}\n \tcleanup_preferred_base();\n \tif (include_tag && nr_result)\ndiff --git a/builtin/prune.c b/builtin/prune.c\nindex c2bcdc07db4..df376b2ed1e 100644\n--- a/builtin/prune.c\n+++ b/builtin/prune.c\n@@ -196,5 +196,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix)\n \t\tprune_shallow(show_only ? PRUNE_SHOW_ONLY : 0);\n \t}\n \n+\trelease_revisions(&revs);\n \treturn 0;\n }\ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex 9407f835cb6..592d5d33442 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -248,6 +248,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)\n \t\tif (verbose)\n \t\t\tprintf(_(\"Marking reachable objects...\"));\n \t\tmark_reachable_objects(&revs, 0, 0, NULL);\n+\t\trelease_revisions(&revs);\n \t\tif (verbose)\n \t\t\tputchar('\\n');\n \t}\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex fcde07c9367..35825f075e3 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -422,6 +422,8 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)\n \telse\n \t\tget_from_rev(&rev, &log);\n \n+\trelease_revisions(&rev);\n+\n \tshortlog_output(&log);\n \tif (log.file != stdout)\n \t\tfclose(log.file);\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 5301612d24b..24980863f68 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1269,6 +1269,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \t\trun_diff_files(&rev, 0);\n \tprepare_submodule_summary(info, &list);\n \tstrvec_clear(&diff_args);\n+\trelease_revisions(&rev);\n \treturn 0;\n }\n \ndiff --git a/fmt-merge-msg.c b/fmt-merge-msg.c\nindex baca57d5b64..f48f44f9cd1 100644\n--- a/fmt-merge-msg.c\n+++ b/fmt-merge-msg.c\n@@ -699,6 +699,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,\n \t\t\tshortlog(origins.items[i].string,\n \t\t\t\t origins.items[i].util,\n \t\t\t\t head, &rev, opts, out);\n+\t\trelease_revisions(&rev);\n \t}\n \n \tstrbuf_complete_line(out);\ndiff --git a/merge-ort.c b/merge-ort.c\nindex 8545354dafd..4c0d4eba19b 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -1594,6 +1594,7 @@ static int find_first_merges(struct repository *repo,\n \t}\n \n \tobject_array_clear(&merges);\n+\trelease_revisions(&revs);\n \treturn result->nr;\n }\n \ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 32bbba5fbb1..acd13b2b069 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1160,6 +1160,7 @@ static int find_first_merges(struct repository *repo,\n \t}\n \n \tobject_array_clear(&merges);\n+\trelease_revisions(&revs);\n \treturn result->nr;\n }\n \ndiff --git a/midx.c b/midx.c\nindex 107365d2114..d5724df7d60 100644\n--- a/midx.c\n+++ b/midx.c\n@@ -1061,6 +1061,7 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr\n \tif (indexed_commits_nr_p)\n \t\t*indexed_commits_nr_p = cb.commits_nr;\n \n+\trelease_revisions(&revs);\n \treturn cb.commits;\n }\n \ndiff --git a/pack-bitmap-write.c b/pack-bitmap-write.c\nindex cf681547f2e..c43375bd344 100644\n--- a/pack-bitmap-write.c\n+++ b/pack-bitmap-write.c\n@@ -326,6 +326,7 @@ static void bitmap_builder_init(struct bitmap_builder *bb,\n \ttrace2_data_intmax(\"pack-bitmap-write\", the_repository,\n \t\t\t   \"num_maximal_commits\", num_maximal);\n \n+\trelease_revisions(&revs);\n \tfree_commit_list(reusable);\n }\n \ndiff --git a/ref-filter.c b/ref-filter.c\nindex 7838bd22b8d..a91688bbf17 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -2392,6 +2392,7 @@ static void reach_filter(struct ref_array *array,\n \t\tclear_commit_marks(merge_commit, ALL_REV_FLAGS);\n \t}\n \n+\trelease_revisions(&revs);\n \tfree(to_clear);\n }\n \ndiff --git a/remote.c b/remote.c\nindex 42a4e7106e1..fa3152a5d52 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -2172,6 +2172,7 @@ static int stat_branch_pair(const char *branch_name, const char *base,\n \tclear_commit_marks(theirs, ALL_REV_FLAGS);\n \n \tstrvec_clear(&argv);\n+\trelease_revisions(&revs);\n \treturn 1;\n }\n \ndiff --git a/sequencer.c b/sequencer.c\nindex a1bb39383db..f9d7acd1065 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1347,6 +1347,7 @@ void print_commit_summary(struct repository *r,\n \t\tlog_tree_commit(&rev, commit);\n \t}\n \n+\trelease_revisions(&rev);\n \tstrbuf_release(&format);\n }\n \n@@ -3415,6 +3416,7 @@ static int make_patch(struct repository *r,\n \t\tunuse_commit_buffer(commit, commit_buffer);\n \t}\n \tstrbuf_release(&buf);\n+\trelease_revisions(&log_tree_opt);\n \n \treturn res;\n }\n@@ -4525,6 +4527,7 @@ static int pick_commits(struct repository *r,\n \t\t\t\t\t      &log_tree_opt.diffopt);\n \t\t\t\tlog_tree_diff_flush(&log_tree_opt);\n \t\t\t}\n+\t\t\trelease_revisions(&log_tree_opt);\n \t\t}\n \t\tflush_rewritten_pending();\n \t\tif (!stat(rebase_path_rewritten_list(), &st) &&\ndiff --git a/shallow.c b/shallow.c\nindex e158be58b05..797a593633f 100644\n--- a/shallow.c\n+++ b/shallow.c\n@@ -262,6 +262,7 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,\n \t\tif ((o->flags & both_flags) == both_flags)\n \t\t\to->flags &= ~not_shallow_flag;\n \t}\n+\trelease_revisions(&revs);\n \treturn result;\n }\n \ndiff --git a/submodule.c b/submodule.c\nindex 86c8f0f89db..4df04ae14e4 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -925,9 +925,11 @@ static void collect_changed_submodules(struct repository *r,\n \t\tdiff_rev.diffopt.format_callback_data = &data;\n \t\tdiff_rev.dense_combined_merges = 1;\n \t\tdiff_tree_combined_merge(commit, &diff_rev);\n+\t\trelease_revisions(&diff_rev);\n \t}\n \n \treset_revision_walk();\n+\trelease_revisions(&rev);\n }\n \n static void free_submodules_data(struct string_list *submodules)\ndiff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c\nindex 625b2dbf822..4a45d5bac2a 100644\n--- a/t/helper/test-revision-walking.c\n+++ b/t/helper/test-revision-walking.c\n@@ -43,6 +43,7 @@ static int run_revision_walk(void)\n \t}\n \n \treset_revision_walk();\n+\trelease_revisions(&rev);\n \treturn got_revision;\n }\n \ndiff --git a/wt-status.c b/wt-status.c\nindex 922cf787f95..f9100621375 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1152,6 +1152,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)\n \t\trev.diffopt.b_prefix = \"w/\";\n \t\trun_diff_files(&rev, 0);\n \t}\n+\trelease_revisions(&rev);\n }\n \n static void wt_longstatus_print_tracking(struct wt_status *s)\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452737","messageId":"patch-v4-12.27-6e41d666223-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 12/27] revisions API users: use release_revisions() in http-push.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:17Z","receivedAt":"2022-03-31T01:12:40Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In the case of cmd_main() in http-push.c we need to move the\ndeceleration of the \"struct rev-list\" into the loop over the\n\"remote_refs\" when adding a release_revisions().\n\nWe'd previously set up the \"revs\" for each remote, but would\npotentially leak memory on each one.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n http-push.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/http-push.c b/http-push.c\nindex f0c044dcf76..01e7c2ac5c8 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -1689,7 +1689,6 @@ int cmd_main(int argc, const char **argv)\n \tstruct refspec rs = REFSPEC_INIT_PUSH;\n \tstruct remote_lock *ref_lock = NULL;\n \tstruct remote_lock *info_ref_lock = NULL;\n-\tstruct rev_info revs;\n \tint delete_branch = 0;\n \tint force_delete = 0;\n \tint objects_to_send;\n@@ -1825,6 +1824,7 @@ int cmd_main(int argc, const char **argv)\n \n \tnew_refs = 0;\n \tfor (ref = remote_refs; ref; ref = ref->next) {\n+\t\tstruct rev_info revs;\n \t\tstruct strvec commit_argv = STRVEC_INIT;\n \n \t\tif (!ref->peer_ref)\n@@ -1941,6 +1941,7 @@ int cmd_main(int argc, const char **argv)\n \t\tunlock_remote(ref_lock);\n \t\tcheck_locks();\n \t\tstrvec_clear(&commit_argv);\n+\t\trelease_revisions(&revs);\n \t}\n \n \t/* Update remote server info if appropriate */\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452738","messageId":"patch-v4-10.27-145a0f74b6a-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 10/27] stash: always have the owner of \"stash_info\" free it","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:15Z","receivedAt":"2022-03-31T01:12:43Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the initialization of the \"revision\" member of \"struct\nstash_info\" to be initialized vi a macro, and more importantly that\nthat initializing function be tasked to free it, usually via a \"goto\ncleanup\" pattern.\n\nDespite the \"revision\" name (and the topic of the series containing\nthis commit) the \"stash info\" has nothing to do with the \"struct\nrev_info\". I'm making this change because in the subsequent commit\nwhen we do want to free the \"struct rev_info\" via a \"goto cleanup\"\npattern we'd otherwise free() uninitialized memory in some cases, as\nwe only strbuf_init() the string in get_stash_info().\n\nSo while it's the smallest possible change, let's convert all users of\nthis pattern in the file while we're at it.\n\nA good follow-up to this change would be to change all the \"ret = -1;\ngoto done;\" in this file to instead use a \"goto cleanup\", and\ninitialize \"int ret = -1\" at the start of the relevant functions. That\nwould allow us to drop a lot of needless brace verbosity on two-line\n\"if\" statements, but let's leave that alone for now.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/stash.c | 70 ++++++++++++++++++++++++++-----------------------\n 1 file changed, 37 insertions(+), 33 deletions(-)\n\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex ad74624c2f7..891572d807c 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -116,6 +116,10 @@ struct stash_info {\n \tint has_u;\n };\n \n+#define STASH_INFO_INIT { \\\n+\t.revision = STRBUF_INIT, \\\n+}\n+\n static void free_stash_info(struct stash_info *info)\n {\n \tstrbuf_release(&info->revision);\n@@ -157,10 +161,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)\n \tif (argc == 1)\n \t\tcommit = argv[0];\n \n-\tstrbuf_init(&info->revision, 0);\n \tif (!commit) {\n \t\tif (!ref_exists(ref_stash)) {\n-\t\t\tfree_stash_info(info);\n \t\t\tfprintf_ln(stderr, _(\"No stash entries found.\"));\n \t\t\treturn -1;\n \t\t}\n@@ -174,11 +176,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)\n \n \trevision = info->revision.buf;\n \n-\tif (get_oid(revision, &info->w_commit)) {\n-\t\terror(_(\"%s is not a valid reference\"), revision);\n-\t\tfree_stash_info(info);\n-\t\treturn -1;\n-\t}\n+\tif (get_oid(revision, &info->w_commit))\n+\t\treturn error(_(\"%s is not a valid reference\"), revision);\n \n \tassert_stash_like(info, revision);\n \n@@ -197,7 +196,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)\n \t\tinfo->is_stash_ref = !strcmp(expanded_ref, ref_stash);\n \t\tbreak;\n \tdefault: /* Invalid or ambiguous */\n-\t\tfree_stash_info(info);\n+\t\tbreak;\n \t}\n \n \tfree(expanded_ref);\n@@ -598,10 +597,10 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,\n \n static int apply_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret;\n+\tint ret = -1;\n \tint quiet = 0;\n \tint index = 0;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n \t\tOPT_BOOL(0, \"index\", &index,\n@@ -613,9 +612,10 @@ static int apply_stash(int argc, const char **argv, const char *prefix)\n \t\t\t     git_stash_apply_usage, 0);\n \n \tif (get_stash_info(&info, argc, argv))\n-\t\treturn -1;\n+\t\tgoto cleanup;\n \n \tret = do_apply_stash(prefix, &info, index, quiet);\n+cleanup:\n \tfree_stash_info(&info);\n \treturn ret;\n }\n@@ -662,9 +662,9 @@ static void assert_stash_ref(struct stash_info *info)\n \n static int drop_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret;\n+\tint ret = -1;\n \tint quiet = 0;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n \t\tOPT_END()\n@@ -674,21 +674,22 @@ static int drop_stash(int argc, const char **argv, const char *prefix)\n \t\t\t     git_stash_drop_usage, 0);\n \n \tif (get_stash_info(&info, argc, argv))\n-\t\treturn -1;\n+\t\tgoto cleanup;\n \n \tassert_stash_ref(&info);\n \n \tret = do_drop_stash(&info, quiet);\n+cleanup:\n \tfree_stash_info(&info);\n \treturn ret;\n }\n \n static int pop_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret;\n+\tint ret = -1;\n \tint index = 0;\n \tint quiet = 0;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n \t\tOPT_BOOL(0, \"index\", &index,\n@@ -700,7 +701,7 @@ static int pop_stash(int argc, const char **argv, const char *prefix)\n \t\t\t     git_stash_pop_usage, 0);\n \n \tif (get_stash_info(&info, argc, argv))\n-\t\treturn -1;\n+\t\tgoto cleanup;\n \n \tassert_stash_ref(&info);\n \tif ((ret = do_apply_stash(prefix, &info, index, quiet)))\n@@ -709,15 +710,16 @@ static int pop_stash(int argc, const char **argv, const char *prefix)\n \telse\n \t\tret = do_drop_stash(&info, quiet);\n \n+cleanup:\n \tfree_stash_info(&info);\n \treturn ret;\n }\n \n static int branch_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret;\n+\tint ret = -1;\n \tconst char *branch = NULL;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tstruct option options[] = {\n \t\tOPT_END()\n@@ -734,7 +736,7 @@ static int branch_stash(int argc, const char **argv, const char *prefix)\n \tbranch = argv[0];\n \n \tif (get_stash_info(&info, argc - 1, argv + 1))\n-\t\treturn -1;\n+\t\tgoto cleanup;\n \n \tcp.git_cmd = 1;\n \tstrvec_pushl(&cp.args, \"checkout\", \"-b\", NULL);\n@@ -746,8 +748,8 @@ static int branch_stash(int argc, const char **argv, const char *prefix)\n \tif (!ret && info.is_stash_ref)\n \t\tret = do_drop_stash(&info, 0);\n \n+cleanup:\n \tfree_stash_info(&info);\n-\n \treturn ret;\n }\n \n@@ -825,8 +827,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op\n static int show_stash(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n-\tint ret = 0;\n-\tstruct stash_info info;\n+\tint ret = -1;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct rev_info rev;\n \tstruct strvec stash_args = STRVEC_INIT;\n \tstruct strvec revision_args = STRVEC_INIT;\n@@ -861,10 +863,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \t\t\tstrvec_push(&revision_args, argv[i]);\n \t}\n \n-\tret = get_stash_info(&info, stash_args.nr, stash_args.v);\n-\tstrvec_clear(&stash_args);\n-\tif (ret)\n-\t\treturn -1;\n+\tif (get_stash_info(&info, stash_args.nr, stash_args.v))\n+\t\tgoto cleanup;\n \n \t/*\n \t * The config settings are applied only if there are not passed\n@@ -878,8 +878,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \t\t\trev.diffopt.output_format |= DIFF_FORMAT_PATCH;\n \n \t\tif (!show_stat && !show_patch) {\n-\t\t\tfree_stash_info(&info);\n-\t\t\treturn 0;\n+\t\t\tret = 0;\n+\t\t\tgoto cleanup;\n \t\t}\n \t}\n \n@@ -912,8 +912,11 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \t}\n \tlog_tree_diff_flush(&rev);\n \n+\tret = diff_result_code(&rev.diffopt, 0);;\n+cleanup:\n+\tstrvec_clear(&stash_args);\n \tfree_stash_info(&info);\n-\treturn diff_result_code(&rev.diffopt, 0);\n+\treturn ret;\n }\n \n static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,\n@@ -1409,9 +1412,9 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b\n \n static int create_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret = 0;\n+\tint ret;\n \tstruct strbuf stash_msg_buf = STRBUF_INIT;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct pathspec ps;\n \n \t/* Starting with argv[1], since argv[0] is \"create\" */\n@@ -1426,6 +1429,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)\n \tif (!ret)\n \t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n \n+\tfree_stash_info(&info);\n \tstrbuf_release(&stash_msg_buf);\n \treturn ret;\n }\n@@ -1434,7 +1438,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q\n \t\t\t int keep_index, int patch_mode, int include_untracked, int only_staged)\n {\n \tint ret = 0;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct strbuf patch = STRBUF_INIT;\n \tstruct strbuf stash_msg_buf = STRBUF_INIT;\n \tstruct strbuf untracked_files = STRBUF_INIT;\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452739","messageId":"patch-v4-11.27-c9898a5a96b-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 11/27] revisions API users: add \"goto cleanup\" for release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:16Z","receivedAt":"2022-03-31T01:12:44Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a release_revisions() to various users of \"struct rev_info\" which\nrequires a minor refactoring to a \"goto cleanup\" pattern to use that\nfunction.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/diff-files.c        |  8 ++++++--\n builtin/rev-list.c          | 19 ++++++++++++-------\n builtin/stash.c             |  1 +\n builtin/submodule--helper.c | 10 +++++++---\n sequencer.c                 | 23 ++++++++++++++++-------\n t/helper/test-fast-rebase.c | 18 +++++++++++++-----\n 6 files changed, 55 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin/diff-files.c b/builtin/diff-files.c\nindex 70103c40952..2bfaf9ba7ae 100644\n--- a/builtin/diff-files.c\n+++ b/builtin/diff-files.c\n@@ -77,8 +77,12 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)\n \n \tif (read_cache_preload(&rev.diffopt.pathspec) < 0) {\n \t\tperror(\"read_cache_preload\");\n-\t\treturn -1;\n+\t\tresult = -1;\n+\t\tgoto cleanup;\n \t}\n+cleanup:\n \tresult = run_diff_files(&rev, options);\n-\treturn diff_result_code(&rev.diffopt, result);\n+\tresult = diff_result_code(&rev.diffopt, result);\n+\trelease_revisions(&rev);\n+\treturn result;\n }\ndiff --git a/builtin/rev-list.c b/builtin/rev-list.c\nindex 572da1472e5..2f0b6c759e4 100644\n--- a/builtin/rev-list.c\n+++ b/builtin/rev-list.c\n@@ -502,6 +502,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \tint use_bitmap_index = 0;\n \tint filter_provided_objects = 0;\n \tconst char *show_progress = NULL;\n+\tint ret = 0;\n \n \tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n \t\tusage(rev_list_usage);\n@@ -585,7 +586,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \t\t}\n \t\tif (!strcmp(arg, \"--test-bitmap\")) {\n \t\t\ttest_bitmap_walk(&revs);\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t\t}\n \t\tif (skip_prefix(arg, \"--progress=\", &arg)) {\n \t\t\tshow_progress = arg;\n@@ -674,11 +675,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \n \tif (use_bitmap_index) {\n \t\tif (!try_bitmap_count(&revs, filter_provided_objects))\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t\tif (!try_bitmap_disk_usage(&revs, filter_provided_objects))\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t\tif (!try_bitmap_traversal(&revs, filter_provided_objects))\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t}\n \n \tif (prepare_revision_walk(&revs))\n@@ -698,8 +699,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \n \t\tfind_bisection(&revs.commits, &reaches, &all, bisect_flags);\n \n-\t\tif (bisect_show_vars)\n-\t\t\treturn show_bisect_vars(&info, reaches, all);\n+\t\tif (bisect_show_vars) {\n+\t\t\tret = show_bisect_vars(&info, reaches, all);\n+\t\t\tgoto cleanup;\n+\t\t}\n \t}\n \n \tif (filter_provided_objects) {\n@@ -754,5 +757,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \tif (show_disk_usage)\n \t\tprintf(\"%\"PRIuMAX\"\\n\", (uintmax_t)total_disk_usage);\n \n-\treturn 0;\n+cleanup:\n+\trelease_revisions(&revs);\n+\treturn ret;\n }\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 891572d807c..76d162387e2 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -916,6 +916,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n cleanup:\n \tstrvec_clear(&stash_args);\n \tfree_stash_info(&info);\n+\trelease_revisions(&rev);\n \treturn ret;\n }\n \ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex cda33ee4d2b..1bd31c85946 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1232,6 +1232,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \tstruct strvec diff_args = STRVEC_INIT;\n \tstruct rev_info rev;\n \tstruct module_cb_list list = MODULE_CB_LIST_INIT;\n+\tint ret = 0;\n \n \tstrvec_push(&diff_args, get_diff_cmd(diff_cmd));\n \tif (info->cached)\n@@ -1257,11 +1258,13 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \t\t\tsetup_work_tree();\n \t\tif (read_cache_preload(&rev.diffopt.pathspec) < 0) {\n \t\t\tperror(\"read_cache_preload\");\n-\t\t\treturn -1;\n+\t\t\tret = -1;\n+\t\t\tgoto cleanup;\n \t\t}\n \t} else if (read_cache() < 0) {\n \t\tperror(\"read_cache\");\n-\t\treturn -1;\n+\t\tret = -1;\n+\t\tgoto cleanup;\n \t}\n \n \tif (diff_cmd == DIFF_INDEX)\n@@ -1269,9 +1272,10 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \telse\n \t\trun_diff_files(&rev, 0);\n \tprepare_submodule_summary(info, &list);\n+cleanup:\n \tstrvec_clear(&diff_args);\n \trelease_revisions(&rev);\n-\treturn 0;\n+\treturn ret;\n }\n \n static int module_summary(int argc, const char **argv, const char *prefix)\ndiff --git a/sequencer.c b/sequencer.c\nindex f9d7acd1065..41ae5e25278 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -5354,6 +5354,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \tint rebase_merges = flags & TODO_LIST_REBASE_MERGES;\n \tint reapply_cherry_picks = flags & TODO_LIST_REAPPLY_CHERRY_PICKS;\n \tint skipped_commit = 0;\n+\tint ret = 0;\n \n \trepo_init_revisions(r, &revs, NULL);\n \trevs.verbose_header = 1;\n@@ -5377,14 +5378,20 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \tpp.fmt = revs.commit_format;\n \tpp.output_encoding = get_log_output_encoding();\n \n-\tif (setup_revisions(argc, argv, &revs, NULL) > 1)\n-\t\treturn error(_(\"make_script: unhandled options\"));\n+\tif (setup_revisions(argc, argv, &revs, NULL) > 1) {\n+\t\tret = error(_(\"make_script: unhandled options\"));\n+\t\tgoto cleanup;\n+\t}\n \n-\tif (prepare_revision_walk(&revs) < 0)\n-\t\treturn error(_(\"make_script: error preparing revisions\"));\n+\tif (prepare_revision_walk(&revs) < 0) {\n+\t\tret = error(_(\"make_script: error preparing revisions\"));\n+\t\tgoto cleanup;\n+\t}\n \n-\tif (rebase_merges)\n-\t\treturn make_script_with_merges(&pp, &revs, out, flags);\n+\tif (rebase_merges) {\n+\t\tret = make_script_with_merges(&pp, &revs, out, flags);\n+\t\tgoto cleanup;\n+\t}\n \n \twhile ((commit = get_revision(&revs))) {\n \t\tint is_empty = is_original_commit_empty(commit);\n@@ -5408,7 +5415,9 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \tif (skipped_commit)\n \t\tadvise_if_enabled(ADVICE_SKIPPED_CHERRY_PICKS,\n \t\t\t\t  _(\"use --reapply-cherry-picks to include skipped commits\"));\n-\treturn 0;\n+cleanup:\n+\trelease_revisions(&revs);\n+\treturn ret;\n }\n \n /*\ndiff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c\nindex 993b90eaedd..4e5553e2024 100644\n--- a/t/helper/test-fast-rebase.c\n+++ b/t/helper/test-fast-rebase.c\n@@ -99,6 +99,7 @@ int cmd__fast_rebase(int argc, const char **argv)\n \tstruct merge_result result;\n \tstruct strbuf reflog_msg = STRBUF_INIT;\n \tstruct strbuf branch_name = STRBUF_INIT;\n+\tint ret = 0;\n \n \t/*\n \t * test-tool stuff doesn't set up the git directory by default; need to\n@@ -137,13 +138,17 @@ int cmd__fast_rebase(int argc, const char **argv)\n \trevs.topo_order = 1;\n \tstrvec_pushl(&rev_walk_args, \"\", argv[4], \"--not\", argv[3], NULL);\n \n-\tif (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1)\n-\t\treturn error(_(\"unhandled options\"));\n+\tif (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) {\n+\t\tret = error(_(\"unhandled options\"));\n+\t\tgoto cleanup;\n+\t}\n \n \tstrvec_clear(&rev_walk_args);\n \n-\tif (prepare_revision_walk(&revs) < 0)\n-\t\treturn error(_(\"error preparing revisions\"));\n+\tif (prepare_revision_walk(&revs) < 0) {\n+\t\tret = error(_(\"error preparing revisions\"));\n+\t\tgoto cleanup;\n+\t}\n \n \tinit_merge_options(&merge_opt, the_repository);\n \tmemset(&result, 0, sizeof(result));\n@@ -220,7 +225,10 @@ int cmd__fast_rebase(int argc, const char **argv)\n \t\t\t       COMMIT_LOCK | SKIP_IF_UNCHANGED))\n \t\tdie(_(\"unable to write %s\"), get_index_file());\n \n+\tret = (result.clean == 0);\n+cleanup:\n \tstrbuf_release(&reflog_msg);\n \tstrbuf_release(&branch_name);\n-\treturn (result.clean == 0);\n+\trelease_revisions(&revs);\n+\treturn ret;\n }\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452740","messageId":"patch-v4-13.27-02ca92660af-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 13/27] revisions API users: use release_revisions() in builtin/log.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:18Z","receivedAt":"2022-03-31T01:12:45Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In preparation for having the \"log\" family of functions make wider use\nof release_revisions() let's have them call it just before\nexiting. This changes the \"log\", \"whatchanged\", \"show\",\n\"format-patch\", etc. commands, all of which live in this file.\n\nThe release_revisions() API still only frees the \"pending\" member, but\nwill learn to release more members of \"struct rev_info\" in subsequent\ncommits.\n\nIn the case of \"format-patch\" revert the addition of UNLEAK() in\ndee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16),\nwhich will cause several tests that previously passed under\n\"TEST_PASSES_SANITIZE_LEAK=true\" to start failing.\n\nIn subsequent commits we'll now be able to use those tests to check\nwhether that part of the API is really leaking memory, and will fix\nall of those memory leaks. Removing the UNLEAK() allows us to make\nincremental progress in that direction. See [1] for further details\nabout this approach.\n\nNote that the release_revisions() will not be sufficient to deal with\nthe code in cmd_show() added in 5d7eeee2ac6 (git-show: grok blobs,\ntrees and tags, too, 2006-12-14) which clobbers the \"pending\" array in\nthe case of \"OBJ_COMMIT\". That will need to be dealt with by some\nfuture follow-up work.\n\n1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/log.c          | 20 ++++++++++++--------\n t/t4126-apply-empty.sh |  2 --\n 2 files changed, 12 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 6f9928fabfe..c40ebe1c3f4 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -295,6 +295,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,\n \tcmd_log_init_finish(argc, argv, prefix, rev, opt);\n }\n \n+static int cmd_log_deinit(int ret, struct rev_info *rev)\n+{\n+\trelease_revisions(rev);\n+\treturn ret;\n+}\n+\n /*\n  * This gives a rough estimate for how many commits we\n  * will print out in the list.\n@@ -558,7 +564,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)\n \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n \tif (!rev.diffopt.output_format)\n \t\trev.diffopt.output_format = DIFF_FORMAT_RAW;\n-\treturn cmd_log_walk(&rev);\n+\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n }\n \n static void show_tagger(const char *buf, struct rev_info *rev)\n@@ -677,7 +683,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n \n \tif (!rev.no_walk)\n-\t\treturn cmd_log_walk(&rev);\n+\t\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n \n \tcount = rev.pending.nr;\n \tobjects = rev.pending.objects;\n@@ -732,8 +738,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \t\t\tret = error(_(\"unknown type: %d\"), o->type);\n \t\t}\n \t}\n-\tfree(objects);\n-\treturn ret;\n+\treturn cmd_log_deinit(ret, &rev);\n }\n \n /*\n@@ -761,7 +766,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)\n \trev.always_show_header = 1;\n \tcmd_log_init_finish(argc, argv, prefix, &rev, &opt);\n \n-\treturn cmd_log_walk(&rev);\n+\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n }\n \n static void log_setup_revisions_tweak(struct rev_info *rev,\n@@ -792,7 +797,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)\n \topt.revarg_opt = REVARG_COMMITTISH;\n \topt.tweak = log_setup_revisions_tweak;\n \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n-\treturn cmd_log_walk(&rev);\n+\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n }\n \n /* format-patch */\n@@ -2289,8 +2294,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tif (rev.ref_message_ids)\n \t\tstring_list_clear(rev.ref_message_ids, 0);\n \tfree(rev.ref_message_ids);\n-\tUNLEAK(rev);\n-\treturn 0;\n+\treturn cmd_log_deinit(0, &rev);\n }\n \n static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)\ndiff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh\nindex 33860d38290..66a7ba8ab8f 100755\n--- a/t/t4126-apply-empty.sh\n+++ b/t/t4126-apply-empty.sh\n@@ -2,8 +2,6 @@\n \n test_description='apply empty'\n \n-\n-TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452741","messageId":"patch-v4-14.27-1aeb1127f34-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 14/27] revisions API users: use release_revisions() with UNLEAK()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:19Z","receivedAt":"2022-03-31T01:12:47Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use a release_revisions() with those \"struct rev_list\" users which\nalready \"UNLEAK\" the struct. It may seem odd to simultaneously attempt\nto free() memory, but also to explicitly ignore whether we have memory\nleaks in the same.\n\nAs explained in preceding commits this is being done to use the\nbuilt-in commands as a guinea pig for whether the release_revisions()\nfunction works as expected, we'd like to test e.g. whether we segfault\nas we change it. In subsequent commits we'll then remove these\nUNLEAK() as the function is made to free the memory that caused us to\nadd them in the first place.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/diff-index.c | 4 +++-\n builtin/diff.c       | 1 +\n 2 files changed, 4 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/diff-index.c b/builtin/diff-index.c\nindex 5fd23ab5b6c..3a83183c312 100644\n--- a/builtin/diff-index.c\n+++ b/builtin/diff-index.c\n@@ -71,5 +71,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)\n \t}\n \tresult = run_diff_index(&rev, option);\n \tUNLEAK(rev);\n-\treturn diff_result_code(&rev.diffopt, result);\n+\tresult = diff_result_code(&rev.diffopt, result);\n+\trelease_revisions(&rev);\n+\treturn result;\n }\ndiff --git a/builtin/diff.c b/builtin/diff.c\nindex bb7fafca618..dd48336da56 100644\n--- a/builtin/diff.c\n+++ b/builtin/diff.c\n@@ -595,6 +595,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \tif (1 < rev.diffopt.skip_stat_unmatch)\n \t\trefresh_index_quietly();\n \tUNLEAK(rev);\n+\trelease_revisions(&rev);\n \tUNLEAK(ent);\n \tUNLEAK(blob);\n \treturn result;\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452742","messageId":"patch-v4-15.27-c12b3d5a035-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 15/27] revisions API users: use release_revisions() for \"prune_data\" users","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:20Z","receivedAt":"2022-03-31T01:12:49Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use release_revisions() for users of \"struct rev_list\" that reach into\nthe \"struct rev_info\" and clear the \"prune_data\" already.\n\nIn a subsequent commit we'll teach release_revisions() to clear this\nitself, but in the meantime let's invoke release_revisions() here to\nclear anything else we may have missed, and for reasons of having\nconsistent boilerplate.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/add.c   | 1 +\n builtin/stash.c | 1 +\n diff-lib.c      | 1 +\n wt-status.c     | 1 +\n 4 files changed, 4 insertions(+)\n\ndiff --git a/builtin/add.c b/builtin/add.c\nindex f507d2191cd..115a26ea633 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -143,6 +143,7 @@ int add_files_to_cache(const char *prefix,\n \trev.max_count = 0; /* do not compare unmerged paths with stage #2 */\n \trun_diff_files(&rev, DIFF_RACY_IS_MODIFIED);\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n \treturn !!data.add_errors;\n }\n \ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 76d162387e2..a6ee030d4bd 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1060,6 +1060,7 @@ static int check_changes_tracked_files(const struct pathspec *ps)\n \n done:\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n \treturn ret;\n }\n \ndiff --git a/diff-lib.c b/diff-lib.c\nindex d6800274bd5..0f16281253f 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -642,6 +642,7 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)\n \tif (diff_cache(&revs, tree_oid, NULL, 1))\n \t\texit(128);\n \tclear_pathspec(&revs.prune_data);\n+\trelease_revisions(&revs);\n \treturn 0;\n }\n \ndiff --git a/wt-status.c b/wt-status.c\nindex f9100621375..a14fad1e03a 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -617,6 +617,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)\n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_files(&rev, 0);\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n }\n \n static void wt_status_collect_changes_index(struct wt_status *s)\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452743","messageId":"patch-v4-17.27-f67f1f5a575-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 17/27] revisions API: have release_revisions() release \"mailmap\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:22Z","receivedAt":"2022-03-31T01:12:52Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"mailmap\" in the \"struct rev_info\".\n\nThe log family of functions now calls the clear_mailmap() function\nadded in fa8afd18e5a (revisions API: provide and use a\nrelease_revisions(), 2021-09-19), allowing us to whitelist some tests\nwith \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nUnfortunately having a pointer to a mailmap in \"struct rev_info\"\ninstead of an embedded member that we \"own\" get a bit messy, as can be\nseen in the change to builtin/commit.c.\n\nWhen we free() this data we won't be able to tell apart a pointer to a\n\"mailmap\" on the heap from one on the stack. As seen in\nea57bc0d41b (log: add --use-mailmap option, 2013-01-05) the \"log\"\nfamily allocates it on the heap, but in the find_author_by_nickname()\ncode added in ea16794e430 (commit: search author pattern against\nmailmap, 2013-08-23) we allocated it on the stack instead.\n\nIdeally we'd simply change that member to a \"struct string_list\nmailmap\" and never free() the \"mailmap\" itself, but that would be a\nmuch larger change to the revisions API.\n\nWe have code that needs to hand an existing \"mailmap\" to a \"struct\nrev_info\", while we could change all of that, let's not go there\nnow.\n\nThe complexity isn't in the ownership of the \"mailmap\" per-se, but\nthat various things assume a \"rev_info.mailmap == NULL\" means \"doesn't\nwant mailmap\", if we changed that to an init'd \"struct string_list\nwe'd need to carefully refactor things to change those assumptions.\n\nLet's instead always free() it, and simply declare that if you add\nsuch a \"mailmap\" it must be allocated on the heap. Any modern libc\nwill correctly panic if we free() a stack variable, so this should be\nsafe going forward.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/commit.c                   | 5 ++---\n revision.c                         | 9 +++++++++\n t/t0056-git-C.sh                   | 1 +\n t/t3302-notes-index-expensive.sh   | 1 +\n t/t4055-diff-context.sh            | 1 +\n t/t4066-diff-emit-delay.sh         | 1 +\n t/t7008-filter-branch-null-sha1.sh | 1 +\n 7 files changed, 16 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex c7eda9bbb72..cd6cebcf8c8 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1100,7 +1100,6 @@ static const char *find_author_by_nickname(const char *name)\n \tstruct rev_info revs;\n \tstruct commit *commit;\n \tstruct strbuf buf = STRBUF_INIT;\n-\tstruct string_list mailmap = STRING_LIST_INIT_NODUP;\n \tconst char *av[20];\n \tint ac = 0;\n \n@@ -1111,7 +1110,8 @@ static const char *find_author_by_nickname(const char *name)\n \tav[++ac] = buf.buf;\n \tav[++ac] = NULL;\n \tsetup_revisions(ac, av, &revs, NULL);\n-\trevs.mailmap = &mailmap;\n+\trevs.mailmap = xmalloc(sizeof(struct string_list));\n+\tstring_list_init_nodup(revs.mailmap);\n \tread_mailmap(revs.mailmap);\n \n \tif (prepare_revision_walk(&revs))\n@@ -1122,7 +1122,6 @@ static const char *find_author_by_nickname(const char *name)\n \t\tctx.date_mode.type = DATE_NORMAL;\n \t\tstrbuf_release(&buf);\n \t\tformat_commit_message(commit, \"%aN <%aE>\", &buf, &ctx);\n-\t\tclear_mailmap(&mailmap);\n \t\trelease_revisions(&revs);\n \t\treturn strbuf_detach(&buf, NULL);\n \t}\ndiff --git a/revision.c b/revision.c\nindex 394e2dcdd90..965e913ebba 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2935,10 +2935,19 @@ static void release_revisions_commit_list(struct rev_info *revs)\n \t}\n }\n \n+static void release_revisions_mailmap(struct string_list *mailmap)\n+{\n+\tif (!mailmap)\n+\t\treturn;\n+\tclear_mailmap(mailmap);\n+\tfree(mailmap);\n+}\n+\n void release_revisions(struct rev_info *revs)\n {\n \trelease_revisions_commit_list(revs);\n \tobject_array_clear(&revs->pending);\n+\trelease_revisions_mailmap(revs->mailmap);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\ndiff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh\nindex 2630e756dab..752aa8c9454 100755\n--- a/t/t0056-git-C.sh\n+++ b/t/t0056-git-C.sh\n@@ -2,6 +2,7 @@\n \n test_description='\"-C <path>\" option and its effects on other path-related options'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success '\"git -C <path>\" runs git from the directory <path>' '\ndiff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh\nindex bb5fea02a03..d0c4d38b4d4 100755\n--- a/t/t3302-notes-index-expensive.sh\n+++ b/t/t3302-notes-index-expensive.sh\n@@ -8,6 +8,7 @@ test_description='Test commit notes index (expensive!)'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n create_repo () {\ndiff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh\nindex 741e0803c1a..73048d0a526 100755\n--- a/t/t4055-diff-context.sh\n+++ b/t/t4055-diff-context.sh\n@@ -5,6 +5,7 @@\n \n test_description='diff.context configuration'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t4066-diff-emit-delay.sh b/t/t4066-diff-emit-delay.sh\nindex a1de63b77f8..0ecb3915412 100755\n--- a/t/t4066-diff-emit-delay.sh\n+++ b/t/t4066-diff-emit-delay.sh\n@@ -4,6 +4,7 @@ test_description='test combined/stat/moved interaction'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # This test covers a weird 3-way interaction between \"--cc -p\", which will run\ndiff --git a/t/t7008-filter-branch-null-sha1.sh b/t/t7008-filter-branch-null-sha1.sh\nindex 9ba9f24ad2f..93fbc92b8db 100755\n--- a/t/t7008-filter-branch-null-sha1.sh\n+++ b/t/t7008-filter-branch-null-sha1.sh\n@@ -1,6 +1,7 @@\n #!/bin/sh\n \n test_description='filter-branch removal of trees with null sha1'\n+\n . ./test-lib.sh\n \n test_expect_success 'setup: base commits' '\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452744","messageId":"patch-v4-19.27-065c3cc91b2-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 19/27] revisions API: have release_revisions() release \"filter\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:24Z","receivedAt":"2022-03-31T01:12:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"filter\" in the \"struct rev_info\". This in combination with a\npreceding change to free \"cmdline\" means that we can mark another set\nof tests as passing under \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nThe \"filter\" member was added recently in ffaa137f646 (revision: put\nobject filter into struct rev_info, 2022-03-09), and this fixes leaks\nintruded in the subsequent leak 7940941de1f (pack-objects: use\nrev.filter when possible, 2022-03-09) and 105c6f14ad3 (bundle: parse\nfilter capability, 2022-03-09).\n\nThe \"builtin/pack-objects.c\" leak in 7940941de1f was effectively with\nus already, but the variable was referred to by a \"static\" file-scoped\nvariable. The \"bundle.c \" leak in 105c6f14ad3 was newly introduced\nwith the new \"filter\" feature for bundles.\n\nThe \"t5600-clone-fail-cleanup.sh\" change here to add\n\"TEST_PASSES_SANITIZE_LEAK=true\" is one of the cases where\nrun-command.c in not carrying the abort() exit code upwards would have\nhad that test passing before, but now it *actually* passes[1]. We\nshould fix the lack of 1=1 mapping of SANITIZE=leak testing to actual\nleaks some other time, but it's an existing edge case, let's just mark\nthe really-passing test as passing for now.\n\n1. https://lore.kernel.org/git/220303.86fsnz5o9w.gmgdl@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c                         | 1 +\n t/t1060-object-corruption.sh       | 1 +\n t/t2015-checkout-unborn.sh         | 1 +\n t/t4207-log-decoration-colors.sh   | 1 +\n t/t5301-sliding-window.sh          | 2 ++\n t/t5313-pack-bounds-checks.sh      | 2 ++\n t/t5316-pack-delta-depth.sh        | 2 ++\n t/t5320-delta-islands.sh           | 2 ++\n t/t5322-pack-objects-sparse.sh     | 1 +\n t/t5506-remote-groups.sh           | 1 +\n t/t5513-fetch-track.sh             | 1 +\n t/t5532-fetch-proxy.sh             | 2 ++\n t/t5600-clone-fail-cleanup.sh      | 1 +\n t/t5900-repo-selection.sh          | 2 ++\n t/t6101-rev-parse-parents.sh       | 1 +\n t/t6114-keep-packs.sh              | 2 ++\n t/t7702-repack-cyclic-alternate.sh | 2 ++\n t/t9127-git-svn-partial-rebuild.sh | 1 -\n 18 files changed, 25 insertions(+), 1 deletion(-)\n\ndiff --git a/revision.c b/revision.c\nindex 8177afebda8..2d52f1b518d 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2959,6 +2959,7 @@ void release_revisions(struct rev_info *revs)\n \trelease_revisions_commit_list(revs);\n \tobject_array_clear(&revs->pending);\n \trelease_revisions_cmdline(&revs->cmdline);\n+\tlist_objects_filter_release(&revs->filter);\n \trelease_revisions_mailmap(revs->mailmap);\n }\n \ndiff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh\nindex e8a58b15897..5b8e47e346c 100755\n--- a/t/t1060-object-corruption.sh\n+++ b/t/t1060-object-corruption.sh\n@@ -2,6 +2,7 @@\n \n test_description='see how we handle various forms of corruption'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # convert \"1234abcd\" to \".git/objects/12/34abcd\"\ndiff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh\nindex a9721215fab..9425aae6395 100755\n--- a/t/t2015-checkout-unborn.sh\n+++ b/t/t2015-checkout-unborn.sh\n@@ -4,6 +4,7 @@ test_description='checkout from unborn branch'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh\nindex b8709424981..36ac6aff1e4 100755\n--- a/t/t4207-log-decoration-colors.sh\n+++ b/t/t4207-log-decoration-colors.sh\n@@ -8,6 +8,7 @@ test_description='Test for \"git log --decorate\" colors'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t5301-sliding-window.sh b/t/t5301-sliding-window.sh\nindex 76f9798ab95..3ccaaeb3977 100755\n--- a/t/t5301-sliding-window.sh\n+++ b/t/t5301-sliding-window.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='mmap sliding window tests'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success \\\ndiff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh\nindex 535313e4dc8..cc4cfaa9d37 100755\n--- a/t/t5313-pack-bounds-checks.sh\n+++ b/t/t5313-pack-bounds-checks.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='bounds-checking of access to mmapped on-disk file formats'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n clear_base () {\ndiff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh\nindex e9045009a11..eb4ef3dda4d 100755\n--- a/t/t5316-pack-delta-depth.sh\n+++ b/t/t5316-pack-delta-depth.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='pack-objects breaks long cross-pack delta chains'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # This mirrors a repeated push setup:\ndiff --git a/t/t5320-delta-islands.sh b/t/t5320-delta-islands.sh\nindex fea92a5777f..124d47603df 100755\n--- a/t/t5320-delta-islands.sh\n+++ b/t/t5320-delta-islands.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='exercise delta islands'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # returns true iff $1 is a delta based on $2\ndiff --git a/t/t5322-pack-objects-sparse.sh b/t/t5322-pack-objects-sparse.sh\nindex d39958c066d..770695c9278 100755\n--- a/t/t5322-pack-objects-sparse.sh\n+++ b/t/t5322-pack-objects-sparse.sh\n@@ -4,6 +4,7 @@ test_description='pack-objects object selection using sparse algorithm'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup repo' '\ndiff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh\nindex 8f150c0793e..5bac03ede81 100755\n--- a/t/t5506-remote-groups.sh\n+++ b/t/t5506-remote-groups.sh\n@@ -4,6 +4,7 @@ test_description='git remote group handling'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n mark() {\ndiff --git a/t/t5513-fetch-track.sh b/t/t5513-fetch-track.sh\nindex 65d1e05bd62..c46c4dbaefc 100755\n--- a/t/t5513-fetch-track.sh\n+++ b/t/t5513-fetch-track.sh\n@@ -2,6 +2,7 @@\n \n test_description='fetch follows remote-tracking branches correctly'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh\nindex 9c2798603b4..d664912799b 100755\n--- a/t/t5532-fetch-proxy.sh\n+++ b/t/t5532-fetch-proxy.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='fetching via git:// using core.gitproxy'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup remote repo' '\ndiff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh\nindex 34b3df40275..c814afa5656 100755\n--- a/t/t5600-clone-fail-cleanup.sh\n+++ b/t/t5600-clone-fail-cleanup.sh\n@@ -13,6 +13,7 @@ Unless the directory already exists, in which case we clean up only what we\n wrote.\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n corrupt_repo () {\ndiff --git a/t/t5900-repo-selection.sh b/t/t5900-repo-selection.sh\nindex 14e59c5b3e4..a84faac242d 100755\n--- a/t/t5900-repo-selection.sh\n+++ b/t/t5900-repo-selection.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='selecting remote repo in ambiguous cases'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n reset() {\ndiff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh\nindex c571fa51797..a3a41c7a3e4 100755\n--- a/t/t6101-rev-parse-parents.sh\n+++ b/t/t6101-rev-parse-parents.sh\n@@ -8,6 +8,7 @@ test_description='Test git rev-parse with different parent options'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_cmp_rev_output () {\ndiff --git a/t/t6114-keep-packs.sh b/t/t6114-keep-packs.sh\nindex 9239d8aa46d..44246f8a63e 100755\n--- a/t/t6114-keep-packs.sh\n+++ b/t/t6114-keep-packs.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='rev-list with .keep packs'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t7702-repack-cyclic-alternate.sh b/t/t7702-repack-cyclic-alternate.sh\nindex 93b74867ac8..f3cdb98eec2 100755\n--- a/t/t7702-repack-cyclic-alternate.sh\n+++ b/t/t7702-repack-cyclic-alternate.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='repack involving cyclic alternate'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh\nindex 90b1b30dde5..97f495bd49b 100755\n--- a/t/t9127-git-svn-partial-rebuild.sh\n+++ b/t/t9127-git-svn-partial-rebuild.sh\n@@ -5,7 +5,6 @@\n \n test_description='git svn partial-rebuild tests'\n \n-TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize svnrepo' '\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452745","messageId":"patch-v4-21.27-fabbe452a14-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 21/27] revisions API: have release_revisions() release \"prune_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:26Z","receivedAt":"2022-03-31T01:12:55Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"prune_data\" in the \"struct rev_info\". This means that any code that\ncalls \"release_revisions()\" already can get rid of adjacent calls to\nclear_pathspec().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n add-interactive.c | 2 --\n builtin/add.c     | 1 -\n builtin/stash.c   | 2 --\n diff-lib.c        | 1 -\n revision.c        | 1 +\n wt-status.c       | 2 --\n 6 files changed, 1 insertion(+), 8 deletions(-)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex 54cdfc82017..6047e8f6489 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -568,8 +568,6 @@ static int get_modified_files(struct repository *r,\n \t\t\trun_diff_files(&rev, 0);\n \t\t}\n \n-\t\tif (ps)\n-\t\t\tclear_pathspec(&rev.prune_data);\n \t\trelease_revisions(&rev);\n \t}\n \thashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 115a26ea633..fc729e14c17 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -142,7 +142,6 @@ int add_files_to_cache(const char *prefix,\n \trev.diffopt.flags.override_submodule_config = 1;\n \trev.max_count = 0; /* do not compare unmerged paths with stage #2 */\n \trun_diff_files(&rev, DIFF_RACY_IS_MODIFIED);\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n \treturn !!data.add_errors;\n }\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex a6ee030d4bd..5237d0d061a 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1059,7 +1059,6 @@ static int check_changes_tracked_files(const struct pathspec *ps)\n \t}\n \n done:\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n \treturn ret;\n }\n@@ -1271,7 +1270,6 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps\n \n done:\n \tdiscard_index(&istate);\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n \tstrbuf_release(&diff_output);\n \tremove_path(stash_index_path.buf);\ndiff --git a/diff-lib.c b/diff-lib.c\nindex 0f16281253f..298265e5b54 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -641,7 +641,6 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)\n \n \tif (diff_cache(&revs, tree_oid, NULL, 1))\n \t\texit(128);\n-\tclear_pathspec(&revs.prune_data);\n \trelease_revisions(&revs);\n \treturn 0;\n }\ndiff --git a/revision.c b/revision.c\nindex 349c3f77536..c7a0e08765a 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2960,6 +2960,7 @@ void release_revisions(struct rev_info *revs)\n \tobject_array_clear(&revs->pending);\n \trelease_revisions_cmdline(&revs->cmdline);\n \tlist_objects_filter_release(&revs->filter);\n+\tclear_pathspec(&revs->prune_data);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n }\ndiff --git a/wt-status.c b/wt-status.c\nindex a14fad1e03a..61e0c1022f5 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -616,7 +616,6 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)\n \trev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;\n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_files(&rev, 0);\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n }\n \n@@ -664,7 +663,6 @@ static void wt_status_collect_changes_index(struct wt_status *s)\n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_index(&rev, 1);\n \trelease_revisions(&rev);\n-\tclear_pathspec(&rev.prune_data);\n }\n \n static int add_file_to_list(const struct object_id *oid,\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452746","messageId":"patch-v4-18.27-1df61550e5a-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 18/27] revisions API: have release_revisions() release \"cmdline\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:23Z","receivedAt":"2022-03-31T01:12:58Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"cmdline\" in the \"struct rev_info\". This in combination with a\npreceding change to free \"commits\" and \"mailmap\" means that we can\nwhitelist another test under \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nThere was a proposal in [1] to do away with xstrdup()-ing this\nadd_rev_cmdline(), perhaps that would be worthwhile, but for now let's\njust free() it.\n\nWe could also make that a \"char *\" in \"struct rev_cmdline_entry\"\nitself, but since we own it let's expose it as a constant to outside\ncallers. I proposed that in [2] but have since changed my mind. See\n14d30cdfc04 (ref-filter: fix memory leak in `free_array_item()`,\n2019-07-10), c514c62a4fd (checkout: fix leak of non-existent branch\nnames, 2020-08-14) and other log history hits for \"free((char *)\" for\nprior art.\n\nThis includes the tests we had false-positive passes on before my\n6798b08e848 (perl Git.pm: don't ignore signalled failure in\n_cmd_close(), 2022-02-01), now they pass for real.\n\nSince there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to\nlist those that don't pass than to touch most of those 66. So let's\nintroduce a \"TEST_FAILS_SANITIZE_LEAK=true\", which if set in the tests\nwon't cause lib-git-svn.sh to set \"TEST_PASSES_SANITIZE_LEAK=true.\n\nThis change also marks all the tests that we removed\n\"TEST_FAILS_SANITIZE_LEAK=true\" from in an earlier commit due to\nremoving the UNLEAK() from cmd_format_patch(), we can now assert that\nits API use doesn't leak any \"struct rev_info\" memory.\n\nThis change also made commit \"t5503-tagfollow.sh\" pass on current\nmaster, but that would regress when combined with\nps/fetch-atomic-fixup's de004e848a9 (t5503: simplify setup of test\nwhich exercises failure of backfill, 2022-03-03) (through no fault of\nthat topic, that change started using \"git clone\" in the test, which\nhas an outstanding leak). Let's leave that test out for now to avoid\nin-flight semantic conflicts.\n\n1. https://lore.kernel.org/git/YUj%2FgFRh6pwrZalY@carlos-mbp.lan/\n2. https://lore.kernel.org/git/87o88obkb1.fsf@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c                                 | 12 ++++++++++++\n t/lib-git-svn.sh                           |  4 ++++\n t/t0062-revision-walking.sh                |  1 +\n t/t0101-at-syntax.sh                       |  2 ++\n t/t1060-object-corruption.sh               |  1 +\n t/t3303-notes-subtrees.sh                  |  1 +\n t/t3305-notes-fanout.sh                    |  1 +\n t/t3408-rebase-multi-line.sh               |  1 +\n t/t4027-diff-submodule.sh                  |  1 +\n t/t4128-apply-root.sh                      |  1 +\n t/t4212-log-corrupt.sh                     |  1 +\n t/t5515-fetch-merge-logic.sh               |  1 +\n t/t5518-fetch-exit-status.sh               |  1 +\n t/t6002-rev-list-bisect.sh                 |  1 +\n t/t6003-rev-list-topo-order.sh             |  1 +\n t/t6005-rev-list-count.sh                  |  1 +\n t/t6018-rev-list-glob.sh                   |  1 +\n t/t6100-rev-list-in-order.sh               |  1 +\n t/t9100-git-svn-basic.sh                   |  1 +\n t/t9101-git-svn-props.sh                   |  2 ++\n t/t9104-git-svn-follow-parent.sh           |  2 ++\n t/t9106-git-svn-commit-diff-clobber.sh     |  2 ++\n t/t9115-git-svn-dcommit-funky-renames.sh   |  1 +\n t/t9116-git-svn-log.sh                     |  2 ++\n t/t9122-git-svn-author.sh                  |  2 ++\n t/t9127-git-svn-partial-rebuild.sh         |  2 ++\n t/t9129-git-svn-i18n-commitencoding.sh     |  1 +\n t/t9132-git-svn-broken-symlink.sh          |  1 +\n t/t9139-git-svn-non-utf8-commitencoding.sh |  1 +\n t/t9146-git-svn-empty-dirs.sh              |  2 ++\n t/t9148-git-svn-propset.sh                 |  1 +\n t/t9151-svn-mergeinfo.sh                   |  1 +\n t/t9160-git-svn-preserve-empty-dirs.sh     |  1 +\n t/t9162-git-svn-dcommit-interactive.sh     |  2 ++\n t/t9164-git-svn-dcommit-concurrent.sh      |  2 ++\n t/t9501-gitweb-standalone-http-status.sh   |  1 +\n 36 files changed, 60 insertions(+)\n\ndiff --git a/revision.c b/revision.c\nindex 965e913ebba..8177afebda8 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2935,6 +2935,17 @@ static void release_revisions_commit_list(struct rev_info *revs)\n \t}\n }\n \n+static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)\n+{\n+\tunsigned int i;\n+\n+\tif (!cmdline)\n+\t\treturn;\n+\tfor (i = 0; i < cmdline->nr; i++)\n+\t\tfree((char *)cmdline->rev[i].name);\n+\tFREE_AND_NULL(cmdline->rev);\n+}\n+\n static void release_revisions_mailmap(struct string_list *mailmap)\n {\n \tif (!mailmap)\n@@ -2947,6 +2958,7 @@ void release_revisions(struct rev_info *revs)\n {\n \trelease_revisions_commit_list(revs);\n \tobject_array_clear(&revs->pending);\n+\trelease_revisions_cmdline(&revs->cmdline);\n \trelease_revisions_mailmap(revs->mailmap);\n }\n \ndiff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh\nindex 2fde2353fd3..ea28971e8ee 100644\n--- a/t/lib-git-svn.sh\n+++ b/t/lib-git-svn.sh\n@@ -1,3 +1,7 @@\n+if test -z \"$TEST_FAILS_SANITIZE_LEAK\"\n+then\n+\tTEST_PASSES_SANITIZE_LEAK=true\n+fi\n . ./test-lib.sh\n \n if test -n \"$NO_SVN_TESTS\"\ndiff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh\nindex 8e215867b8c..b9480c81781 100755\n--- a/t/t0062-revision-walking.sh\n+++ b/t/t0062-revision-walking.sh\n@@ -5,6 +5,7 @@\n \n test_description='Test revision walking api'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n cat >run_twice_expected <<-EOF\ndiff --git a/t/t0101-at-syntax.sh b/t/t0101-at-syntax.sh\nindex a1998b558f9..878aadd64c9 100755\n--- a/t/t0101-at-syntax.sh\n+++ b/t/t0101-at-syntax.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='various @{whatever} syntax tests'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh\nindex bc89371f534..e8a58b15897 100755\n--- a/t/t1060-object-corruption.sh\n+++ b/t/t1060-object-corruption.sh\n@@ -1,6 +1,7 @@\n #!/bin/sh\n \n test_description='see how we handle various forms of corruption'\n+\n . ./test-lib.sh\n \n # convert \"1234abcd\" to \".git/objects/12/34abcd\"\ndiff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh\nindex eac193757bf..bc9b791d1b9 100755\n--- a/t/t3303-notes-subtrees.sh\n+++ b/t/t3303-notes-subtrees.sh\n@@ -5,6 +5,7 @@ test_description='Test commit notes organized in subtrees'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n number_of_commits=100\ndiff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh\nindex 9976d787f47..64a9915761a 100755\n--- a/t/t3305-notes-fanout.sh\n+++ b/t/t3305-notes-fanout.sh\n@@ -2,6 +2,7 @@\n \n test_description='Test that adding/removing many notes triggers automatic fanout restructuring'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n path_has_fanout() {\ndiff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh\nindex cde3562e3a6..7b4607d72f2 100755\n--- a/t/t3408-rebase-multi-line.sh\n+++ b/t/t3408-rebase-multi-line.sh\n@@ -5,6 +5,7 @@ test_description='rebasing a commit with multi-line first paragraph.'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh\nindex 295da987cce..40164ae07d2 100755\n--- a/t/t4027-diff-submodule.sh\n+++ b/t/t4027-diff-submodule.sh\n@@ -2,6 +2,7 @@\n \n test_description='difference in submodules'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-diff.sh\n \ndiff --git a/t/t4128-apply-root.sh b/t/t4128-apply-root.sh\nindex f6db5a79dd9..ed94c90204e 100755\n--- a/t/t4128-apply-root.sh\n+++ b/t/t4128-apply-root.sh\n@@ -2,6 +2,7 @@\n \n test_description='apply same filename'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh\nindex 0244888a5a7..30a219894bb 100755\n--- a/t/t4212-log-corrupt.sh\n+++ b/t/t4212-log-corrupt.sh\n@@ -2,6 +2,7 @@\n \n test_description='git log with invalid commit headers'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh\nindex 320d26796d2..c100a809c5e 100755\n--- a/t/t5515-fetch-merge-logic.sh\n+++ b/t/t5515-fetch-merge-logic.sh\n@@ -14,6 +14,7 @@ export GIT_TEST_PROTOCOL_VERSION\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n build_script () {\ndiff --git a/t/t5518-fetch-exit-status.sh b/t/t5518-fetch-exit-status.sh\nindex 5c4ac2556e7..c13120088fa 100755\n--- a/t/t5518-fetch-exit-status.sh\n+++ b/t/t5518-fetch-exit-status.sh\n@@ -8,6 +8,7 @@ test_description='fetch exit status test'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh\nindex b95a0212adf..162cf50778d 100755\n--- a/t/t6002-rev-list-bisect.sh\n+++ b/t/t6002-rev-list-bisect.sh\n@@ -4,6 +4,7 @@\n #\n test_description='Tests git rev-list --bisect functionality'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-t6000.sh # t6xxx specific functions\n \ndiff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh\nindex 24d1836f417..1f7d7dd20c1 100755\n--- a/t/t6003-rev-list-topo-order.sh\n+++ b/t/t6003-rev-list-topo-order.sh\n@@ -5,6 +5,7 @@\n \n test_description='Tests git rev-list --topo-order functionality'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-t6000.sh # t6xxx specific functions\n \ndiff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh\nindex e960049f647..0729f800c3c 100755\n--- a/t/t6005-rev-list-count.sh\n+++ b/t/t6005-rev-list-count.sh\n@@ -2,6 +2,7 @@\n \n test_description='git rev-list --max-count and --skip test'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh\nindex 24b34add833..e1abc5c2b32 100755\n--- a/t/t6018-rev-list-glob.sh\n+++ b/t/t6018-rev-list-glob.sh\n@@ -5,6 +5,7 @@ test_description='rev-list/rev-parse --glob'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n commit () {\ndiff --git a/t/t6100-rev-list-in-order.sh b/t/t6100-rev-list-in-order.sh\nindex e934bc239c5..88ed7bd75a7 100755\n--- a/t/t6100-rev-list-in-order.sh\n+++ b/t/t6100-rev-list-in-order.sh\n@@ -2,6 +2,7 @@\n \n test_description='rev-list testing in-commit-order'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup a commit history with trees, blobs' '\ndiff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh\nindex fea41b3c360..7c5b847f584 100755\n--- a/t/t9100-git-svn-basic.sh\n+++ b/t/t9100-git-svn-basic.sh\n@@ -8,6 +8,7 @@ test_description='git svn basic tests'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n prepare_utf8_locale\ndiff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh\nindex 8b5681dd68a..d043e80fc34 100755\n--- a/t/t9101-git-svn-props.sh\n+++ b/t/t9101-git-svn-props.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn property tests'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n mkdir import\ndiff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh\nindex c7d8e0bf00f..5cf2ef4b8b0 100755\n--- a/t/t9104-git-svn-follow-parent.sh\n+++ b/t/t9104-git-svn-follow-parent.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn fetching'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh\nindex aec45bca3b7..3cab0b9720a 100755\n--- a/t/t9106-git-svn-commit-diff-clobber.sh\n+++ b/t/t9106-git-svn-commit-diff-clobber.sh\n@@ -2,6 +2,8 @@\n #\n # Copyright (c) 2006 Eric Wong\n test_description='git svn commit-diff clobber'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh\nindex 743fbe1fe46..419f055721d 100755\n--- a/t/t9115-git-svn-dcommit-funky-renames.sh\n+++ b/t/t9115-git-svn-dcommit-funky-renames.sh\n@@ -5,6 +5,7 @@\n \n test_description='git svn dcommit can commit renames of files with ugly names'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'load repository with strange names' '\ndiff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh\nindex 0a9f1ef366d..34f6c80dea3 100755\n--- a/t/t9116-git-svn-log.sh\n+++ b/t/t9116-git-svn-log.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn log tests'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup repository and import' '\ndiff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh\nindex 9e8fe38e7ef..527ba3d2932 100755\n--- a/t/t9122-git-svn-author.sh\n+++ b/t/t9122-git-svn-author.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='git svn authorship'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup svn repository' '\ndiff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh\nindex 2e4789d061f..90b1b30dde5 100755\n--- a/t/t9127-git-svn-partial-rebuild.sh\n+++ b/t/t9127-git-svn-partial-rebuild.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn partial-rebuild tests'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize svnrepo' '\ndiff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh\nindex 01e1e8a8f76..185248a4cd7 100755\n--- a/t/t9129-git-svn-i18n-commitencoding.sh\n+++ b/t/t9129-git-svn-i18n-commitencoding.sh\n@@ -4,6 +4,7 @@\n \n test_description='git svn honors i18n.commitEncoding in config'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n compare_git_head_with () {\ndiff --git a/t/t9132-git-svn-broken-symlink.sh b/t/t9132-git-svn-broken-symlink.sh\nindex aeceffaf7b0..4d8d0584b79 100755\n--- a/t/t9132-git-svn-broken-symlink.sh\n+++ b/t/t9132-git-svn-broken-symlink.sh\n@@ -2,6 +2,7 @@\n \n test_description='test that git handles an svn repository with empty symlinks'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n test_expect_success 'load svn dumpfile' '\n \tsvnadmin load \"$rawsvnrepo\" <<EOF\ndiff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh\nindex 22d80b0be2b..b7f756b2b7f 100755\n--- a/t/t9139-git-svn-non-utf8-commitencoding.sh\n+++ b/t/t9139-git-svn-non-utf8-commitencoding.sh\n@@ -4,6 +4,7 @@\n \n test_description='git svn refuses to dcommit non-UTF8 messages'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n # ISO-2022-JP can pass for valid UTF-8, so skipping that in this test\ndiff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh\nindex 80cb55fee70..79c26ed69c1 100755\n--- a/t/t9146-git-svn-empty-dirs.sh\n+++ b/t/t9146-git-svn-empty-dirs.sh\n@@ -3,6 +3,8 @@\n # Copyright (c) 2009 Eric Wong\n \n test_description='git svn creates empty directories'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9148-git-svn-propset.sh b/t/t9148-git-svn-propset.sh\nindex aebb28995e5..6cc76a07b39 100755\n--- a/t/t9148-git-svn-propset.sh\n+++ b/t/t9148-git-svn-propset.sh\n@@ -5,6 +5,7 @@\n \n test_description='git svn propset tests'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup propset via import' '\ndiff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh\nindex c93a5beab25..85221d439bd 100755\n--- a/t/t9151-svn-mergeinfo.sh\n+++ b/t/t9151-svn-mergeinfo.sh\n@@ -5,6 +5,7 @@\n \n test_description='git-svn svn mergeinfo properties'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'load svn dump' \"\ndiff --git a/t/t9160-git-svn-preserve-empty-dirs.sh b/t/t9160-git-svn-preserve-empty-dirs.sh\nindex 36c6b1a12ff..9cf7a1427ab 100755\n--- a/t/t9160-git-svn-preserve-empty-dirs.sh\n+++ b/t/t9160-git-svn-preserve-empty-dirs.sh\n@@ -9,6 +9,7 @@ This test uses git to clone a Subversion repository that contains empty\n directories, and checks that corresponding directories are created in the\n local Git repository with placeholder files.'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n GIT_REPO=git-svn-repo\ndiff --git a/t/t9162-git-svn-dcommit-interactive.sh b/t/t9162-git-svn-dcommit-interactive.sh\nindex e38d9fa37b5..e2aa8ed88a9 100755\n--- a/t/t9162-git-svn-dcommit-interactive.sh\n+++ b/t/t9162-git-svn-dcommit-interactive.sh\n@@ -3,6 +3,8 @@\n # Copyright (c) 2011 Frédéric Heitzmann\n \n test_description='git svn dcommit --interactive series'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9164-git-svn-dcommit-concurrent.sh b/t/t9164-git-svn-dcommit-concurrent.sh\nindex 8466269bf50..1465156072e 100755\n--- a/t/t9164-git-svn-dcommit-concurrent.sh\n+++ b/t/t9164-git-svn-dcommit-concurrent.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='concurrent git svn dcommit'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n \ndiff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh\nindex 32814e75df5..c900231079c 100755\n--- a/t/t9501-gitweb-standalone-http-status.sh\n+++ b/t/t9501-gitweb-standalone-http-status.sh\n@@ -13,6 +13,7 @@ code and message.'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./lib-gitweb.sh\n \n #\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452747","messageId":"patch-v4-20.27-806fef06b87-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 20/27] revisions API: have release_revisions() release \"grep_filter\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:25Z","receivedAt":"2022-03-31T01:13:00Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"grep_filter\" in the \"struct rev_info\".This allows us to mark a test\nas passing under \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c               | 1 +\n t/t9151-svn-mergeinfo.sh | 1 -\n 2 files changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/revision.c b/revision.c\nindex 2d52f1b518d..349c3f77536 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2961,6 +2961,7 @@ void release_revisions(struct rev_info *revs)\n \trelease_revisions_cmdline(&revs->cmdline);\n \tlist_objects_filter_release(&revs->filter);\n \trelease_revisions_mailmap(revs->mailmap);\n+\tfree_grep_patterns(&revs->grep_filter);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\ndiff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh\nindex 85221d439bd..c93a5beab25 100755\n--- a/t/t9151-svn-mergeinfo.sh\n+++ b/t/t9151-svn-mergeinfo.sh\n@@ -5,7 +5,6 @@\n \n test_description='git-svn svn mergeinfo properties'\n \n-TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'load svn dump' \"\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452748","messageId":"patch-v4-24.27-bad359b2983-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:29Z","receivedAt":"2022-03-31T01:13:02Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Call diff_free() on the \"pruning\" member of \"struct rev_info\".  Doing\nso makes several tests pass under SANITIZE=leak.\n\nThis was also the last missing piece that allows us to remove the\nUNLEAK() in \"cmd_diff\" and \"cmd_diff_index\", which allows us to use\nthose commands as a canary for general leaks in the revisions API. See\n[1] for further rationale, and 886e1084d78 (builtin/: add UNLEAKs,\n2017-10-01) for the commit that added the UNLEAK() there.\n\n1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/diff-index.c                | 1 -\n builtin/diff.c                      | 1 -\n revision.c                          | 1 +\n t/t1001-read-tree-m-2way.sh         | 1 +\n t/t1002-read-tree-m-u-2way.sh       | 1 +\n t/t2200-add-update.sh               | 1 +\n t/t4039-diff-assume-unchanged.sh    | 1 +\n t/t4206-log-follow-harder-copies.sh | 2 ++\n t/t6131-pathspec-icase.sh           | 2 ++\n 9 files changed, 9 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/diff-index.c b/builtin/diff-index.c\nindex 3a83183c312..7d158af6b6d 100644\n--- a/builtin/diff-index.c\n+++ b/builtin/diff-index.c\n@@ -70,7 +70,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)\n \t\treturn -1;\n \t}\n \tresult = run_diff_index(&rev, option);\n-\tUNLEAK(rev);\n \tresult = diff_result_code(&rev.diffopt, result);\n \trelease_revisions(&rev);\n \treturn result;\ndiff --git a/builtin/diff.c b/builtin/diff.c\nindex dd48336da56..f539132ac68 100644\n--- a/builtin/diff.c\n+++ b/builtin/diff.c\n@@ -594,7 +594,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \tresult = diff_result_code(&rev.diffopt, result);\n \tif (1 < rev.diffopt.skip_stat_unmatch)\n \t\trefresh_index_quietly();\n-\tUNLEAK(rev);\n \trelease_revisions(&rev);\n \tUNLEAK(ent);\n \tUNLEAK(blob);\ndiff --git a/revision.c b/revision.c\nindex 24b6bc3ac7e..6490179fb83 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2964,6 +2964,7 @@ void release_revisions(struct rev_info *revs)\n \tclear_pathspec(&revs->prune_data);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n+\tdiff_free(&revs->pruning);\n \treflog_walk_info_release(revs->reflog_info);\n }\n \ndiff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh\nindex 0710b1fb1e9..516a6112fdc 100755\n--- a/t/t1001-read-tree-m-2way.sh\n+++ b/t/t1001-read-tree-m-2way.sh\n@@ -21,6 +21,7 @@ In the test, these paths are used:\n \tyomin   - not in H or M\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-read-tree.sh\n \ndiff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh\nindex 46cbd5514a6..bd5313caec9 100755\n--- a/t/t1002-read-tree-m-u-2way.sh\n+++ b/t/t1002-read-tree-m-u-2way.sh\n@@ -9,6 +9,7 @@ This is identical to t1001, but uses -u to update the work tree as well.\n \n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-read-tree.sh\n \ndiff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh\nindex 0c38f8e3569..be394f1131a 100755\n--- a/t/t2200-add-update.sh\n+++ b/t/t2200-add-update.sh\n@@ -14,6 +14,7 @@ only the updates to dir/sub.\n Also tested are \"git add -u\" without limiting, and \"git add -u\"\n without contents changes, and other conditions'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh\nindex 0eb0314a8b3..78090e6852d 100755\n--- a/t/t4039-diff-assume-unchanged.sh\n+++ b/t/t4039-diff-assume-unchanged.sh\n@@ -2,6 +2,7 @@\n \n test_description='diff with assume-unchanged entries'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # external diff has been tested in t4020-diff-external.sh\ndiff --git a/t/t4206-log-follow-harder-copies.sh b/t/t4206-log-follow-harder-copies.sh\nindex 4871a5dc92f..33ecf82c7f9 100755\n--- a/t/t4206-log-follow-harder-copies.sh\n+++ b/t/t4206-log-follow-harder-copies.sh\n@@ -6,6 +6,8 @@\n test_description='Test --follow should always find copies hard in git log.\n \n '\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-diff.sh\n \ndiff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh\nindex 39fc3f6769b..770cce026cb 100755\n--- a/t/t6131-pathspec-icase.sh\n+++ b/t/t6131-pathspec-icase.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='test case insensitive pathspec limiting'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n if test_have_prereq CASE_INSENSITIVE_FS\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452749","messageId":"patch-v4-23.27-0363a69073c-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 23/27] revisions API: release \"reflog_info\" in release revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:28Z","receivedAt":"2022-03-31T01:13:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a missing reflog_walk_info_release() to \"reflog-walk.c\" and use it\nin release_revisions().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n reflog-walk.c            | 24 +++++++++++++++++++++++-\n reflog-walk.h            |  1 +\n revision.c               |  1 +\n t/t0100-previous.sh      |  1 +\n t/t1401-symbolic-ref.sh  |  2 ++\n t/t1411-reflog-show.sh   |  1 +\n t/t1412-reflog-loop.sh   |  2 ++\n t/t1415-worktree-refs.sh |  1 +\n 8 files changed, 32 insertions(+), 1 deletion(-)\n\ndiff --git a/reflog-walk.c b/reflog-walk.c\nindex 8ac4b284b6b..7aa6595a51f 100644\n--- a/reflog-walk.c\n+++ b/reflog-walk.c\n@@ -8,7 +8,7 @@\n \n struct complete_reflogs {\n \tchar *ref;\n-\tconst char *short_ref;\n+\tchar *short_ref;\n \tstruct reflog_info {\n \t\tstruct object_id ooid, noid;\n \t\tchar *email;\n@@ -51,9 +51,16 @@ static void free_complete_reflog(struct complete_reflogs *array)\n \t}\n \tfree(array->items);\n \tfree(array->ref);\n+\tfree(array->short_ref);\n \tfree(array);\n }\n \n+static void complete_reflogs_clear(void *util, const char *str)\n+{\n+\tstruct complete_reflogs *array = util;\n+\tfree_complete_reflog(array);\n+}\n+\n static struct complete_reflogs *read_complete_reflog(const char *ref)\n {\n \tstruct complete_reflogs *reflogs =\n@@ -116,6 +123,21 @@ void init_reflog_walk(struct reflog_walk_info **info)\n \t(*info)->complete_reflogs.strdup_strings = 1;\n }\n \n+void reflog_walk_info_release(struct reflog_walk_info *info)\n+{\n+\tsize_t i;\n+\n+\tif (!info)\n+\t\treturn;\n+\n+\tfor (i = 0; i < info->nr; i++)\n+\t\tfree(info->logs[i]);\n+\tstring_list_clear_func(&info->complete_reflogs,\n+\t\t\t       complete_reflogs_clear);\n+\tfree(info->logs);\n+\tfree(info);\n+}\n+\n int add_reflog_for_walk(struct reflog_walk_info *info,\n \t\tstruct commit *commit, const char *name)\n {\ndiff --git a/reflog-walk.h b/reflog-walk.h\nindex e9e00ffd479..8076f10d9fb 100644\n--- a/reflog-walk.h\n+++ b/reflog-walk.h\n@@ -8,6 +8,7 @@ struct reflog_walk_info;\n struct date_mode;\n \n void init_reflog_walk(struct reflog_walk_info **info);\n+void reflog_walk_info_release(struct reflog_walk_info *info);\n int add_reflog_for_walk(struct reflog_walk_info *info,\n \t\t\tstruct commit *commit, const char *name);\n void show_reflog_message(struct reflog_walk_info *info, int,\ndiff --git a/revision.c b/revision.c\nindex 9806240770c..24b6bc3ac7e 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2964,6 +2964,7 @@ void release_revisions(struct rev_info *revs)\n \tclear_pathspec(&revs->prune_data);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n+\treflog_walk_info_release(revs->reflog_info);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\ndiff --git a/t/t0100-previous.sh b/t/t0100-previous.sh\nindex 69beb59f627..a16cc3d2983 100755\n--- a/t/t0100-previous.sh\n+++ b/t/t0100-previous.sh\n@@ -5,6 +5,7 @@ test_description='previous branch syntax @{-n}'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'branch -d @{-1}' '\ndiff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh\nindex 132a1b885ac..9fb0b90f252 100755\n--- a/t/t1401-symbolic-ref.sh\n+++ b/t/t1401-symbolic-ref.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='basic symbolic-ref tests'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # If the tests munging HEAD fail, they can break detection of\ndiff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh\nindex 0bb319b944a..3770ceffafd 100755\n--- a/t/t1411-reflog-show.sh\n+++ b/t/t1411-reflog-show.sh\n@@ -4,6 +4,7 @@ test_description='Test reflog display routines'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t1412-reflog-loop.sh b/t/t1412-reflog-loop.sh\nindex 977603f7f1c..ff30874f940 100755\n--- a/t/t1412-reflog-loop.sh\n+++ b/t/t1412-reflog-loop.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='reflog walk shows repeated commits again'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup commits' '\ndiff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh\nindex a3e6ea08088..3b531842dd4 100755\n--- a/t/t1415-worktree-refs.sh\n+++ b/t/t1415-worktree-refs.sh\n@@ -2,6 +2,7 @@\n \n test_description='per-worktree refs'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452750","messageId":"patch-v4-22.27-cb201fa1914-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 22/27] revisions API: clear \"boundary_commits\" in release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:27Z","receivedAt":"2022-03-31T01:13:06Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Clear the \"boundary_commits\" object_array in release_revisions(). This\nmakes a few more tests pass under SANITIZE=leak, including\n\"t/t4126-apply-empty.sh\" which started failed as an UNLEAK() in\ncmd_format_patch() was removed in a preceding commit.\n\nThis also re-marks the various tests relying on \"git format-patch\" as\npassing under \"SANITIZE=leak\", in the preceding \"revisions API users:\nuse release_revisions() in builtin/log.c\" commit those were marked as\nfailing as we removed the UNLEAK(rev) from cmd_format_patch() in\n\"builtin/log.c\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c                           | 1 +\n t/t4021-format-patch-numbered.sh     | 1 +\n t/t4028-format-patch-mime-headers.sh | 2 ++\n t/t4036-format-patch-signer-mime.sh  | 1 +\n t/t4122-apply-symlink-inside.sh      | 1 +\n t/t4126-apply-empty.sh               | 1 +\n t/t6110-rev-list-sparse.sh           | 1 +\n t/t9001-send-email.sh                | 1 +\n t/t9116-git-svn-log.sh               | 1 -\n 9 files changed, 9 insertions(+), 1 deletion(-)\n\ndiff --git a/revision.c b/revision.c\nindex c7a0e08765a..9806240770c 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2958,6 +2958,7 @@ void release_revisions(struct rev_info *revs)\n {\n \trelease_revisions_commit_list(revs);\n \tobject_array_clear(&revs->pending);\n+\tobject_array_clear(&revs->boundary_commits);\n \trelease_revisions_cmdline(&revs->cmdline);\n \tlist_objects_filter_release(&revs->filter);\n \tclear_pathspec(&revs->prune_data);\ndiff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh\nindex 9be65fd4440..1219aa226dc 100755\n--- a/t/t4021-format-patch-numbered.sh\n+++ b/t/t4021-format-patch-numbered.sh\n@@ -5,6 +5,7 @@\n \n test_description='Format-patch numbering options'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4028-format-patch-mime-headers.sh b/t/t4028-format-patch-mime-headers.sh\nindex 204ba673cb5..60cb819c42e 100755\n--- a/t/t4028-format-patch-mime-headers.sh\n+++ b/t/t4028-format-patch-mime-headers.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='format-patch mime headers and extra headers do not conflict'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'create commit with utf-8 body' '\ndiff --git a/t/t4036-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh\nindex 98d9713d8b2..48655bcc789 100755\n--- a/t/t4036-format-patch-signer-mime.sh\n+++ b/t/t4036-format-patch-signer-mime.sh\n@@ -2,6 +2,7 @@\n \n test_description='format-patch -s should force MIME encoding as needed'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh\nindex aa52de401b9..96965373036 100755\n--- a/t/t4122-apply-symlink-inside.sh\n+++ b/t/t4122-apply-symlink-inside.sh\n@@ -4,6 +4,7 @@ test_description='apply to deeper directory without getting fooled with symlink'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh\nindex 66a7ba8ab8f..ece9fae207d 100755\n--- a/t/t4126-apply-empty.sh\n+++ b/t/t4126-apply-empty.sh\n@@ -2,6 +2,7 @@\n \n test_description='apply empty'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t6110-rev-list-sparse.sh b/t/t6110-rev-list-sparse.sh\nindex 13c1da53528..ddefc7f24ee 100755\n--- a/t/t6110-rev-list-sparse.sh\n+++ b/t/t6110-rev-list-sparse.sh\n@@ -4,6 +4,7 @@ test_description='operations that cull histories in unusual ways'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh\nindex 84d0f40d76a..dfa6b20f7a6 100755\n--- a/t/t9001-send-email.sh\n+++ b/t/t9001-send-email.sh\n@@ -4,6 +4,7 @@ test_description='git send-email'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # May be altered later in the test\ndiff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh\nindex 34f6c80dea3..d74d7b2de68 100755\n--- a/t/t9116-git-svn-log.sh\n+++ b/t/t9116-git-svn-log.sh\n@@ -5,7 +5,6 @@\n \n test_description='git svn log tests'\n \n-TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup repository and import' '\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452751","messageId":"patch-v4-27.27-b02cb8fcded-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 27/27] revisions API: add a TODO for diff_free(&revs->diffopt)","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:32Z","receivedAt":"2022-03-31T01:13:12Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a TODO comment indicating that we should release \"diffopt\" in\nrelease_revisions(). In a preceding commit we started releasing the\n\"pruning\" member of the same type, but handling \"diffopt\" will require\nus to untangle the \"no_free\" conditions I added in e900d494dcf (diff:\nadd an API for deferred freeing, 2021-02-11).\n\nLet's leave a TODO comment to that effect, and so that we don't forget\nrefactor code that was changed to use release_revisions() in earlier\ncommits to stop using the \"diffopt\" member after a call to\nrelease_revisions(). This works currently, but would become a logic\nerror as soon as we started freeing \"diffopt\". Doing that change now\ndoesn't harm anything, and future-proofs us against a later change to\nrelease_revisions().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n diff-lib.c  | 4 +++-\n revision.c  | 1 +\n wt-status.c | 6 ++++--\n 3 files changed, 8 insertions(+), 3 deletions(-)\n\ndiff --git a/diff-lib.c b/diff-lib.c\nindex 298265e5b54..7eb66a417aa 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -651,6 +651,7 @@ int index_differs_from(struct repository *r,\n {\n \tstruct rev_info rev;\n \tstruct setup_revision_opt opt;\n+\tunsigned has_changes;\n \n \trepo_init_revisions(r, &rev, NULL);\n \tmemset(&opt, 0, sizeof(opt));\n@@ -662,8 +663,9 @@ int index_differs_from(struct repository *r,\n \t\tdiff_flags_or(&rev.diffopt.flags, flags);\n \trev.diffopt.ita_invisible_in_index = ita_invisible_in_index;\n \trun_diff_index(&rev, 1);\n+\thas_changes = rev.diffopt.flags.has_changes;\n \trelease_revisions(&rev);\n-\treturn (rev.diffopt.flags.has_changes != 0);\n+\treturn (has_changes != 0);\n }\n \n static struct strbuf *idiff_prefix_cb(struct diff_options *opt, void *data)\ndiff --git a/revision.c b/revision.c\nindex 33a458d7c0c..6235946a34e 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2967,6 +2967,7 @@ void release_revisions(struct rev_info *revs)\n \tdate_mode_release(&revs->date_mode);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n+\t/* TODO (need to handle \"no_free\"): diff_free(&revs->diffopt) */\n \tdiff_free(&revs->pruning);\n \treflog_walk_info_release(revs->reflog_info);\n \trelease_revisions_topo_walk_info(revs->topo_walk_info);\ndiff --git a/wt-status.c b/wt-status.c\nindex 61e0c1022f5..102d904adcb 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -2545,8 +2545,9 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)\n \trev_info.diffopt.flags.quick = 1;\n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_files(&rev_info, 0);\n+\tresult = diff_result_code(&rev_info.diffopt, result);\n \trelease_revisions(&rev_info);\n-\treturn diff_result_code(&rev_info.diffopt, result);\n+\treturn result;\n }\n \n /**\n@@ -2578,8 +2579,9 @@ int has_uncommitted_changes(struct repository *r,\n \n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_index(&rev_info, 1);\n+\tresult = diff_result_code(&rev_info.diffopt, result);\n \trelease_revisions(&rev_info);\n-\treturn diff_result_code(&rev_info.diffopt, result);\n+\treturn result;\n }\n \n /**\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452752","messageId":"patch-v4-25.27-9a51c1ae1d1-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 25/27] revisions API: have release_revisions() release \"date_mode\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:30Z","receivedAt":"2022-03-31T01:13:15Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"date_mode\" in the \"struct ref_info\".\n\nThis uses the date_mode_release() function added in 974c919d36d (date\nAPI: add and use a date_mode_release(), 2022-02-16). As that commit\nnotes \"t7004-tag.sh\" tests for the leaks that are being fixed\nhere. That test now fails \"only\" 44 tests, instead of the 46 it failed\nbefore this change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/revision.c b/revision.c\nindex 6490179fb83..31fc5e58da0 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2962,6 +2962,7 @@ void release_revisions(struct rev_info *revs)\n \trelease_revisions_cmdline(&revs->cmdline);\n \tlist_objects_filter_release(&revs->filter);\n \tclear_pathspec(&revs->prune_data);\n+\tdate_mode_release(&revs->date_mode);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n \tdiff_free(&revs->pruning);\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452753","messageId":"patch-v4-16.27-2e351702677-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 16/27] revisions API: have release_revisions() release \"commits\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:21Z","receivedAt":"2022-03-31T01:13:18Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"commits\" in the \"struct rev_info\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/revision.c b/revision.c\nindex 8fc157e9ee7..394e2dcdd90 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2937,6 +2937,7 @@ static void release_revisions_commit_list(struct rev_info *revs)\n \n void release_revisions(struct rev_info *revs)\n {\n+\trelease_revisions_commit_list(revs);\n \tobject_array_clear(&revs->pending);\n }\n \n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452754","messageId":"patch-v4-26.27-b56db37940d-20220331T005325Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v4 26/27] revisions API: have release_revisions() release \"topo_walk_info\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-03-31T01:11:31Z","receivedAt":"2022-03-31T01:13:20Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor the existing reset_topo_walk() into a thin wrapper for a\nrelease_revisions_topo_walk_info() + resetting the member to \"NULL\",\nand call release_revisions_topo_walk_info() from release_revisions().\n\nThis fixes memory leaks that have been with us ever since\n\"topo_walk_info\" was added to revision.[ch] in\nf0d9cc4196a (revision.c: begin refactoring --topo-order logic,\n2018-11-01).\n\nDue to various other leaks this makes no tests pass in their entirety,\nbut e.g. before this running this on git.git:\n\n    ./git -P log --pretty=tformat:\"%P   %H | %s\" --parents --full-history --topo-order -3 -- README.md\n\nWould report under SANITIZE=leak:\n\n    SUMMARY: LeakSanitizer: 531064 byte(s) leaked in 6 allocation(s).\n\nNow we'll free all of that memory.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 16 ++++++++++++----\n 1 file changed, 12 insertions(+), 4 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex 31fc5e58da0..33a458d7c0c 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2954,6 +2954,8 @@ static void release_revisions_mailmap(struct string_list *mailmap)\n \tfree(mailmap);\n }\n \n+static void release_revisions_topo_walk_info(struct topo_walk_info *info);\n+\n void release_revisions(struct rev_info *revs)\n {\n \trelease_revisions_commit_list(revs);\n@@ -2967,6 +2969,7 @@ void release_revisions(struct rev_info *revs)\n \tfree_grep_patterns(&revs->grep_filter);\n \tdiff_free(&revs->pruning);\n \treflog_walk_info_release(revs->reflog_info);\n+\trelease_revisions_topo_walk_info(revs->topo_walk_info);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n@@ -3479,17 +3482,22 @@ static void compute_indegrees_to_depth(struct rev_info *revs,\n \t\tindegree_walk_step(revs);\n }\n \n-static void reset_topo_walk(struct rev_info *revs)\n+static void release_revisions_topo_walk_info(struct topo_walk_info *info)\n {\n-\tstruct topo_walk_info *info = revs->topo_walk_info;\n-\n+\tif (!info)\n+\t\treturn;\n \tclear_prio_queue(&info->explore_queue);\n \tclear_prio_queue(&info->indegree_queue);\n \tclear_prio_queue(&info->topo_queue);\n \tclear_indegree_slab(&info->indegree);\n \tclear_author_date_slab(&info->author_date);\n+\tfree(info);\n+}\n \n-\tFREE_AND_NULL(revs->topo_walk_info);\n+static void reset_topo_walk(struct rev_info *revs)\n+{\n+\trelease_revisions_topo_walk_info(revs->topo_walk_info);\n+\trevs->topo_walk_info = NULL;\n }\n \n static void init_topo_walk(struct rev_info *revs)\n-- \n2.35.1.1557.g4e3e9cdb5e0\n\n"},{"id":"452835","messageId":"xmqq1qyh25a5.fsf@gitster.g","threadId":"57525","inReplyTo":"patch-v4-09.27-2f4e65fb534-20220331T005325Z-avarab@gmail.com","subject":"Re: [PATCH v4 09/27] revisions API users: use release_revisions() needing REV_INFO_INIT","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-03-31T21:14:42Z","receivedAt":"2022-03-31T21:14:51Z","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> Use release_revisions() to various users of \"struct rev_list\" which\n> need to have their \"struct rev_info\" zero-initialized before we can\n> start using it.\n>\n> To do this add a stub \"REV_INFO_INIT\" macro, ideally macro would be\n> able to fully initialize a \"struct rev_info\", but all it does is the\n> equivalent of assigning \"{ 0 }\" to the struct, the API user will still\n> need to use repo_init_revisions(). In some future follow-up work we'll\n> hopefully make REV_INFO_INIT be a \"stand-alone\" init likke STRBUF_INIT\n> and other similar macros.\n\nI do not think we want to leave such a misleading paragraph to\nfuture developers.\n\nYes, We may want to move some of what init_revisions() does to\nREV_INFO_INIT(), and for that, it helps to start using greppable\nstring REV_INFO_INT early rather than { 0 } to ease such transition,\nand that is what we should be stressing, instead of ranting \"it does\nnot do anything, so why are we stupidly introducing a name, instead\nof writing { 0 }, which is what amounts to it anyway?\" without\nexplicitly saying so but hinting with words like \"stub\", \"all it\ndoes is\", and \"will still need to\".\n\nIs that some kind of passive-aggressive thing?\n\nYou cannot use get_revision() even after calling init_revisions(),\nand still need to use setup_revisions() before hand, but that does\nnot mean init_revisions() is not doing its job.  It may be\nimplemented as the zero-initialization right now, but it misses the\npoint to put a stress on the fact that it doesn't do much now.\n"},{"id":"452864","messageId":"220401.86h77dnkpw.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"xmqq1qyh25a5.fsf@gitster.g","subject":"Re: [PATCH v4 09/27] revisions API users: use release_revisions() needing REV_INFO_INIT","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-01T10:31:07Z","receivedAt":"2022-04-01T10:48:03Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Thu, Mar 31 2022, Junio C Hamano wrote:\n\n> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:\n>\n>> Use release_revisions() to various users of \"struct rev_list\" which\n>> need to have their \"struct rev_info\" zero-initialized before we can\n>> start using it.\n>>\n>> To do this add a stub \"REV_INFO_INIT\" macro, ideally macro would be\n>> able to fully initialize a \"struct rev_info\", but all it does is the\n>> equivalent of assigning \"{ 0 }\" to the struct, the API user will still\n>> need to use repo_init_revisions(). In some future follow-up work we'll\n>> hopefully make REV_INFO_INIT be a \"stand-alone\" init likke STRBUF_INIT\n>> and other similar macros.\n>\n> I do not think we want to leave such a misleading paragraph to\n> future developers.\n>\n> Yes, We may want to move some of what init_revisions() does to\n> REV_INFO_INIT(), and for that, it helps to start using greppable\n> string REV_INFO_INT early rather than { 0 } to ease such transition,\n> and that is what we should be stressing, instead of ranting \"it does\n> not do anything, so why are we stupidly introducing a name, instead\n> of writing { 0 }, which is what amounts to it anyway?\" without\n> explicitly saying so but hinting with words like \"stub\", \"all it\n> does is\", and \"will still need to\".\n>\n> Is that some kind of passive-aggressive thing?\n\nNo, I'm not trying to be a dick. I'm just confused, sorry :)\n\nI.e. I didn't grok what you really wanted from that REV_INFO_INIT\npattern in the beginning, and I largely draft my commit messages as a\nway to adequately explain things to myself.\n\nThe \"stub\" and \"not sufficient (yet!)\" part of the docs is a (probably\ntoo leaky) artifact of having (after the whole thread about\nREV_INFO_INIT started) gotten that mostly working as a \"real\"\ninitializer.\n\nWhich I figured I'd submit at some point after this lands. The grep.c\ncase was quite tricky, but the rest look pretty easy, I just didn't\nfinish them.\n\nI can re-roll & omit this entirely from the commit message, and the\nAPI docs.\n\nI just thought, and still think, that it's worth drawing the API user's\nattention to the fact that unlike most stand-alone *_INIT macros this\none isn't sufficient to be up & running with a \"struct rev_info\".\n\nWhich isn't the case with {STRBUF,STRVEC}_INIT, CHILD_PROCESS_INIT,\nSTRING_LIST_INIT_* etc. etc., i.e. if you've used any of these:\n\n    git grep -h -o '\\S+_INIT;' | sort | uniq -c | sort -nr\n\nYou're likely to expect them to \"flow\" a certain way when it comes to\ninitializing the struct on the stack, but this one will be a bit of an\nodd case while repo_init_revisions() is still required, which seems to\nme to be worth explaining.\n\nBut not enough to argue about it, so whatever you'd prefer...\n\n> You cannot use get_revision() even after calling init_revisions(),\n> and still need to use setup_revisions() before hand, but that does\n> not mean init_revisions() is not doing its job.  It may be\n> implemented as the zero-initialization right now, but it misses the\n> point to put a stress on the fact that it doesn't do much now.\n\nI don't think it's broken, having gotten most of the way towards\nrewriting it to be macro-init'd there are some tricky edge cases where\nwe might always need to call a function to fully initialize it.\n\nBut per the above I thought it made sense to explain that this\nparticular *_INIT is a bit of an odd case if you've gotten used to other\nsuch macros.\n"},{"id":"452891","messageId":"ffb2f216-4504-b1d7-6b59-55c09df4f5d1@gmail.com","threadId":"57525","inReplyTo":"patch-v4-08.27-086cec742b4-20220331T005325Z-avarab@gmail.com","subject":"Re: [PATCH v4 08/27] revision.[ch]: document and move code declared around \"init\"","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2022-04-01T15:20:52Z","receivedAt":"2022-04-01T15:53:22Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Ævar\n\nOn 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:\n> A subsequent commit will add \"REV_INFO_INIT\" macro adjacent to\n> repo_init_revisions(), unfortunately between the \"struct rev_info\"\n> itself and that function we've added various miscellaneous code\n> between the two over the years.\n> \n> Let's move that code either lower in revision.h, giving it API docs\n> while we're at it, or in cases where it wasn't public API at all move\n> it into revision.c No lines of code are changed here, only moved\n> around. The only changes are the addition of new API comments.\n\nIt's nice to see to see the API being documented\n\n >[...]\n>   \n> +/**\n> + * Helpers to check if a \"struct string_list\" item matches wild\n> + * wildmatch().\n\nwild wildmatch()??\n\nBest Wishes\n\nPhillip\n"},{"id":"452892","messageId":"e1bdb860-070b-8596-2a32-e9bc3ad354b0@gmail.com","threadId":"57525","inReplyTo":"patch-v4-05.27-2d0ed57ec2e-20220331T005325Z-avarab@gmail.com","subject":"Re: [PATCH v4 05/27] revision.[ch]: split freeing of revs->commit into a function","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2022-04-01T15:15:06Z","receivedAt":"2022-04-01T15:53:55Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Ævar\n\nOn 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:\n> Move the existing code for invoking free_commit_list() and setting\n> revs->commits to NULL into a new release_revisions_commit_list()\n> function. This will be used as part of a general free()-ing mechanism\n> for \"struct rev_info\".\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>   revision.c | 13 +++++++++----\n>   1 file changed, 9 insertions(+), 4 deletions(-)\n> \n> diff --git a/revision.c b/revision.c\n> index 7d435f80480..29f102cb83d 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -2923,6 +2923,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n>   \treturn left;\n>   }\n>   \n> +static void release_revisions_commit_list(struct rev_info *revs)\n> +{\n> +\tif (revs->commits) {\n\nThis guard copied for the original is not needed, free_commit_list() \nwill handle NULL just fine.\n\nBest Wishes\n\nPhillip\n> +\t\tfree_commit_list(revs->commits);\n> +\t\trevs->commits = NULL;\n> +\t}\n> +}\n> +\n>   static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n>   {\n>   \tstruct commit_list *l = xcalloc(1, sizeof(*l));\n> @@ -4080,10 +4088,7 @@ static void create_boundary_commit_list(struct rev_info *revs)\n>   \t * boundary commits anyway.  (This is what the code has always\n>   \t * done.)\n>   \t */\n> -\tif (revs->commits) {\n> -\t\tfree_commit_list(revs->commits);\n> -\t\trevs->commits = NULL;\n> -\t}\n> +\trelease_revisions_commit_list(revs);\n>   \n>   \t/*\n>   \t * Put all of the actual boundary commits from revs->boundary_commits\n"},{"id":"452894","messageId":"fb26835b-c2fe-be3c-a7c4-6ea8b5748969@gmail.com","threadId":"57525","inReplyTo":"patch-v4-06.27-b9beaba16bf-20220331T005325Z-avarab@gmail.com","subject":"Re: [PATCH v4 06/27] revision.[ch]: provide and start using a release_revisions()","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2022-04-01T15:17:21Z","receivedAt":"2022-04-01T15:54:14Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Ævar\n\nOn 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:\n> The users of the revision.[ch] API's \"struct rev_info\" are a major\n> source of memory leaks in the test suite under SANITIZE=leak, which in\n> turn adds a lot of noise when trying to mark up tests with\n> \"TEST_PASSES_SANITIZE_LEAK=true\".\n> \n> The users of that API are largely one-shot, e.g. \"git log\", so\n> arguably freeing the memory is a waste of time, but in many cases\n> they've actually been trying to free the memory, and just doing that\n> in a buggy manner.\n\nI've not looked that closely at these changes but builtin/log.c is not \nmodified so I'm a bit confused by the mention of \"git log\" above.\n\nBest Wishes\n\nPhillip\n\n> Let's provide a release_revisions() function for these users, and\n> start migrating them over per the plan outlined in [1]. Right now this\n> only handles the \"pending\" member of the struct, but more will be\n> added in subsequent commits.\n> \n> Even though we only clear the \"pending\" member now, let's not leave a\n> trap in code like the pre-image of index_differs_from(), where we'd\n> start doing the wrong thing as soon as the release_revisions() learned\n> to clear its \"diffopt\". I.e. we need to call release_revisions() after\n> we've inspected any state in \"struct rev_info\".\n> \n> This leaves in place e.g. clear_pathspec(&rev.prune_data) in\n> stash_working_tree() in builtin/stash.c, subsequent commits will teach\n> release_revisions() to free \"prune_data\" and other members that in\n> some cases are individually cleared by users of \"struct rev_info\" by\n> reaching into its members. Those subsequent commits will remove the\n> relevant calls to e.g. clear_pathspec().\n> \n> We avoid amending code in index_differs_from() in diff-lib.c as well\n> as wt_status_collect_changes_index(), has_unstaged_changes() and\n> has_uncommitted_changes() in wt-status.c in a way that assumes that we\n> are already clearing the \"diffopt\" member. That will be handled in a\n> subsequent commit.\n> \n> 1. https://lore.kernel.org/git/87a6k8daeu.fsf@evledraar.gmail.com/\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>   builtin/checkout.c | 2 +-\n>   builtin/stash.c    | 3 +--\n>   diff-lib.c         | 2 +-\n>   range-diff.c       | 2 +-\n>   revision.c         | 5 +++++\n>   revision.h         | 6 ++++++\n>   wt-status.c        | 5 +++--\n>   7 files changed, 18 insertions(+), 7 deletions(-)\n> \n> diff --git a/builtin/checkout.c b/builtin/checkout.c\n> index 797681481d1..4d9e0bd3ac1 100644\n> --- a/builtin/checkout.c\n> +++ b/builtin/checkout.c\n> @@ -629,7 +629,7 @@ static void show_local_changes(struct object *head,\n>   \tdiff_setup_done(&rev.diffopt);\n>   \tadd_pending_object(&rev, head, NULL);\n>   \trun_diff_index(&rev, 0);\n> -\tobject_array_clear(&rev.pending);\n> +\trelease_revisions(&rev);\n>   }\n>   \n>   static void describe_detached_head(const char *msg, struct commit *commit)\n> diff --git a/builtin/stash.c b/builtin/stash.c\n> index ccdfdab44be..ad74624c2f7 100644\n> --- a/builtin/stash.c\n> +++ b/builtin/stash.c\n> @@ -1266,9 +1266,8 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps\n>   \n>   done:\n>   \tdiscard_index(&istate);\n> -\tUNLEAK(rev);\n> -\tobject_array_clear(&rev.pending);\n>   \tclear_pathspec(&rev.prune_data);\n> +\trelease_revisions(&rev);\n>   \tstrbuf_release(&diff_output);\n>   \tremove_path(stash_index_path.buf);\n>   \treturn ret;\n> diff --git a/diff-lib.c b/diff-lib.c\n> index ca085a03efc..d6800274bd5 100644\n> --- a/diff-lib.c\n> +++ b/diff-lib.c\n> @@ -662,7 +662,7 @@ int index_differs_from(struct repository *r,\n>   \t\tdiff_flags_or(&rev.diffopt.flags, flags);\n>   \trev.diffopt.ita_invisible_in_index = ita_invisible_in_index;\n>   \trun_diff_index(&rev, 1);\n> -\tobject_array_clear(&rev.pending);\n> +\trelease_revisions(&rev);\n>   \treturn (rev.diffopt.flags.has_changes != 0);\n>   }\n>   \n> diff --git a/range-diff.c b/range-diff.c\n> index b72eb9fdbee..39cc010c628 100644\n> --- a/range-diff.c\n> +++ b/range-diff.c\n> @@ -596,6 +596,6 @@ int is_range_diff_range(const char *arg)\n>   \t}\n>   \n>   \tfree(copy);\n> -\tobject_array_clear(&revs.pending);\n> +\trelease_revisions(&revs);\n>   \treturn negative > 0 && positive > 0;\n>   }\n> diff --git a/revision.c b/revision.c\n> index 29f102cb83d..9a3c05b69a5 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -2931,6 +2931,11 @@ static void release_revisions_commit_list(struct rev_info *revs)\n>   \t}\n>   }\n>   \n> +void release_revisions(struct rev_info *revs)\n> +{\n> +\tobject_array_clear(&revs->pending);\n> +}\n> +\n>   static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n>   {\n>   \tstruct commit_list *l = xcalloc(1, sizeof(*l));\n> diff --git a/revision.h b/revision.h\n> index 5bc59c7bfe1..61c780fc4cd 100644\n> --- a/revision.h\n> +++ b/revision.h\n> @@ -377,6 +377,12 @@ void repo_init_revisions(struct repository *r,\n>   int setup_revisions(int argc, const char **argv, struct rev_info *revs,\n>   \t\t    struct setup_revision_opt *);\n>   \n> +/**\n> + * Free data allocated in a \"struct rev_info\" after it's been\n> + * initialized with repo_init_revisions().\n> + */\n> +void release_revisions(struct rev_info *revs);\n> +\n>   void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,\n>   \t\t\tconst struct option *options,\n>   \t\t\tconst char * const usagestr[]);\n> diff --git a/wt-status.c b/wt-status.c\n> index d33f9272b72..922cf787f95 100644\n> --- a/wt-status.c\n> +++ b/wt-status.c\n> @@ -662,7 +662,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)\n>   \n>   \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n>   \trun_diff_index(&rev, 1);\n> -\tobject_array_clear(&rev.pending);\n> +\trelease_revisions(&rev);\n>   \tclear_pathspec(&rev.prune_data);\n>   }\n>   \n> @@ -2545,6 +2545,7 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)\n>   \trev_info.diffopt.flags.quick = 1;\n>   \tdiff_setup_done(&rev_info.diffopt);\n>   \tresult = run_diff_files(&rev_info, 0);\n> +\trelease_revisions(&rev_info);\n>   \treturn diff_result_code(&rev_info.diffopt, result);\n>   }\n>   \n> @@ -2577,7 +2578,7 @@ int has_uncommitted_changes(struct repository *r,\n>   \n>   \tdiff_setup_done(&rev_info.diffopt);\n>   \tresult = run_diff_index(&rev_info, 1);\n> -\tobject_array_clear(&rev_info.pending);\n> +\trelease_revisions(&rev_info);\n>   \treturn diff_result_code(&rev_info.diffopt, result);\n>   }\n>   \n"},{"id":"452895","messageId":"851584aa-a31d-7446-4994-e641f298f0cd@gmail.com","threadId":"57525","inReplyTo":"patch-v4-04.27-69f0aabe38f-20220331T005325Z-avarab@gmail.com","subject":"Re: [PATCH v4 04/27] format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2022-04-01T15:13:48Z","receivedAt":"2022-04-01T15:54:23Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Ævar\n\nOn 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:\n> Fix two memory leaks in \"struct rev_info\" by freeing that memory in\n> cmd_format_patch(). These two are unusual special-cases in being in\n> the \"struct rev_info\", but not being \"owned\" by the code in\n> revision.c. I.e. they're members of the struct so that this code in\n> \"builtin/log.c\" can pass information code in log-tree.c.\n\nI'm not sure that I necessarily agree that these are owned by \nbuiltin/log.c. For rev.extra_headers it is set in builtin/log.c but \nnever used there which makes me think we are transferring ownership to \nstruct rev_info. For ref_message_ids it is less clear cut but having it \nowned by struct rev_info and freeing it in release_revisions() would \nmake things clearer I think. Having some members owned by struct \nrev_info but others allocated and freed by other code is confusing and \nis likely to lead to memory errors. I don't think struct rev_info is \nborrowing a reference to these items as they are being allocated for \nit's exclusive use.\n\nBest Wishes\n\nPhillip\n\n> See 20ff06805c6 (format-patch: resurrect extra headers from config,\n> 2006-06-02) and d1566f7883f (git-format-patch: Make the second and\n> subsequent mails replies to the first, 2006-07-14) for the initial\n> introduction of \"extra_headers\" and \"ref_message_ids\".\n> \n> We can count on repo_init_revisions() memset()-ing this data to 0\n> however, so we can count on it being either NULL or something we\n> allocated. In the case of \"extra_headers\" let's add a local \"char *\"\n> variable to hold it, to avoid the eventual cast from \"const char *\"\n> when we free() it.\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>   builtin/log.c | 7 ++++++-\n>   1 file changed, 6 insertions(+), 1 deletion(-)\n> \n> diff --git a/builtin/log.c b/builtin/log.c\n> index 634dc782cce..6f9928fabfe 100644\n> --- a/builtin/log.c\n> +++ b/builtin/log.c\n> @@ -1747,6 +1747,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n>   \tstruct commit *commit;\n>   \tstruct commit **list = NULL;\n>   \tstruct rev_info rev;\n> +\tchar *to_free = NULL;\n>   \tstruct setup_revision_opt s_r_opt;\n>   \tint nr = 0, total, i;\n>   \tint use_stdout = 0;\n> @@ -1947,7 +1948,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n>   \t\tstrbuf_addch(&buf, '\\n');\n>   \t}\n>   \n> -\trev.extra_headers = strbuf_detach(&buf, NULL);\n> +\trev.extra_headers = to_free = strbuf_detach(&buf, NULL);\n>   \n>   \tif (from) {\n>   \t\tif (split_ident_line(&rev.from_ident, from, strlen(from)))\n> @@ -2284,6 +2285,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n>   \tstrbuf_release(&rdiff1);\n>   \tstrbuf_release(&rdiff2);\n>   \tstrbuf_release(&rdiff_title);\n> +\tfree(to_free);\n> +\tif (rev.ref_message_ids)\n> +\t\tstring_list_clear(rev.ref_message_ids, 0);\n> +\tfree(rev.ref_message_ids);\n>   \tUNLEAK(rev);\n>   \treturn 0;\n>   }\n"},{"id":"452900","messageId":"9a487124-ef1e-0644-9585-174343555914@gmail.com","threadId":"57525","inReplyTo":"patch-v4-10.27-145a0f74b6a-20220331T005325Z-avarab@gmail.com","subject":"Re: [PATCH v4 10/27] stash: always have the owner of \"stash_info\" free it","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2022-04-01T15:29:40Z","receivedAt":"2022-04-01T16:02:52Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Ævar\n\nOn 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:\n> Change the initialization of the \"revision\" member of \"struct\n> stash_info\" to be initialized vi a macro, and more importantly that\n> that initializing function be tasked to free it, usually via a \"goto\n> cleanup\" pattern.\n> \n> Despite the \"revision\" name (and the topic of the series containing\n> this commit) the \"stash info\" has nothing to do with the \"struct\n> rev_info\". I'm making this change because in the subsequent commit\n> when we do want to free the \"struct rev_info\" via a \"goto cleanup\"\n> pattern we'd otherwise free() uninitialized memory in some cases, as\n> we only strbuf_init() the string in get_stash_info().\n> \n> So while it's the smallest possible change, let's convert all users of\n> this pattern in the file while we're at it.\n> \n> A good follow-up to this change would be to change all the \"ret = -1;\n> goto done;\" in this file to instead use a \"goto cleanup\", and\n> initialize \"int ret = -1\" at the start of the relevant functions. That\n> would allow us to drop a lot of needless brace verbosity on two-line\n> \"if\" statements, but let's leave that alone for now.\n\nYou seem to have made this change here.\n\n>[...]\n> @@ -861,10 +863,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n>   \t\t\tstrvec_push(&revision_args, argv[i]);\n>   \t}\n>   \n> -\tret = get_stash_info(&info, stash_args.nr, stash_args.v);\n> -\tstrvec_clear(&stash_args);\n> -\tif (ret)\n> -\t\treturn -1;\n> +\tif (get_stash_info(&info, stash_args.nr, stash_args.v))\n> +\t\tgoto cleanup;\n>   \n>   \t/*\n>   \t * The config settings are applied only if there are not passed\n> @@ -878,8 +878,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n>   \t\t\trev.diffopt.output_format |= DIFF_FORMAT_PATCH;\n>   \n>   \t\tif (!show_stat && !show_patch) {\n> -\t\t\tfree_stash_info(&info);\n> -\t\t\treturn 0;\n> +\t\t\tret = 0;\n> +\t\t\tgoto cleanup;\n>   \t\t}\n>   \t}\n>   \n> @@ -912,8 +912,11 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n>   \t}\n>   \tlog_tree_diff_flush(&rev);\n>   \n> +\tret = diff_result_code(&rev.diffopt, 0);;\n> +cleanup:\n> +\tstrvec_clear(&stash_args);\n\nThis seems to be fixing a leak that's not mentioned in the commit message.\n\n>   \tfree_stash_info(&info);\n> -\treturn diff_result_code(&rev.diffopt, 0);\n> +\treturn ret;\n>   }\n>[...]\n> @@ -1434,7 +1438,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q\n>   \t\t\t int keep_index, int patch_mode, int include_untracked, int only_staged)\n>   {\n>   \tint ret = 0;\n> -\tstruct stash_info info;\n> +\tstruct stash_info info = STASH_INFO_INIT;\n\nThere doesn't seem to be a call to free_stash_info() in this function.\n\nBest Wishes\n\nPhillip\n"},{"id":"452901","messageId":"3b08ab38-0d91-a361-544c-c3af68715e79@gmail.com","threadId":"57525","inReplyTo":"patch-v4-10.27-145a0f74b6a-20220331T005325Z-avarab@gmail.com","subject":"Re: [PATCH v4 10/27] stash: always have the owner of \"stash_info\" free it","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2022-04-01T15:33:21Z","receivedAt":"2022-04-01T16:09:51Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"\nHi Ævar\n\nOn 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:\n> Change the initialization of the \"revision\" member of \"struct\n> stash_info\" to be initialized vi a macro, and more importantly that\n> that initializing function be tasked to free it, usually via a \"goto\n> cleanup\" pattern.\n> \n> Despite the \"revision\" name (and the topic of the series containing\n> this commit) the \"stash info\" has nothing to do with the \"struct\n> rev_info\". I'm making this change because in the subsequent commit\n> when we do want to free the \"struct rev_info\" via a \"goto cleanup\"\n> pattern we'd otherwise free() uninitialized memory in some cases, as\n> we only strbuf_init() the string in get_stash_info().\n> \n> So while it's the smallest possible change, let's convert all users of\n> this pattern in the file while we're at it.\n> \n> A good follow-up to this change would be to change all the \"ret = -1;\n> goto done;\" in this file to instead use a \"goto cleanup\", and\n> initialize \"int ret = -1\" at the start of the relevant functions. That\n> would allow us to drop a lot of needless brace verbosity on two-line\n> \"if\" statements, but let's leave that alone for now.\n>[...]   \n> @@ -912,8 +912,11 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n>   \t}\n>   \tlog_tree_diff_flush(&rev);\n>   \n> +\tret = diff_result_code(&rev.diffopt, 0);;\n\nExtra ';'\n\nBest Wishes\n\nPhillip\n"},{"id":"452909","messageId":"xmqqlewoybv3.fsf@gitster.g","threadId":"57525","inReplyTo":"e1bdb860-070b-8596-2a32-e9bc3ad354b0@gmail.com","subject":"Re: [PATCH v4 05/27] revision.[ch]: split freeing of revs->commit into a function","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-01T17:03:44Z","receivedAt":"2022-04-01T17:03:55Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n>>   +static void release_revisions_commit_list(struct rev_info *revs)\n>> +{\n>> +\tif (revs->commits) {\n>\n> This guard copied for the original is not needed, free_commit_list()\n> will handle NULL just fine.\n\nThanks for a careful reading.\n"},{"id":"452910","messageId":"220401.868rsoogxf.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"851584aa-a31d-7446-4994-e641f298f0cd@gmail.com","subject":"Re: [PATCH v4 04/27] format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-01T17:16:38Z","receivedAt":"2022-04-01T17:24:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Apr 01 2022, Phillip Wood wrote:\n\n> Hi Ævar\n>\n> On 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:\n>> Fix two memory leaks in \"struct rev_info\" by freeing that memory in\n>> cmd_format_patch(). These two are unusual special-cases in being in\n>> the \"struct rev_info\", but not being \"owned\" by the code in\n>> revision.c. I.e. they're members of the struct so that this code in\n>> \"builtin/log.c\" can pass information code in log-tree.c.\n>\n> I'm not sure that I necessarily agree that these are owned by\n> builtin/log.c. For rev.extra_headers it is set in builtin/log.c but \n> never used there which makes me think we are transferring ownership to\n> struct rev_info. For ref_message_ids it is less clear cut but having\n> it owned by struct rev_info and freeing it in release_revisions()\n> would make things clearer I think. Having some members owned by struct \n> rev_info but others allocated and freed by other code is confusing and\n> is likely to lead to memory errors. I don't think struct rev_info is \n> borrowing a reference to these items as they are being allocated for\n> it's exclusive use.\n\nThis really isn't for the use of revision.[ch] at all, but just\nsomething that's used as ad-hoc message passing between builtin/log.c\nand log-tree.c. It just so happens that it's done by ferrying it via the\nstruct rev_info.\n\nThe below patch fails tests, it's just a quick (and obviously flawed)\nsearch/replacement that I hacked up to get the removal of these from\nrevision.h to compile, which shows that it's just something between\nlog.c and log-tree.c, pretty much.\n\nAnyway, while I'm 100% in agreement with you that this *should* be fixed\nI'd really like to do the bare minimum to address leaks in this initial\niteration.\n\nI.e. you're right that this relatively fragile, and I'm also concern\nabout catching memory errors.\n\nBut being able to run (most of) the format-patch tests tests as a result\nof this more isolated leak will leave us much better position to\nvalidate any subsequent larger fixes, such as (something like) the\nbelow.\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex c211d66d1d0..dc1acd730d1 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1192,7 +1192,8 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,\n \t\t\t      struct commit *origin,\n \t\t\t      int nr, struct commit **list,\n \t\t\t      const char *branch_name,\n-\t\t\t      int quiet)\n+\t\t\t      int quiet, const char *extra_headers,\n+\t\t\t      struct string_list *ref_message_ids)\n {\n \tconst char *committer;\n \tstruct shortlog log;\n@@ -1212,7 +1213,9 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,\n \t    open_next_file(NULL, rev->numbered_files ? NULL : \"cover-letter\", rev, quiet))\n \t\tdie(_(\"failed to create cover-letter file\"));\n \n-\tlog_write_email_headers(rev, head, &pp.after_subject, &need_8bit_cte, 0);\n+\tlog_write_email_headers(rev, head, pp.after_subject, ref_message_ids,\n+\t\t\t\t&need_8bit_cte, 0);\n+\tpp.after_subject = extra_headers;\n \n \tfor (i = 0; !need_8bit_cte && i < nr; i++) {\n \t\tconst char *buf = get_commit_buffer(list[i], NULL);\n@@ -1777,6 +1780,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tstruct strbuf rdiff2 = STRBUF_INIT;\n \tstruct strbuf rdiff_title = STRBUF_INIT;\n \tint creation_factor = -1;\n+\tchar *extra_headers;\n+\tstruct string_list *ref_message_ids;\n \n \tconst struct option builtin_format_patch_options[] = {\n \t\tOPT_CALLBACK_F('n', \"numbered\", &numbered, NULL,\n@@ -1946,7 +1951,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tstrbuf_addch(&buf, '\\n');\n \t}\n \n-\trev.extra_headers = strbuf_detach(&buf, NULL);\n+\textra_headers = strbuf_detach(&buf, NULL);\n \n \tif (from) {\n \t\tif (split_ident_line(&rev.from_ident, from, strlen(from)))\n@@ -2174,10 +2179,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t}\n \n \tif (in_reply_to || thread || cover_letter)\n-\t\trev.ref_message_ids = xcalloc(1, sizeof(struct string_list));\n+\t\tref_message_ids = xcalloc(1, sizeof(struct string_list));\n \tif (in_reply_to) {\n \t\tconst char *msgid = clean_message_id(in_reply_to);\n-\t\tstring_list_append(rev.ref_message_ids, msgid);\n+\t\tstring_list_append(ref_message_ids, msgid);\n \t}\n \trev.numbered_files = just_numbers;\n \trev.patch_suffix = fmt_patch_suffix;\n@@ -2185,7 +2190,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tif (thread)\n \t\t\tgen_message_id(&rev, \"cover\");\n \t\tmake_cover_letter(&rev, !!output_directory,\n-\t\t\t\t  origin, nr, list, branch_name, quiet);\n+\t\t\t\t  origin, nr, list, branch_name, quiet,\n+\t\t\t\t  extra_headers, ref_message_ids);\n \t\tprint_bases(&bases, rev.diffopt.file);\n \t\tprint_signature(rev.diffopt.file);\n \t\ttotal++;\n@@ -2229,11 +2235,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\t\t\t * --in-reply-to, if specified.\n \t\t\t\t */\n \t\t\t\tif (thread == THREAD_SHALLOW\n-\t\t\t\t    && rev.ref_message_ids->nr > 0\n+\t\t\t\t    && ref_message_ids->nr > 0\n \t\t\t\t    && (!cover_letter || rev.nr > 1))\n \t\t\t\t\tfree(rev.message_id);\n \t\t\t\telse\n-\t\t\t\t\tstring_list_append(rev.ref_message_ids,\n+\t\t\t\t\tstring_list_append(ref_message_ids,\n \t\t\t\t\t\t\t   rev.message_id);\n \t\t\t}\n \t\t\tgen_message_id(&rev, oid_to_hex(&commit->object.oid));\ndiff --git a/log-tree.c b/log-tree.c\nindex 38e5cccc1a1..66170b75254 100644\n--- a/log-tree.c\n+++ b/log-tree.c\n@@ -419,11 +419,11 @@ void fmt_output_email_subject(struct strbuf *sb, struct rev_info *opt)\n }\n \n void log_write_email_headers(struct rev_info *opt, struct commit *commit,\n-\t\t\t     const char **extra_headers_p,\n+\t\t\t     const char *extra_headers,\n+\t\t\t     struct string_list *ref_message_ids,\n \t\t\t     int *need_8bit_cte_p,\n \t\t\t     int maybe_multipart)\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 \n@@ -435,13 +435,13 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,\n \t\tfprintf(opt->diffopt.file, \"Message-Id: <%s>\\n\", opt->message_id);\n \t\tgraph_show_oneline(opt->graph);\n \t}\n-\tif (opt->ref_message_ids && opt->ref_message_ids->nr > 0) {\n+\tif (ref_message_ids && ref_message_ids->nr > 0) {\n \t\tint i, n;\n-\t\tn = opt->ref_message_ids->nr;\n-\t\tfprintf(opt->diffopt.file, \"In-Reply-To: <%s>\\n\", opt->ref_message_ids->items[n-1].string);\n+\t\tn = ref_message_ids->nr;\n+\t\tfprintf(opt->diffopt.file, \"In-Reply-To: <%s>\\n\", ref_message_ids->items[n-1].string);\n \t\tfor (i = 0; i < n; i++)\n \t\t\tfprintf(opt->diffopt.file, \"%s<%s>\\n\", (i > 0 ? \"\\t\" : \"References: \"),\n-\t\t\t       opt->ref_message_ids->items[i].string);\n+\t\t\t       ref_message_ids->items[i].string);\n \t\tgraph_show_oneline(opt->graph);\n \t}\n \tif (opt->mime_boundary && maybe_multipart) {\n@@ -488,7 +488,6 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,\n \t\topt->diffopt.stat_sep = buffer.buf;\n \t\tstrbuf_release(&filename);\n \t}\n-\t*extra_headers_p = extra_headers;\n }\n \n static void show_sig_lines(struct rev_info *opt, int status, const char *bol)\n@@ -621,13 +620,12 @@ static void next_commentary_block(struct rev_info *opt, struct strbuf *sb)\n \topt->shown_dashes = 1;\n }\n \n-void show_log(struct rev_info *opt)\n+void show_log_extra_headers(struct rev_info *opt, const char *extra_headers)\n {\n \tstruct strbuf msgbuf = STRBUF_INIT;\n \tstruct log_info *log = opt->loginfo;\n \tstruct commit *commit = log->commit, *parent = log->parent;\n \tint abbrev_commit = opt->abbrev_commit ? opt->abbrev : the_hash_algo->hexsz;\n-\tconst char *extra_headers = opt->extra_headers;\n \tstruct pretty_print_context ctx = {0};\n \n \topt->loginfo = NULL;\n@@ -687,7 +685,7 @@ void show_log(struct rev_info *opt)\n \t */\n \n \tif (cmit_fmt_is_mail(opt->commit_format)) {\n-\t\tlog_write_email_headers(opt, commit, &extra_headers,\n+\t\tlog_write_email_headers(opt, commit, extra_headers, NULL,\n \t\t\t\t\t&ctx.need_8bit_cte, 1);\n \t\tctx.rev = opt;\n \t\tctx.print_email_subject = 1;\n@@ -848,6 +846,11 @@ void show_log(struct rev_info *opt)\n \t}\n }\n \n+void show_log(struct rev_info *opt)\n+{\n+\tshow_log_extra_headers(opt, NULL);\n+}\n+\n int log_tree_diff_flush(struct rev_info *opt)\n {\n \topt->shown_dashes = 0;\ndiff --git a/log-tree.h b/log-tree.h\nindex e7e4641cf83..32ae026f6fb 100644\n--- a/log-tree.h\n+++ b/log-tree.h\n@@ -16,6 +16,7 @@ struct decoration_filter {\n int parse_decorate_color_config(const char *var, const char *slot_name, const char *value);\n int log_tree_diff_flush(struct rev_info *);\n int log_tree_commit(struct rev_info *, struct commit *);\n+void show_log_extra_headers(struct rev_info *opt, const char *extra_headers);\n void show_log(struct rev_info *opt);\n void format_decorations_extended(struct strbuf *sb, const struct commit *commit,\n \t\t\t     int use_color,\n@@ -26,7 +27,8 @@ void format_decorations_extended(struct strbuf *sb, const struct commit *commit,\n \t\t\t     format_decorations_extended((strbuf), (commit), (color), \" (\", \", \", \")\")\n void show_decorations(struct rev_info *opt, struct commit *commit);\n void log_write_email_headers(struct rev_info *opt, struct commit *commit,\n-\t\t\t     const char **extra_headers_p,\n+\t\t\t     const char *extra_headers,\n+\t\t\t     struct string_list *ref_message_ids,\n \t\t\t     int *need_8bit_cte_p,\n \t\t\t     int maybe_multipart);\n void load_ref_decorations(struct decoration_filter *filter, int flags);\ndiff --git a/revision.h b/revision.h\nindex 5bc59c7bfe1..e2e08673bba 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -243,9 +243,7 @@ struct rev_info {\n \tconst char\t*reroll_count;\n \tchar\t\t*message_id;\n \tstruct ident_split from_ident;\n-\tstruct string_list *ref_message_ids;\n \tint\t\tadd_signoff;\n-\tconst char\t*extra_headers;\n \tconst char\t*log_reencode;\n \tconst char\t*subject_prefix;\n \tint\t\tpatch_name_max;\n"},{"id":"452911","messageId":"220401.864k3cogvk.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"fb26835b-c2fe-be3c-a7c4-6ea8b5748969@gmail.com","subject":"Re: [PATCH v4 06/27] revision.[ch]: provide and start using a release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-01T17:25:07Z","receivedAt":"2022-04-01T17:25:42Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Apr 01 2022, Phillip Wood wrote:\n\n> Hi Ævar\n>\n> On 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:\n>> The users of the revision.[ch] API's \"struct rev_info\" are a major\n>> source of memory leaks in the test suite under SANITIZE=leak, which in\n>> turn adds a lot of noise when trying to mark up tests with\n>> \"TEST_PASSES_SANITIZE_LEAK=true\".\n>> The users of that API are largely one-shot, e.g. \"git log\", so\n>> arguably freeing the memory is a waste of time, but in many cases\n>> they've actually been trying to free the memory, and just doing that\n>> in a buggy manner.\n>\n> I've not looked that closely at these changes but builtin/log.c is not\n> modified so I'm a bit confused by the mention of \"git log\" above.\n\nAlong with git-rev-list it's the main user-facing interface to\nrevision.c. I'll clarify that in a re-roll.\n"},{"id":"452912","messageId":"220401.86zgl4n20f.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"9a487124-ef1e-0644-9585-174343555914@gmail.com","subject":"Re: [PATCH v4 10/27] stash: always have the owner of \"stash_info\" free it","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-01T17:29:05Z","receivedAt":"2022-04-01T17:32:06Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Fri, Apr 01 2022, Phillip Wood wrote:\n\n> Hi Ævar\n>\n> On 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:\n>> Change the initialization of the \"revision\" member of \"struct\n>> stash_info\" to be initialized vi a macro, and more importantly that\n>> that initializing function be tasked to free it, usually via a \"goto\n>> cleanup\" pattern.\n>> Despite the \"revision\" name (and the topic of the series containing\n>> this commit) the \"stash info\" has nothing to do with the \"struct\n>> rev_info\". I'm making this change because in the subsequent commit\n>> when we do want to free the \"struct rev_info\" via a \"goto cleanup\"\n>> pattern we'd otherwise free() uninitialized memory in some cases, as\n>> we only strbuf_init() the string in get_stash_info().\n>> So while it's the smallest possible change, let's convert all users\n>> of\n>> this pattern in the file while we're at it.\n>> A good follow-up to this change would be to change all the \"ret =\n>> -1;\n>> goto done;\" in this file to instead use a \"goto cleanup\", and\n>> initialize \"int ret = -1\" at the start of the relevant functions. That\n>> would allow us to drop a lot of needless brace verbosity on two-line\n>> \"if\" statements, but let's leave that alone for now.\n>\n> You seem to have made this change here.\n>\n>>[...]\n>> @@ -861,10 +863,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n>>   \t\t\tstrvec_push(&revision_args, argv[i]);\n>>   \t}\n>>   -\tret = get_stash_info(&info, stash_args.nr, stash_args.v);\n>> -\tstrvec_clear(&stash_args);\n>> -\tif (ret)\n>> -\t\treturn -1;\n>> +\tif (get_stash_info(&info, stash_args.nr, stash_args.v))\n>> +\t\tgoto cleanup;\n>>     \t/*\n>>   \t * The config settings are applied only if there are not passed\n>> @@ -878,8 +878,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n>>   \t\t\trev.diffopt.output_format |= DIFF_FORMAT_PATCH;\n>>     \t\tif (!show_stat && !show_patch) {\n>> -\t\t\tfree_stash_info(&info);\n>> -\t\t\treturn 0;\n>> +\t\t\tret = 0;\n>> +\t\t\tgoto cleanup;\n>>   \t\t}\n>>   \t}\n>>   @@ -912,8 +912,11 @@ static int show_stash(int argc, const char\n>> **argv, const char *prefix)\n>>   \t}\n>>   \tlog_tree_diff_flush(&rev);\n>>   +\tret = diff_result_code(&rev.diffopt, 0);;\n>> +cleanup:\n>> +\tstrvec_clear(&stash_args);\n>\n> This seems to be fixing a leak that's not mentioned in the commit message.\n\nThis is just moving the exsting strvec_clear() shown above to the\n\"cleanup\" branch, as we change return to \"goto cleanup\".\n\n>>   \tfree_stash_info(&info);\n>> -\treturn diff_result_code(&rev.diffopt, 0);\n>> +\treturn ret;\n>>   }\n>>[...]\n>> @@ -1434,7 +1438,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q\n>>   \t\t\t int keep_index, int patch_mode, int include_untracked, int only_staged)\n>>   {\n>>   \tint ret = 0;\n>> -\tstruct stash_info info;\n>> +\tstruct stash_info info = STASH_INFO_INIT;\n>\n> There doesn't seem to be a call to free_stash_info() in this function.\n\nHrm, I think you're right about that (but it was an existing leak). Will\nfix.\n"},{"id":"452916","messageId":"xmqq35iwwqe9.fsf@gitster.g","threadId":"57525","inReplyTo":"220401.868rsoogxf.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v4 04/27] format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-01T19:32:46Z","receivedAt":"2022-04-01T19:32:54Z","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 below patch fails tests, it's just a quick (and obviously flawed)\n> search/replacement that I hacked up to get the removal of these from\n> revision.h to compile, which shows that it's just something between\n> log.c and log-tree.c, pretty much.\n\nGiven that the way most end-users see log-tree.c in action is via\nlog.c (is there a useful entry point into log-tree that bypasses\nlog.c that implements end-user actions?), I would actually think it\nis that extra_headers is quite an integral part of rev_info\nstructure, which is how the revision traversal API passes\ninformation relevant to the current traversal around various layors\nof the implementation.  So, I am not sure what the above experiment\nshows us.\n\n> Anyway, while I'm 100% in agreement with you that this *should* be fixed\n> I'd really like to do the bare minimum to address leaks in this initial\n> iteration.\n>\n> I.e. you're right that this relatively fragile, and I'm also concern\n> about catching memory errors.\n\nAs long as we know there are yet more work to do (are we leaving\nNEEDSWORK comments as we go, by the way?), I think that's fine.  We\ncannot go all the way in one go for a large code base like revisions\nAPI, and we can leave a small corner \"not quite optimial\", and we can\neven leave it in \"sometimes still leaks\" status, as long as we never\nmake anything to free too much by mistake.\n"},{"id":"452925","messageId":"14c17fa9-e9ff-ac5f-dbda-4a566ed09fd3@gmail.com","threadId":"57525","inReplyTo":"patch-v4-13.27-02ca92660af-20220331T005325Z-avarab@gmail.com","subject":"Re: [PATCH v4 13/27] revisions API users: use release_revisions() in builtin/log.c","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2022-04-02T09:22:44Z","receivedAt":"2022-04-02T09:22:55Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Ævar\n\nOn 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:\n> In preparation for having the \"log\" family of functions make wider use\n> of release_revisions() let's have them call it just before\n> exiting. This changes the \"log\", \"whatchanged\", \"show\",\n> \"format-patch\", etc. commands, all of which live in this file.\n> \n> The release_revisions() API still only frees the \"pending\" member, but\n> will learn to release more members of \"struct rev_info\" in subsequent\n> commits.\n> \n> In the case of \"format-patch\" revert the addition of UNLEAK() in\n> dee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16),\n> which will cause several tests that previously passed under\n> \"TEST_PASSES_SANITIZE_LEAK=true\" to start failing.\n> \n> In subsequent commits we'll now be able to use those tests to check\n> whether that part of the API is really leaking memory, and will fix\n> all of those memory leaks. Removing the UNLEAK() allows us to make\n> incremental progress in that direction. See [1] for further details\n> about this approach.\n\nThis breaks \"git bisect\" but only when running the test suite to detect \nleaks so I guess that's not too bad. An alternative would be to manually \nremove the UNLEAK() when you're testing rather than committing the change.\n\n> Note that the release_revisions() will not be sufficient to deal with\n> the code in cmd_show() added in 5d7eeee2ac6 (git-show: grok blobs,\n> trees and tags, too, 2006-12-14) which clobbers the \"pending\" array in\n> the case of \"OBJ_COMMIT\". That will need to be dealt with by some\n> future follow-up work.\n> \n> 1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>   builtin/log.c          | 20 ++++++++++++--------\n>   t/t4126-apply-empty.sh |  2 --\n>   2 files changed, 12 insertions(+), 10 deletions(-)\n> \n> diff --git a/builtin/log.c b/builtin/log.c\n> index 6f9928fabfe..c40ebe1c3f4 100644\n> --- a/builtin/log.c\n> +++ b/builtin/log.c\n> @@ -295,6 +295,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,\n>   \tcmd_log_init_finish(argc, argv, prefix, rev, opt);\n>   }\n>   \n> +static int cmd_log_deinit(int ret, struct rev_info *rev)\n> +{\n> +\trelease_revisions(rev);\n> +\treturn ret;\n> +}\n\n\n>   /*\n>    * This gives a rough estimate for how many commits we\n>    * will print out in the list.\n> @@ -558,7 +564,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)\n>   \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n>   \tif (!rev.diffopt.output_format)\n>   \t\trev.diffopt.output_format = DIFF_FORMAT_RAW;\n> -\treturn cmd_log_walk(&rev);\n> +\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n\nThis is a rather unusual pattern, at first I wondered if there were \ngoing to be more added to the body of cmd_log_deinit() in later commits \nbut there isn't so why not just call release_revisions() here to be \nconsistent with the other release_revisions() call that are added in \nother patches?\n\n\nBest Wishes\n\nPhillip\n"},{"id":"452927","messageId":"patch-v5-01.27-e20f951a64c-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 01/27] t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:15Z","receivedAt":"2022-04-02T10:49:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak that's been with us since f9500261e0a (fast-rebase:\nwrite conflict state to working tree, index, and HEAD, 2021-05-20)\nchanged this code to move these strbuf_release() into an if/else\nblock.\n\nWe'll also add to \"reflog_msg\" in the \"else\" arm of the \"if\" block\nbeing modified here, and we'll append to \"branch_msg\" in both\ncases. But after f9500261e0a only the \"if\" block would free these two\n\"struct strbuf\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/helper/test-fast-rebase.c | 5 +++--\n 1 file changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c\nindex fc2d4609043..993b90eaedd 100644\n--- a/t/helper/test-fast-rebase.c\n+++ b/t/helper/test-fast-rebase.c\n@@ -201,8 +201,6 @@ int cmd__fast_rebase(int argc, const char **argv)\n \t\t}\n \t\tif (create_symref(\"HEAD\", branch_name.buf, reflog_msg.buf) < 0)\n \t\t\tdie(_(\"unable to update HEAD\"));\n-\t\tstrbuf_release(&reflog_msg);\n-\t\tstrbuf_release(&branch_name);\n \n \t\tprime_cache_tree(the_repository, the_repository->index,\n \t\t\t\t result.tree);\n@@ -221,5 +219,8 @@ int cmd__fast_rebase(int argc, const char **argv)\n \tif (write_locked_index(&the_index, &lock,\n \t\t\t       COMMIT_LOCK | SKIP_IF_UNCHANGED))\n \t\tdie(_(\"unable to write %s\"), get_index_file());\n+\n+\tstrbuf_release(&reflog_msg);\n+\tstrbuf_release(&branch_name);\n \treturn (result.clean == 0);\n }\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452928","messageId":"patch-v5-02.27-de2e08a14e6-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 02/27] blame: use \"goto cleanup\" for cleanup_scoreboard()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:16Z","receivedAt":"2022-04-02T10:49:54Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Amend a freeing pattern added in 0906ac2b54b (blame: use changed-path\nBloom filters, 2020-04-16) to use a \"goto cleanup\", so that we can be\nsure that we call cleanup_scoreboard().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/blame.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 8d15b68afc9..885b381ab83 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1167,7 +1167,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tif (!incremental)\n \t\tsetup_pager();\n \telse\n-\t\treturn 0;\n+\t\tgoto cleanup;\n \n \tblame_sort_final(&sb);\n \n@@ -1201,6 +1201,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\tprintf(\"num commits: %d\\n\", sb.num_commits);\n \t}\n \n+cleanup:\n \tcleanup_scoreboard(&sb);\n \treturn 0;\n }\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452929","messageId":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v4-00.27-00000000000-20220331T005325Z-avarab@gmail.com","subject":"[PATCH v5 00/27] revision.[ch]: add and use release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:14Z","receivedAt":"2022-04-02T10:49:55Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This series reduces the memory leaks spotted by our tests by at least\nhalf, see v3[1] for a more general overview.\n\nThanks a lot to Phillip Wood and Junio for the v4 review! I think this\nv5 should address all of the things brought up in v4 in one way or\nanother, changes:\n\n * The \"extra_headers\" or \"ref_message_ids\" freeing is the same, but\n   the commit message is altered to explain that commit.\n\n * The \"revision.[ch]: split freeing of revs->commit into a function\"\n   is gone, instead I applied a coccinelle rule to free_commit_list()\n   as suggested by Phillip Wood (well, the change in pattern, not the\n   rule was the suggestion).\n\n * The release_revisions() is now simply using free_commit_list()\n   without a NULL assignment to the member, and I changed some\n   FREE_AND_NULL() that I'd added (for e.g. cmdline) to just free().\n\n   The \"and NULL\" was just cargo-culted from elswhere, but for\n   release_revisions() we not only aren't using the struct again (it's\n   not a \"reset\"), but it it wouldn't have worked as a reset before\n   anyway (some things free'd without NULL-ing).\n\n   We could trivially have a \"reset\" variant of release_revisions(),\n   but since no current API user wanted it let's leave it, leving the\n   memory uninitialized will help to catch any invalid API usage.\n\n * I missed a object_array_clear() in builtin/stash.c that\n   could/should be free'd by release_revisions().\n\n * An obscure memory leak in stash.c (on \"master\") around\n   assert_stash_ref() has been fixed.\n\n * Add a \"goto usage\" pattern for a memory leak new in stash.c that\n   this series introduced, i.e. because strvec_clear() was moved to a\n   \"cleanup\" block if we invoked usage_with_options() between the two\n   we'd exit without the cleanup. Now we'll cleanup on \"usage\" too.\n\n * Updated commit message & docs around REV_INFO_INIT, hopefully\n   addressing the thread left off at\n   https://lore.kernel.org/git/220401.86h77dnkpw.gmgdl@evledraar.gmail.com/\n\n1. https://lore.kernel.org/git/cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com/\n\nÆvar Arnfjörð Bjarmason (27):\n  t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"\n  blame: use \"goto cleanup\" for cleanup_scoreboard()\n  string_list API users: use string_list_init_{no,}dup\n  format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"\n  cocci: add and apply free_commit_list() rules\n  revision.[ch]: provide and start using a release_revisions()\n  revisions API users: add straightforward release_revisions()\n  revision.[ch]: document and move code declared around \"init\"\n  revisions API users: use release_revisions() needing REV_INFO_INIT\n  stash: always have the owner of \"stash_info\" free it\n  revisions API users: add \"goto cleanup\" for release_revisions()\n  revisions API users: use release_revisions() in http-push.c\n  revisions API users: use release_revisions() in builtin/log.c\n  revisions API users: use release_revisions() with UNLEAK()\n  revisions API users: use release_revisions() for \"prune_data\" users\n  revisions API: have release_revisions() release \"commits\"\n  revisions API: have release_revisions() release \"mailmap\"\n  revisions API: have release_revisions() release \"cmdline\"\n  revisions API: have release_revisions() release \"filter\"\n  revisions API: have release_revisions() release \"grep_filter\"\n  revisions API: have release_revisions() release \"prune_data\"\n  revisions API: clear \"boundary_commits\" in release_revisions()\n  revisions API: release \"reflog_info\" in release revisions()\n  revisions API: call diff_free(&revs->pruning) in revisions_release()\n  revisions API: have release_revisions() release \"date_mode\"\n  revisions API: have release_revisions() release \"topo_walk_info\"\n  revisions API: add a TODO for diff_free(&revs->diffopt)\n\n add-interactive.c                          |   3 +-\n bisect.c                                   |  20 ++--\n builtin/add.c                              |   3 +-\n builtin/am.c                               |   3 +\n builtin/bisect--helper.c                   |   2 +\n builtin/blame.c                            |   4 +-\n builtin/checkout.c                         |   3 +-\n builtin/commit.c                           |   6 +-\n builtin/describe.c                         |   2 +\n builtin/diff-files.c                       |   8 +-\n builtin/diff-index.c                       |   5 +-\n builtin/diff.c                             |   2 +-\n builtin/fast-export.c                      |   1 +\n builtin/log.c                              |  36 ++++---\n builtin/merge.c                            |   2 +\n builtin/pack-objects.c                     |   2 +\n builtin/prune.c                            |   1 +\n builtin/reflog.c                           |   1 +\n builtin/rev-list.c                         |  25 +++--\n builtin/shortlog.c                         |   8 +-\n builtin/stash.c                            | 115 +++++++++++----------\n builtin/submodule--helper.c                |  14 ++-\n bundle.c                                   |  12 ++-\n commit.c                                   |  19 ++--\n contrib/coccinelle/free.cocci              |  27 +++++\n diff-lib.c                                 |   8 +-\n fmt-merge-msg.c                            |   1 +\n http-push.c                                |   3 +-\n merge-ort.c                                |   1 +\n merge-recursive.c                          |   5 +-\n midx.c                                     |   1 +\n pack-bitmap-write.c                        |   1 +\n range-diff.c                               |   2 +-\n ref-filter.c                               |   1 +\n reflog-walk.c                              |  24 ++++-\n reflog-walk.h                              |   1 +\n remote.c                                   |   1 +\n revision.c                                 |  72 ++++++++++---\n revision.h                                 |  73 ++++++++-----\n sequencer.c                                |  26 +++--\n shallow.c                                  |   1 +\n submodule.c                                |  11 +-\n t/helper/test-fast-rebase.c                |  23 +++--\n t/helper/test-revision-walking.c           |   1 +\n t/lib-git-svn.sh                           |   4 +\n t/t0056-git-C.sh                           |   1 +\n t/t0062-revision-walking.sh                |   1 +\n t/t0100-previous.sh                        |   1 +\n t/t0101-at-syntax.sh                       |   2 +\n t/t1001-read-tree-m-2way.sh                |   1 +\n t/t1002-read-tree-m-u-2way.sh              |   1 +\n t/t1060-object-corruption.sh               |   2 +\n t/t1401-symbolic-ref.sh                    |   2 +\n t/t1411-reflog-show.sh                     |   1 +\n t/t1412-reflog-loop.sh                     |   2 +\n t/t1415-worktree-refs.sh                   |   1 +\n t/t2015-checkout-unborn.sh                 |   1 +\n t/t2200-add-update.sh                      |   1 +\n t/t3302-notes-index-expensive.sh           |   1 +\n t/t3303-notes-subtrees.sh                  |   1 +\n t/t3305-notes-fanout.sh                    |   1 +\n t/t3408-rebase-multi-line.sh               |   1 +\n t/t4021-format-patch-numbered.sh           |   1 +\n t/t4027-diff-submodule.sh                  |   1 +\n t/t4028-format-patch-mime-headers.sh       |   2 +\n t/t4036-format-patch-signer-mime.sh        |   1 +\n t/t4039-diff-assume-unchanged.sh           |   1 +\n t/t4055-diff-context.sh                    |   1 +\n t/t4066-diff-emit-delay.sh                 |   1 +\n t/t4122-apply-symlink-inside.sh            |   1 +\n t/t4126-apply-empty.sh                     |   1 -\n t/t4128-apply-root.sh                      |   1 +\n t/t4206-log-follow-harder-copies.sh        |   2 +\n t/t4207-log-decoration-colors.sh           |   1 +\n t/t4212-log-corrupt.sh                     |   1 +\n t/t5301-sliding-window.sh                  |   2 +\n t/t5313-pack-bounds-checks.sh              |   2 +\n t/t5316-pack-delta-depth.sh                |   2 +\n t/t5320-delta-islands.sh                   |   2 +\n t/t5322-pack-objects-sparse.sh             |   1 +\n t/t5506-remote-groups.sh                   |   1 +\n t/t5513-fetch-track.sh                     |   1 +\n t/t5515-fetch-merge-logic.sh               |   1 +\n t/t5518-fetch-exit-status.sh               |   1 +\n t/t5532-fetch-proxy.sh                     |   2 +\n t/t5600-clone-fail-cleanup.sh              |   1 +\n t/t5900-repo-selection.sh                  |   2 +\n t/t6002-rev-list-bisect.sh                 |   1 +\n t/t6003-rev-list-topo-order.sh             |   1 +\n t/t6005-rev-list-count.sh                  |   1 +\n t/t6018-rev-list-glob.sh                   |   1 +\n t/t6100-rev-list-in-order.sh               |   1 +\n t/t6101-rev-parse-parents.sh               |   1 +\n t/t6110-rev-list-sparse.sh                 |   1 +\n t/t6114-keep-packs.sh                      |   2 +\n t/t6131-pathspec-icase.sh                  |   2 +\n t/t7008-filter-branch-null-sha1.sh         |   1 +\n t/t7702-repack-cyclic-alternate.sh         |   2 +\n t/t9001-send-email.sh                      |   1 +\n t/t9100-git-svn-basic.sh                   |   1 +\n t/t9101-git-svn-props.sh                   |   2 +\n t/t9104-git-svn-follow-parent.sh           |   2 +\n t/t9106-git-svn-commit-diff-clobber.sh     |   2 +\n t/t9115-git-svn-dcommit-funky-renames.sh   |   1 +\n t/t9116-git-svn-log.sh                     |   1 +\n t/t9122-git-svn-author.sh                  |   2 +\n t/t9127-git-svn-partial-rebuild.sh         |   1 +\n t/t9129-git-svn-i18n-commitencoding.sh     |   1 +\n t/t9132-git-svn-broken-symlink.sh          |   1 +\n t/t9139-git-svn-non-utf8-commitencoding.sh |   1 +\n t/t9146-git-svn-empty-dirs.sh              |   2 +\n t/t9148-git-svn-propset.sh                 |   1 +\n t/t9160-git-svn-preserve-empty-dirs.sh     |   1 +\n t/t9162-git-svn-dcommit-interactive.sh     |   2 +\n t/t9164-git-svn-dcommit-concurrent.sh      |   2 +\n t/t9501-gitweb-standalone-http-status.sh   |   1 +\n wt-status.c                                |  15 +--\n 117 files changed, 502 insertions(+), 190 deletions(-)\n\nRange-diff against v4:\n 1:  b99a07f98ee =  1:  e20f951a64c t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"\n 2:  da5be507c0f =  2:  de2e08a14e6 blame: use \"goto cleanup\" for cleanup_scoreboard()\n 3:  243f7c8df96 =  3:  364aa7d8990 string_list API users: use string_list_init_{no,}dup\n 4:  69f0aabe38f !  4:  5d4ad92d47b format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"\n    @@ Commit message\n         cmd_format_patch(). These two are unusual special-cases in being in\n         the \"struct rev_info\", but not being \"owned\" by the code in\n         revision.c. I.e. they're members of the struct so that this code in\n    -    \"builtin/log.c\" can pass information code in log-tree.c.\n    +    \"builtin/log.c\" can conveniently pass information code in\n    +    \"log-tree.c\".\n    +\n    +    See e.g. the make_cover_letter() caller of log_write_email_headers()\n    +    here in \"builtin/log.c\", and [1] for a demonstration of where the\n    +    \"extra_headers\" and \"ref_message_ids\" struct members are used.\n     \n         See 20ff06805c6 (format-patch: resurrect extra headers from config,\n         2006-06-02) and d1566f7883f (git-format-patch: Make the second and\n    @@ Commit message\n         variable to hold it, to avoid the eventual cast from \"const char *\"\n         when we free() it.\n     \n    +    1. https://lore.kernel.org/git/220401.868rsoogxf.gmgdl@evledraar.gmail.com/\n    +\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/log.c ##\n 5:  2d0ed57ec2e <  -:  ----------- revision.[ch]: split freeing of revs->commit into a function\n -:  ----------- >  5:  c38db28f920 cocci: add and apply free_commit_list() rules\n 6:  b9beaba16bf !  6:  b99f96599bb revision.[ch]: provide and start using a release_revisions()\n    @@ Commit message\n         turn adds a lot of noise when trying to mark up tests with\n         \"TEST_PASSES_SANITIZE_LEAK=true\".\n     \n    -    The users of that API are largely one-shot, e.g. \"git log\", so\n    -    arguably freeing the memory is a waste of time, but in many cases\n    -    they've actually been trying to free the memory, and just doing that\n    -    in a buggy manner.\n    +    The users of that API are largely one-shot, e.g. \"git rev-list\" or\n    +    \"git log\", or the \"git checkout\" and \"git stash\" being modified here\n    +\n    +    For these callers freeing the memory is arguably a waste of time, but\n    +    in many cases they've actually been trying to free the memory, and\n    +    just doing that in a buggy manner.\n     \n         Let's provide a release_revisions() function for these users, and\n         start migrating them over per the plan outlined in [1]. Right now this\n    @@ builtin/checkout.c: static void show_local_changes(struct object *head,\n      static void describe_detached_head(const char *msg, struct commit *commit)\n     \n      ## builtin/stash.c ##\n    +@@ builtin/stash.c: static int check_changes_tracked_files(const struct pathspec *ps)\n    + \t\tgoto done;\n    + \t}\n    + \n    +-\tobject_array_clear(&rev.pending);\n    + \tresult = run_diff_files(&rev, 0);\n    + \tif (diff_result_code(&rev.diffopt, result)) {\n    + \t\tret = 1;\n    +@@ builtin/stash.c: static int check_changes_tracked_files(const struct pathspec *ps)\n    + \n    + done:\n    + \tclear_pathspec(&rev.prune_data);\n    ++\trelease_revisions(&rev);\n    + \treturn ret;\n    + }\n    + \n     @@ builtin/stash.c: static int stash_working_tree(struct stash_info *info, const struct pathspec *ps\n      \n      done:\n    @@ range-diff.c: int is_range_diff_range(const char *arg)\n      }\n     \n      ## revision.c ##\n    -@@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)\n    - \t}\n    +@@ revision.c: int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n    + \treturn left;\n      }\n      \n     +void release_revisions(struct rev_info *revs)\n 7:  80ec9a131c0 =  7:  521f6967b64 revisions API users: add straightforward release_revisions()\n 8:  086cec742b4 !  8:  003b507e0b7 revision.[ch]: document and move code declared around \"init\"\n    @@ revision.h: void mark_trees_uninteresting_sparse(struct repository *r, struct oi\n      void show_object_with_name(FILE *, struct object *, const char *);\n      \n     +/**\n    -+ * Helpers to check if a \"struct string_list\" item matches wild\n    ++ * Helpers to check if a \"struct string_list\" item matches with\n     + * wildmatch().\n     + */\n     +int ref_excluded(struct string_list *, const char *path);\n 9:  2f4e65fb534 !  9:  240307bc3c6 revisions API users: use release_revisions() needing REV_INFO_INIT\n    @@ Commit message\n         need to have their \"struct rev_info\" zero-initialized before we can\n         start using it.\n     \n    -    To do this add a stub \"REV_INFO_INIT\" macro, ideally macro would be\n    -    able to fully initialize a \"struct rev_info\", but all it does is the\n    -    equivalent of assigning \"{ 0 }\" to the struct, the API user will still\n    -    need to use repo_init_revisions(). In some future follow-up work we'll\n    -    hopefully make REV_INFO_INIT be a \"stand-alone\" init likke STRBUF_INIT\n    -    and other similar macros.\n    -\n         For the bundle.c code see the early exit case added in\n         3bbbe467f29 (bundle verify: error out if called without an object\n         database, 2019-05-27).\n    @@ revision.h: struct rev_info {\n     +/**\n     + * Initialize the \"struct rev_info\" structure with a macro.\n     + *\n    -+ * This is not sufficient (yet!) to initialize a \"struct rev_info\",\n    -+ * but it's OK (but redundant) to use it before a call to\n    -+ * repo_init_revisions(), which does the real initialization. By using\n    -+ * this it's safe to call release_revisions() on the \"struct rev_info\"\n    -+ * without having called repo_init_revisions().\n    ++ * This will not fully initialize a \"struct rev_info\", the\n    ++ * repo_init_revisions() function needs to be called before\n    ++ * setup_revisions() and any revision walking takes place.\n    ++ *\n    ++ * Use REV_INFO_INIT to make the \"struct rev_info\" safe for passing to\n    ++ * release_revisions() when it's inconvenient (e.g. due to a \"goto\n    ++ * cleanup\" pattern) to arrange for repo_init_revisions() to be called\n    ++ * before release_revisions() is called.\n    ++ *\n    ++ * Initializing with this REV_INFO_INIT is redundant to invoking\n    ++ * repo_init_revisions(). If repo_init_revisions() is guaranteed to be\n    ++ * called before release_revisions() the \"struct rev_info\" can be left\n    ++ * uninitialized.\n     + */\n     +#define REV_INFO_INIT { 0 }\n     +\n    @@ submodule.c: void show_submodule_diff_summary(struct diff_options *o, const char\n      \tstruct commit_list *merge_bases = NULL;\n      \tstruct repository *sub;\n     @@ submodule.c: void show_submodule_diff_summary(struct diff_options *o, const char *path,\n    - \tprint_submodule_diff_summary(sub, &rev, o);\n      \n      out:\n    + \tfree_commit_list(merge_bases);\n     +\trelease_revisions(&rev);\n    - \tif (merge_bases)\n    - \t\tfree_commit_list(merge_bases);\n      \tclear_commit_marks(left, ~0);\n    + \tclear_commit_marks(right, ~0);\n    + \tif (sub) {\n10:  145a0f74b6a ! 10:  4c5404912e9 stash: always have the owner of \"stash_info\" free it\n    @@ Commit message\n         pattern we'd otherwise free() uninitialized memory in some cases, as\n         we only strbuf_init() the string in get_stash_info().\n     \n    -    So while it's the smallest possible change, let's convert all users of\n    -    this pattern in the file while we're at it.\n    +    So while it's not the smallest possible change, let's convert all\n    +    users of this pattern in the file while we're at it.\n     \n         A good follow-up to this change would be to change all the \"ret = -1;\n         goto done;\" in this file to instead use a \"goto cleanup\", and\n    @@ Commit message\n         would allow us to drop a lot of needless brace verbosity on two-line\n         \"if\" statements, but let's leave that alone for now.\n     \n    +    To ensure that there's a 1=1 mapping between owners of the \"struct\n    +    stash_info\" and free_stash_info() change the assert_stash_ref()\n    +    function to be a trivial get_stash_info_assert() wrapper. The caller\n    +    will call free_stash_info(), and by returning -1 we'll eventually (via\n    +    !!ret) exit with status 1 anyway.\n    +\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## builtin/stash.c ##\n    @@ builtin/stash.c: static int apply_stash(int argc, const char **argv, const char\n      \tfree_stash_info(&info);\n      \treturn ret;\n      }\n    -@@ builtin/stash.c: static void assert_stash_ref(struct stash_info *info)\n    +@@ builtin/stash.c: static int do_drop_stash(struct stash_info *info, int quiet)\n    + \treturn 0;\n    + }\n    + \n    +-static void assert_stash_ref(struct stash_info *info)\n    ++static int get_stash_info_assert(struct stash_info *info, int argc,\n    ++\t\t\t\t const char **argv)\n    + {\n    +-\tif (!info->is_stash_ref) {\n    +-\t\terror(_(\"'%s' is not a stash reference\"), info->revision.buf);\n    +-\t\tfree_stash_info(info);\n    +-\t\texit(1);\n    +-\t}\n    ++\tint ret = get_stash_info(info, argc, argv);\n    ++\n    ++\tif (ret < 0)\n    ++\t\treturn ret;\n    ++\n    ++\tif (!info->is_stash_ref)\n    ++\t\treturn error(_(\"'%s' is not a stash reference\"), info->revision.buf);\n    ++\n    ++\treturn 0;\n    + }\n      \n      static int drop_stash(int argc, const char **argv, const char *prefix)\n      {\n    @@ builtin/stash.c: static void assert_stash_ref(struct stash_info *info)\n      \t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n      \t\tOPT_END()\n     @@ builtin/stash.c: static int drop_stash(int argc, const char **argv, const char *prefix)\n    + \targc = parse_options(argc, argv, prefix, options,\n      \t\t\t     git_stash_drop_usage, 0);\n      \n    - \tif (get_stash_info(&info, argc, argv))\n    +-\tif (get_stash_info(&info, argc, argv))\n     -\t\treturn -1;\n    +-\n    +-\tassert_stash_ref(&info);\n    ++\tif (get_stash_info_assert(&info, argc, argv))\n     +\t\tgoto cleanup;\n      \n    - \tassert_stash_ref(&info);\n    - \n      \tret = do_drop_stash(&info, quiet);\n     +cleanup:\n      \tfree_stash_info(&info);\n    @@ builtin/stash.c: static int drop_stash(int argc, const char **argv, const char *\n      \t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n      \t\tOPT_BOOL(0, \"index\", &index,\n     @@ builtin/stash.c: static int pop_stash(int argc, const char **argv, const char *prefix)\n    + \targc = parse_options(argc, argv, prefix, options,\n      \t\t\t     git_stash_pop_usage, 0);\n      \n    - \tif (get_stash_info(&info, argc, argv))\n    +-\tif (get_stash_info(&info, argc, argv))\n     -\t\treturn -1;\n    ++\tif (get_stash_info_assert(&info, argc, argv))\n     +\t\tgoto cleanup;\n      \n    - \tassert_stash_ref(&info);\n    +-\tassert_stash_ref(&info);\n      \tif ((ret = do_apply_stash(prefix, &info, index, quiet)))\n    -@@ builtin/stash.c: static int pop_stash(int argc, const char **argv, const char *prefix)\n    + \t\tprintf_ln(_(\"The stash entry is kept in case \"\n    + \t\t\t    \"you need it again.\"));\n      \telse\n      \t\tret = do_drop_stash(&info, quiet);\n      \n    @@ builtin/stash.c: static void diff_include_untracked(const struct stash_info *inf\n      \tstruct rev_info rev;\n      \tstruct strvec stash_args = STRVEC_INIT;\n      \tstruct strvec revision_args = STRVEC_INIT;\n    +@@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *prefix)\n    + \t\t\t      UNTRACKED_ONLY, PARSE_OPT_NONEG),\n    + \t\tOPT_END()\n    + \t};\n    ++\tint do_usage = 0;\n    + \n    + \tinit_diff_ui_defaults();\n    + \tgit_config(git_diff_ui_config, NULL);\n     @@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *prefix)\n      \t\t\tstrvec_push(&revision_args, argv[i]);\n      \t}\n    @@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *\n      \t\t}\n      \t}\n      \n    + \targc = setup_revisions(revision_args.nr, revision_args.v, &rev, NULL);\n    +-\tif (argc > 1) {\n    +-\t\tfree_stash_info(&info);\n    +-\t\tusage_with_options(git_stash_show_usage, options);\n    +-\t}\n    ++\tif (argc > 1)\n    ++\t\tgoto usage;\n    + \tif (!rev.diffopt.output_format) {\n    + \t\trev.diffopt.output_format = DIFF_FORMAT_PATCH;\n    + \t\tdiff_setup_done(&rev.diffopt);\n     @@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *prefix)\n      \t}\n      \tlog_tree_diff_flush(&rev);\n      \n    -+\tret = diff_result_code(&rev.diffopt, 0);;\n    ++\tret = diff_result_code(&rev.diffopt, 0);\n     +cleanup:\n     +\tstrvec_clear(&stash_args);\n      \tfree_stash_info(&info);\n     -\treturn diff_result_code(&rev.diffopt, 0);\n    ++\tif (do_usage)\n    ++\t\tusage_with_options(git_stash_show_usage, options);\n     +\treturn ret;\n    ++usage:\n    ++\tdo_usage = 1;\n    ++\tgoto cleanup;\n      }\n      \n      static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,\n    @@ builtin/stash.c: static int do_push_stash(const struct pathspec *ps, const char\n      \tstruct strbuf patch = STRBUF_INIT;\n      \tstruct strbuf stash_msg_buf = STRBUF_INIT;\n      \tstruct strbuf untracked_files = STRBUF_INIT;\n    +@@ builtin/stash.c: static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q\n    + \t}\n    + \n    + done:\n    ++\tfree_stash_info(&info);\n    + \tstrbuf_release(&stash_msg_buf);\n    + \treturn ret;\n    + }\n11:  c9898a5a96b ! 11:  4210232e3a1 revisions API users: add \"goto cleanup\" for release_revisions()\n    @@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *\n      \tstrvec_clear(&stash_args);\n      \tfree_stash_info(&info);\n     +\trelease_revisions(&rev);\n    + \tif (do_usage)\n    + \t\tusage_with_options(git_stash_show_usage, options);\n      \treturn ret;\n    - }\n    - \n     \n      ## builtin/submodule--helper.c ##\n     @@ builtin/submodule--helper.c: static int compute_summary_module_list(struct object_id *head_oid,\n12:  6e41d666223 = 12:  1918ca0a85d revisions API users: use release_revisions() in http-push.c\n13:  02ca92660af = 13:  6a02b7aae4e revisions API users: use release_revisions() in builtin/log.c\n14:  1aeb1127f34 = 14:  ddc7402b054 revisions API users: use release_revisions() with UNLEAK()\n15:  c12b3d5a035 ! 15:  03e9bd73d4b revisions API users: use release_revisions() for \"prune_data\" users\n    @@ builtin/add.c: int add_files_to_cache(const char *prefix,\n      }\n      \n     \n    - ## builtin/stash.c ##\n    -@@ builtin/stash.c: static int check_changes_tracked_files(const struct pathspec *ps)\n    - \n    - done:\n    - \tclear_pathspec(&rev.prune_data);\n    -+\trelease_revisions(&rev);\n    - \treturn ret;\n    - }\n    - \n    -\n      ## diff-lib.c ##\n     @@ diff-lib.c: int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)\n      \tif (diff_cache(&revs, tree_oid, NULL, 1))\n16:  2e351702677 <  -:  ----------- revisions API: have release_revisions() release \"commits\"\n -:  ----------- > 16:  285673e40bf revisions API: have release_revisions() release \"commits\"\n17:  f67f1f5a575 ! 17:  ca659940ee5 revisions API: have release_revisions() release \"mailmap\"\n    @@ builtin/commit.c: static const char *find_author_by_nickname(const char *name)\n      \t}\n     \n      ## revision.c ##\n    -@@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)\n    - \t}\n    +@@ revision.c: int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n    + \treturn left;\n      }\n      \n     +static void release_revisions_mailmap(struct string_list *mailmap)\n    @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)\n     +\n      void release_revisions(struct rev_info *revs)\n      {\n    - \trelease_revisions_commit_list(revs);\n    + \tfree_commit_list(revs->commits);\n      \tobject_array_clear(&revs->pending);\n     +\trelease_revisions_mailmap(revs->mailmap);\n      }\n18:  1df61550e5a ! 18:  7987c748ba4 revisions API: have release_revisions() release \"cmdline\"\n    @@ Commit message\n         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n     \n      ## revision.c ##\n    -@@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)\n    - \t}\n    +@@ revision.c: int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n    + \treturn left;\n      }\n      \n     +static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)\n    @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)\n     +\t\treturn;\n     +\tfor (i = 0; i < cmdline->nr; i++)\n     +\t\tfree((char *)cmdline->rev[i].name);\n    -+\tFREE_AND_NULL(cmdline->rev);\n    ++\tfree(cmdline->rev);\n     +}\n     +\n      static void release_revisions_mailmap(struct string_list *mailmap)\n    @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)\n      \tif (!mailmap)\n     @@ revision.c: void release_revisions(struct rev_info *revs)\n      {\n    - \trelease_revisions_commit_list(revs);\n    + \tfree_commit_list(revs->commits);\n      \tobject_array_clear(&revs->pending);\n     +\trelease_revisions_cmdline(&revs->cmdline);\n      \trelease_revisions_mailmap(revs->mailmap);\n19:  065c3cc91b2 ! 19:  e6e87ce3608 revisions API: have release_revisions() release \"filter\"\n    @@ Commit message\n     \n      ## revision.c ##\n     @@ revision.c: void release_revisions(struct rev_info *revs)\n    - \trelease_revisions_commit_list(revs);\n    + \tfree_commit_list(revs->commits);\n      \tobject_array_clear(&revs->pending);\n      \trelease_revisions_cmdline(&revs->cmdline);\n     +\tlist_objects_filter_release(&revs->filter);\n20:  806fef06b87 = 20:  ebdcdc88d79 revisions API: have release_revisions() release \"grep_filter\"\n21:  fabbe452a14 = 21:  84c8714571a revisions API: have release_revisions() release \"prune_data\"\n22:  cb201fa1914 ! 22:  d96642d3502 revisions API: clear \"boundary_commits\" in release_revisions()\n    @@ Commit message\n      ## revision.c ##\n     @@ revision.c: void release_revisions(struct rev_info *revs)\n      {\n    - \trelease_revisions_commit_list(revs);\n    + \tfree_commit_list(revs->commits);\n      \tobject_array_clear(&revs->pending);\n     +\tobject_array_clear(&revs->boundary_commits);\n      \trelease_revisions_cmdline(&revs->cmdline);\n23:  0363a69073c = 23:  0e9745d9ee1 revisions API: release \"reflog_info\" in release revisions()\n24:  bad359b2983 = 24:  c7f3e92a8d1 revisions API: call diff_free(&revs->pruning) in revisions_release()\n25:  9a51c1ae1d1 = 25:  d428d752462 revisions API: have release_revisions() release \"date_mode\"\n26:  b56db37940d ! 26:  fbf233ec768 revisions API: have release_revisions() release \"topo_walk_info\"\n    @@ revision.c: static void release_revisions_mailmap(struct string_list *mailmap)\n     +\n      void release_revisions(struct rev_info *revs)\n      {\n    - \trelease_revisions_commit_list(revs);\n    + \tfree_commit_list(revs->commits);\n     @@ revision.c: void release_revisions(struct rev_info *revs)\n      \tfree_grep_patterns(&revs->grep_filter);\n      \tdiff_free(&revs->pruning);\n27:  b02cb8fcded = 27:  ec2c77ce869 revisions API: add a TODO for diff_free(&revs->diffopt)\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452930","messageId":"patch-v5-03.27-364aa7d8990-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 03/27] string_list API users: use string_list_init_{no,}dup","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:17Z","receivedAt":"2022-04-02T10:49:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Follow-up on the introduction of string_list_init_nodup() and\nstring_list_init_dup() in the series merged in bd4232fac33 (Merge\nbranch 'ab/struct-init', 2021-07-16) and convert code that implicitly\nrelied on xcalloc() being equivalent to the initializer to use\nxmalloc() and string_list_init_{no,}dup() instead.\n\nIn the case of get_unmerged() in merge-recursive.c we used the\ncombination of xcalloc() and assigning \"1\" to \"strdup_strings\" to get\nwhat we'd get via string_list_init_dup(), let's use that instead.\n\nAdjacent code in cmd_format_patch() will be changed in a subsequent\ncommit, since we're changing that let's change the other in-tree\npatterns that do the same. Let's also convert a \"x == NULL\" to \"!x\"\nper our CodingGuidelines, as we need to change the \"if\" line anyway.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/log.c      | 9 ++++++---\n builtin/shortlog.c | 6 ++++--\n merge-recursive.c  | 4 ++--\n 3 files changed, 12 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex c211d66d1d0..634dc782cce 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -231,7 +231,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,\n \t}\n \n \tif (mailmap) {\n-\t\trev->mailmap = xcalloc(1, sizeof(struct string_list));\n+\t\trev->mailmap = xmalloc(sizeof(struct string_list));\n+\t\tstring_list_init_nodup(rev->mailmap);\n \t\tread_mailmap(rev->mailmap);\n \t}\n \n@@ -2173,8 +2174,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tprepare_bases(&bases, base, list, nr);\n \t}\n \n-\tif (in_reply_to || thread || cover_letter)\n-\t\trev.ref_message_ids = xcalloc(1, sizeof(struct string_list));\n+\tif (in_reply_to || thread || cover_letter) {\n+\t\trev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));\n+\t\tstring_list_init_nodup(rev.ref_message_ids);\n+\t}\n \tif (in_reply_to) {\n \t\tconst char *msgid = clean_message_id(in_reply_to);\n \t\tstring_list_append(rev.ref_message_ids, msgid);\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex 26c5c0cf935..fcde07c9367 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -81,8 +81,10 @@ static void insert_one_record(struct shortlog *log,\n \t\tformat_subject(&subject, oneline, \" \");\n \t\tbuffer = strbuf_detach(&subject, NULL);\n \n-\t\tif (item->util == NULL)\n-\t\t\titem->util = xcalloc(1, sizeof(struct string_list));\n+\t\tif (!item->util) {\n+\t\t\titem->util = xmalloc(sizeof(struct string_list));\n+\t\t\tstring_list_init_nodup(item->util);\n+\t\t}\n \t\tstring_list_append(item->util, buffer);\n \t}\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 1ee6364e8b1..32bbba5fbb1 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -522,10 +522,10 @@ static struct stage_data *insert_stage_data(struct repository *r,\n  */\n static struct string_list *get_unmerged(struct index_state *istate)\n {\n-\tstruct string_list *unmerged = xcalloc(1, sizeof(struct string_list));\n+\tstruct string_list *unmerged = xmalloc(sizeof(struct string_list));\n \tint i;\n \n-\tunmerged->strdup_strings = 1;\n+\tstring_list_init_dup(unmerged);\n \n \t/* TODO: audit for interaction with sparse-index. */\n \tensure_full_index(istate);\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452931","messageId":"patch-v5-04.27-5d4ad92d47b-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 04/27] format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:18Z","receivedAt":"2022-04-02T10:49:58Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix two memory leaks in \"struct rev_info\" by freeing that memory in\ncmd_format_patch(). These two are unusual special-cases in being in\nthe \"struct rev_info\", but not being \"owned\" by the code in\nrevision.c. I.e. they're members of the struct so that this code in\n\"builtin/log.c\" can conveniently pass information code in\n\"log-tree.c\".\n\nSee e.g. the make_cover_letter() caller of log_write_email_headers()\nhere in \"builtin/log.c\", and [1] for a demonstration of where the\n\"extra_headers\" and \"ref_message_ids\" struct members are used.\n\nSee 20ff06805c6 (format-patch: resurrect extra headers from config,\n2006-06-02) and d1566f7883f (git-format-patch: Make the second and\nsubsequent mails replies to the first, 2006-07-14) for the initial\nintroduction of \"extra_headers\" and \"ref_message_ids\".\n\nWe can count on repo_init_revisions() memset()-ing this data to 0\nhowever, so we can count on it being either NULL or something we\nallocated. In the case of \"extra_headers\" let's add a local \"char *\"\nvariable to hold it, to avoid the eventual cast from \"const char *\"\nwhen we free() it.\n\n1. https://lore.kernel.org/git/220401.868rsoogxf.gmgdl@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/log.c | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 634dc782cce..6f9928fabfe 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1747,6 +1747,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tstruct commit *commit;\n \tstruct commit **list = NULL;\n \tstruct rev_info rev;\n+\tchar *to_free = NULL;\n \tstruct setup_revision_opt s_r_opt;\n \tint nr = 0, total, i;\n \tint use_stdout = 0;\n@@ -1947,7 +1948,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tstrbuf_addch(&buf, '\\n');\n \t}\n \n-\trev.extra_headers = strbuf_detach(&buf, NULL);\n+\trev.extra_headers = to_free = strbuf_detach(&buf, NULL);\n \n \tif (from) {\n \t\tif (split_ident_line(&rev.from_ident, from, strlen(from)))\n@@ -2284,6 +2285,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tstrbuf_release(&rdiff1);\n \tstrbuf_release(&rdiff2);\n \tstrbuf_release(&rdiff_title);\n+\tfree(to_free);\n+\tif (rev.ref_message_ids)\n+\t\tstring_list_clear(rev.ref_message_ids, 0);\n+\tfree(rev.ref_message_ids);\n \tUNLEAK(rev);\n \treturn 0;\n }\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452932","messageId":"patch-v5-05.27-c38db28f920-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 05/27] cocci: add and apply free_commit_list() rules","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:19Z","receivedAt":"2022-04-02T10:50:00Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add and apply coccinelle rules to remove \"if (E)\" before\n\"free_commit_list(E)\", the function can accept NULL, and further\nchange cases where \"E = NULL\" followed to also be unconditionally.\n\nThe code changes in this commit were entirely made by the coccinelle\nrule being added here, and applied with:\n\n    make contrib/coccinelle/free.cocci.patch\n    patch -p1 <contrib/coccinelle/free.cocci.patch\n\nThe only manual intervention here is that the the relevant code in\ncommit.c has been manually re-indented.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/rev-list.c            |  6 ++----\n commit.c                      | 19 ++++++++-----------\n contrib/coccinelle/free.cocci | 27 +++++++++++++++++++++++++++\n revision.c                    | 17 ++++++-----------\n submodule.c                   |  6 ++----\n 5 files changed, 45 insertions(+), 30 deletions(-)\n\ndiff --git a/builtin/rev-list.c b/builtin/rev-list.c\nindex 572da1472e5..07c0ad11d8d 100644\n--- a/builtin/rev-list.c\n+++ b/builtin/rev-list.c\n@@ -213,10 +213,8 @@ static void show_commit(struct commit *commit, void *data)\n \n static void finish_commit(struct commit *commit)\n {\n-\tif (commit->parents) {\n-\t\tfree_commit_list(commit->parents);\n-\t\tcommit->parents = NULL;\n-\t}\n+\tfree_commit_list(commit->parents);\n+\tcommit->parents = NULL;\n \tfree_commit_buffer(the_repository->parsed_objects,\n \t\t\t   commit);\n }\ndiff --git a/commit.c b/commit.c\nindex 59b6c3e4552..9628716bf2f 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -407,17 +407,14 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b\n \n \tif (item->object.parsed)\n \t\treturn 0;\n-\n-\tif (item->parents) {\n-\t\t/*\n-\t\t * Presumably this is leftover from an earlier failed parse;\n-\t\t * clear it out in preparation for us re-parsing (we'll hit the\n-\t\t * same error, but that's good, since it lets our caller know\n-\t\t * the result cannot be trusted.\n-\t\t */\n-\t\tfree_commit_list(item->parents);\n-\t\titem->parents = NULL;\n-\t}\n+\t/*\n+\t * Presumably this is leftover from an earlier failed parse;\n+\t * clear it out in preparation for us re-parsing (we'll hit the\n+\t * same error, but that's good, since it lets our caller know\n+\t * the result cannot be trusted.\n+\t */\n+\tfree_commit_list(item->parents);\n+\titem->parents = NULL;\n \n \ttail += size;\n \tif (tail <= bufptr + tree_entry_len + 1 || memcmp(bufptr, \"tree \", 5) ||\ndiff --git a/contrib/coccinelle/free.cocci b/contrib/coccinelle/free.cocci\nindex 4490069df96..6fb9eb6e883 100644\n--- a/contrib/coccinelle/free.cocci\n+++ b/contrib/coccinelle/free.cocci\n@@ -2,13 +2,21 @@\n expression E;\n @@\n - if (E)\n+(\n   free(E);\n+|\n+  free_commit_list(E);\n+)\n \n @@\n expression E;\n @@\n - if (!E)\n+(\n   free(E);\n+|\n+  free_commit_list(E);\n+)\n \n @@\n expression E;\n@@ -16,3 +24,22 @@ expression E;\n - free(E);\n + FREE_AND_NULL(E);\n - E = NULL;\n+\n+@@\n+expression E;\n+@@\n+- if (E)\n+- {\n+  free_commit_list(E);\n+  E = NULL;\n+- }\n+\n+@@\n+expression E;\n+statement S;\n+@@\n+- if (E) {\n++ if (E)\n+  S\n+  free_commit_list(E);\n+- }\ndiff --git a/revision.c b/revision.c\nindex 7d435f80480..4963ba7def8 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1456,10 +1456,9 @@ static int limit_list(struct rev_info *revs)\n \tif (revs->left_only || revs->right_only)\n \t\tlimit_left_right(newlist, revs);\n \n-\tif (bottom) {\n+\tif (bottom)\n \t\tlimit_to_ancestry(bottom, newlist);\n-\t\tfree_commit_list(bottom);\n-\t}\n+\tfree_commit_list(bottom);\n \n \t/*\n \t * Check if any commits have become TREESAME by some of their parents\n@@ -4080,10 +4079,8 @@ static void create_boundary_commit_list(struct rev_info *revs)\n \t * boundary commits anyway.  (This is what the code has always\n \t * done.)\n \t */\n-\tif (revs->commits) {\n-\t\tfree_commit_list(revs->commits);\n-\t\trevs->commits = NULL;\n-\t}\n+\tfree_commit_list(revs->commits);\n+\trevs->commits = NULL;\n \n \t/*\n \t * Put all of the actual boundary commits from revs->boundary_commits\n@@ -4220,10 +4217,8 @@ struct commit *get_revision(struct rev_info *revs)\n \t\tgraph_update(revs->graph, c);\n \tif (!c) {\n \t\tfree_saved_parents(revs);\n-\t\tif (revs->previous_parents) {\n-\t\t\tfree_commit_list(revs->previous_parents);\n-\t\t\trevs->previous_parents = NULL;\n-\t\t}\n+\t\tfree_commit_list(revs->previous_parents);\n+\t\trevs->previous_parents = NULL;\n \t}\n \treturn c;\n }\ndiff --git a/submodule.c b/submodule.c\nindex 86c8f0f89db..9b715b4a5cb 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -645,8 +645,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,\n \tprint_submodule_diff_summary(sub, &rev, o);\n \n out:\n-\tif (merge_bases)\n-\t\tfree_commit_list(merge_bases);\n+\tfree_commit_list(merge_bases);\n \tclear_commit_marks(left, ~0);\n \tclear_commit_marks(right, ~0);\n \tif (sub) {\n@@ -735,8 +734,7 @@ void show_submodule_inline_diff(struct diff_options *o, const char *path,\n \n done:\n \tstrbuf_release(&sb);\n-\tif (merge_bases)\n-\t\tfree_commit_list(merge_bases);\n+\tfree_commit_list(merge_bases);\n \tif (left)\n \t\tclear_commit_marks(left, ~0);\n \tif (right)\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452933","messageId":"patch-v5-06.27-b99f96599bb-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 06/27] revision.[ch]: provide and start using a release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:20Z","receivedAt":"2022-04-02T10:50:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The users of the revision.[ch] API's \"struct rev_info\" are a major\nsource of memory leaks in the test suite under SANITIZE=leak, which in\nturn adds a lot of noise when trying to mark up tests with\n\"TEST_PASSES_SANITIZE_LEAK=true\".\n\nThe users of that API are largely one-shot, e.g. \"git rev-list\" or\n\"git log\", or the \"git checkout\" and \"git stash\" being modified here\n\nFor these callers freeing the memory is arguably a waste of time, but\nin many cases they've actually been trying to free the memory, and\njust doing that in a buggy manner.\n\nLet's provide a release_revisions() function for these users, and\nstart migrating them over per the plan outlined in [1]. Right now this\nonly handles the \"pending\" member of the struct, but more will be\nadded in subsequent commits.\n\nEven though we only clear the \"pending\" member now, let's not leave a\ntrap in code like the pre-image of index_differs_from(), where we'd\nstart doing the wrong thing as soon as the release_revisions() learned\nto clear its \"diffopt\". I.e. we need to call release_revisions() after\nwe've inspected any state in \"struct rev_info\".\n\nThis leaves in place e.g. clear_pathspec(&rev.prune_data) in\nstash_working_tree() in builtin/stash.c, subsequent commits will teach\nrelease_revisions() to free \"prune_data\" and other members that in\nsome cases are individually cleared by users of \"struct rev_info\" by\nreaching into its members. Those subsequent commits will remove the\nrelevant calls to e.g. clear_pathspec().\n\nWe avoid amending code in index_differs_from() in diff-lib.c as well\nas wt_status_collect_changes_index(), has_unstaged_changes() and\nhas_uncommitted_changes() in wt-status.c in a way that assumes that we\nare already clearing the \"diffopt\" member. That will be handled in a\nsubsequent commit.\n\n1. https://lore.kernel.org/git/87a6k8daeu.fsf@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/checkout.c | 2 +-\n builtin/stash.c    | 5 ++---\n diff-lib.c         | 2 +-\n range-diff.c       | 2 +-\n revision.c         | 5 +++++\n revision.h         | 6 ++++++\n wt-status.c        | 5 +++--\n 7 files changed, 19 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 797681481d1..4d9e0bd3ac1 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -629,7 +629,7 @@ static void show_local_changes(struct object *head,\n \tdiff_setup_done(&rev.diffopt);\n \tadd_pending_object(&rev, head, NULL);\n \trun_diff_index(&rev, 0);\n-\tobject_array_clear(&rev.pending);\n+\trelease_revisions(&rev);\n }\n \n static void describe_detached_head(const char *msg, struct commit *commit)\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 0c7b6a95882..74fa810cf8c 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1047,7 +1047,6 @@ static int check_changes_tracked_files(const struct pathspec *ps)\n \t\tgoto done;\n \t}\n \n-\tobject_array_clear(&rev.pending);\n \tresult = run_diff_files(&rev, 0);\n \tif (diff_result_code(&rev.diffopt, result)) {\n \t\tret = 1;\n@@ -1056,6 +1055,7 @@ static int check_changes_tracked_files(const struct pathspec *ps)\n \n done:\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n \treturn ret;\n }\n \n@@ -1266,9 +1266,8 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps\n \n done:\n \tdiscard_index(&istate);\n-\tUNLEAK(rev);\n-\tobject_array_clear(&rev.pending);\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n \tstrbuf_release(&diff_output);\n \tremove_path(stash_index_path.buf);\n \treturn ret;\ndiff --git a/diff-lib.c b/diff-lib.c\nindex ca085a03efc..d6800274bd5 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -662,7 +662,7 @@ int index_differs_from(struct repository *r,\n \t\tdiff_flags_or(&rev.diffopt.flags, flags);\n \trev.diffopt.ita_invisible_in_index = ita_invisible_in_index;\n \trun_diff_index(&rev, 1);\n-\tobject_array_clear(&rev.pending);\n+\trelease_revisions(&rev);\n \treturn (rev.diffopt.flags.has_changes != 0);\n }\n \ndiff --git a/range-diff.c b/range-diff.c\nindex b72eb9fdbee..39cc010c628 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -596,6 +596,6 @@ int is_range_diff_range(const char *arg)\n \t}\n \n \tfree(copy);\n-\tobject_array_clear(&revs.pending);\n+\trelease_revisions(&revs);\n \treturn negative > 0 && positive > 0;\n }\ndiff --git a/revision.c b/revision.c\nindex 4963ba7def8..5dd4b2e910c 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2922,6 +2922,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \treturn left;\n }\n \n+void release_revisions(struct rev_info *revs)\n+{\n+\tobject_array_clear(&revs->pending);\n+}\n+\n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n {\n \tstruct commit_list *l = xcalloc(1, sizeof(*l));\ndiff --git a/revision.h b/revision.h\nindex 5bc59c7bfe1..61c780fc4cd 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -377,6 +377,12 @@ void repo_init_revisions(struct repository *r,\n int setup_revisions(int argc, const char **argv, struct rev_info *revs,\n \t\t    struct setup_revision_opt *);\n \n+/**\n+ * Free data allocated in a \"struct rev_info\" after it's been\n+ * initialized with repo_init_revisions().\n+ */\n+void release_revisions(struct rev_info *revs);\n+\n void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,\n \t\t\tconst struct option *options,\n \t\t\tconst char * const usagestr[]);\ndiff --git a/wt-status.c b/wt-status.c\nindex d33f9272b72..922cf787f95 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -662,7 +662,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)\n \n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_index(&rev, 1);\n-\tobject_array_clear(&rev.pending);\n+\trelease_revisions(&rev);\n \tclear_pathspec(&rev.prune_data);\n }\n \n@@ -2545,6 +2545,7 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)\n \trev_info.diffopt.flags.quick = 1;\n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_files(&rev_info, 0);\n+\trelease_revisions(&rev_info);\n \treturn diff_result_code(&rev_info.diffopt, result);\n }\n \n@@ -2577,7 +2578,7 @@ int has_uncommitted_changes(struct repository *r,\n \n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_index(&rev_info, 1);\n-\tobject_array_clear(&rev_info.pending);\n+\trelease_revisions(&rev_info);\n \treturn diff_result_code(&rev_info.diffopt, result);\n }\n \n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452934","messageId":"patch-v5-07.27-521f6967b64-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 07/27] revisions API users: add straightforward release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:21Z","receivedAt":"2022-04-02T10:50:06Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a release_revisions() to various users of \"struct rev_list\" in\nthose straightforward cases where we only need to add the\nrelease_revisions() call to the end of a block, and don't need to\ne.g. refactor anything to use a \"goto cleanup\" pattern.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n add-interactive.c                | 1 +\n bisect.c                         | 2 ++\n builtin/add.c                    | 1 +\n builtin/am.c                     | 3 +++\n builtin/bisect--helper.c         | 2 ++\n builtin/blame.c                  | 1 +\n builtin/checkout.c               | 1 +\n builtin/commit.c                 | 1 +\n builtin/describe.c               | 2 ++\n builtin/fast-export.c            | 1 +\n builtin/merge.c                  | 2 ++\n builtin/pack-objects.c           | 2 ++\n builtin/prune.c                  | 1 +\n builtin/reflog.c                 | 1 +\n builtin/shortlog.c               | 2 ++\n builtin/submodule--helper.c      | 1 +\n fmt-merge-msg.c                  | 1 +\n merge-ort.c                      | 1 +\n merge-recursive.c                | 1 +\n midx.c                           | 1 +\n pack-bitmap-write.c              | 1 +\n ref-filter.c                     | 1 +\n remote.c                         | 1 +\n sequencer.c                      | 3 +++\n shallow.c                        | 1 +\n submodule.c                      | 2 ++\n t/helper/test-revision-walking.c | 1 +\n wt-status.c                      | 1 +\n 28 files changed, 39 insertions(+)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex 72472103017..54cdfc82017 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -570,6 +570,7 @@ static int get_modified_files(struct repository *r,\n \n \t\tif (ps)\n \t\t\tclear_pathspec(&rev.prune_data);\n+\t\trelease_revisions(&rev);\n \t}\n \thashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);\n \tif (unmerged_count)\ndiff --git a/bisect.c b/bisect.c\nindex 9e6a2b7f201..cc6b8b6230d 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -884,6 +884,7 @@ static int check_ancestors(struct repository *r, int rev_nr,\n \t/* Clean up objects used, as they will be reused. */\n \tclear_commit_marks_many(rev_nr, rev, ALL_REV_FLAGS);\n \n+\trelease_revisions(&revs);\n \treturn res;\n }\n \n@@ -964,6 +965,7 @@ static void show_diff_tree(struct repository *r,\n \n \tsetup_revisions(ARRAY_SIZE(argv) - 1, argv, &opt, NULL);\n \tlog_tree_commit(&opt, commit);\n+\trelease_revisions(&opt);\n }\n \n /*\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 3ffb86a4338..f507d2191cd 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -340,6 +340,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)\n \n \tunlink(file);\n \tfree(file);\n+\trelease_revisions(&rev);\n \treturn 0;\n }\n \ndiff --git a/builtin/am.c b/builtin/am.c\nindex 0f4111bafa0..93bec62afa9 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -1397,6 +1397,7 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm\n \tadd_pending_object(&rev_info, &commit->object, \"\");\n \tdiff_setup_done(&rev_info.diffopt);\n \tlog_tree_commit(&rev_info, commit);\n+\trelease_revisions(&rev_info);\n }\n \n /**\n@@ -1429,6 +1430,7 @@ static void write_index_patch(const struct am_state *state)\n \tadd_pending_object(&rev_info, &tree->object, \"\");\n \tdiff_setup_done(&rev_info.diffopt);\n \trun_diff_index(&rev_info, 1);\n+\trelease_revisions(&rev_info);\n }\n \n /**\n@@ -1582,6 +1584,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \t\tadd_pending_oid(&rev_info, \"HEAD\", &our_tree, 0);\n \t\tdiff_setup_done(&rev_info.diffopt);\n \t\trun_diff_index(&rev_info, 1);\n+\t\trelease_revisions(&rev_info);\n \t}\n \n \tif (run_apply(state, index_path))\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex 8b2b259ff0d..e4d7b6779ae 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -555,6 +555,7 @@ static int bisect_skipped_commits(struct bisect_terms *terms)\n \treset_revision_walk();\n \n \tstrbuf_release(&commit_name);\n+\trelease_revisions(&revs);\n \tfclose(fp);\n \treturn 0;\n }\n@@ -1041,6 +1042,7 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar\n \t\t\t\t\t\toid_to_hex(&commit->object.oid));\n \n \t\t\treset_revision_walk();\n+\t\t\trelease_revisions(&revs);\n \t\t} else {\n \t\t\tstrvec_push(&argv_state, argv[i]);\n \t\t}\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 885b381ab83..24bac822c56 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1203,5 +1203,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \n cleanup:\n \tcleanup_scoreboard(&sb);\n+\trelease_revisions(&revs);\n \treturn 0;\n }\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 4d9e0bd3ac1..7ad4a7113c9 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1082,6 +1082,7 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne\n \n \t/* Clean up objects used, as they will be reused. */\n \trepo_clear_commit_marks(the_repository, ALL_REV_FLAGS);\n+\trelease_revisions(&revs);\n }\n \n static int switch_branches(const struct checkout_opts *opts,\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 009a1de0a3d..c7eda9bbb72 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1123,6 +1123,7 @@ static const char *find_author_by_nickname(const char *name)\n \t\tstrbuf_release(&buf);\n \t\tformat_commit_message(commit, \"%aN <%aE>\", &buf, &ctx);\n \t\tclear_mailmap(&mailmap);\n+\t\trelease_revisions(&revs);\n \t\treturn strbuf_detach(&buf, NULL);\n \t}\n \tdie(_(\"--author '%s' is not 'Name <email>' and matches no existing author\"), name);\ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex 42159cd26bd..a76f1a1a7a7 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -517,6 +517,7 @@ static void describe_blob(struct object_id oid, struct strbuf *dst)\n \n \ttraverse_commit_list(&revs, process_commit, process_object, &pcd);\n \treset_revision_walk();\n+\trelease_revisions(&revs);\n }\n \n static void describe(const char *arg, int last_one)\n@@ -667,6 +668,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)\n \t\t\t\tsuffix = NULL;\n \t\t\telse\n \t\t\t\tsuffix = dirty;\n+\t\t\trelease_revisions(&revs);\n \t\t}\n \t\tdescribe(\"HEAD\", 1);\n \t} else if (dirty) {\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex a7d72697fba..f34ae451ee3 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -1275,6 +1275,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)\n \t\tprintf(\"done\\n\");\n \n \trefspec_clear(&refspecs);\n+\trelease_revisions(&revs);\n \n \treturn 0;\n }\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex f178f5a3ee1..d9784d4891c 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -443,6 +443,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead\n \t}\n \twrite_file_buf(git_path_squash_msg(the_repository), out.buf, out.len);\n \tstrbuf_release(&out);\n+\trelease_revisions(&rev);\n }\n \n static void finish(struct commit *head_commit,\n@@ -998,6 +999,7 @@ static int evaluate_result(void)\n \t */\n \tcnt += count_unmerged_entries();\n \n+\trelease_revisions(&rev);\n \treturn cnt;\n }\n \ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 014dcd4bc98..180fff03569 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -4177,11 +4177,13 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)\n \t\tread_object_list_from_stdin();\n \t} else if (pfd.have_revs) {\n \t\tget_object_list(&pfd.revs, rp.nr, rp.v);\n+\t\trelease_revisions(&pfd.revs);\n \t} else {\n \t\tstruct rev_info revs;\n \n \t\trepo_init_revisions(the_repository, &revs, NULL);\n \t\tget_object_list(&revs, rp.nr, rp.v);\n+\t\trelease_revisions(&revs);\n \t}\n \tcleanup_preferred_base();\n \tif (include_tag && nr_result)\ndiff --git a/builtin/prune.c b/builtin/prune.c\nindex c2bcdc07db4..df376b2ed1e 100644\n--- a/builtin/prune.c\n+++ b/builtin/prune.c\n@@ -196,5 +196,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix)\n \t\tprune_shallow(show_only ? PRUNE_SHOW_ONLY : 0);\n \t}\n \n+\trelease_revisions(&revs);\n \treturn 0;\n }\ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex 9407f835cb6..592d5d33442 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -248,6 +248,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)\n \t\tif (verbose)\n \t\t\tprintf(_(\"Marking reachable objects...\"));\n \t\tmark_reachable_objects(&revs, 0, 0, NULL);\n+\t\trelease_revisions(&revs);\n \t\tif (verbose)\n \t\t\tputchar('\\n');\n \t}\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex fcde07c9367..35825f075e3 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -422,6 +422,8 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)\n \telse\n \t\tget_from_rev(&rev, &log);\n \n+\trelease_revisions(&rev);\n+\n \tshortlog_output(&log);\n \tif (log.file != stdout)\n \t\tfclose(log.file);\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 5301612d24b..24980863f68 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1269,6 +1269,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \t\trun_diff_files(&rev, 0);\n \tprepare_submodule_summary(info, &list);\n \tstrvec_clear(&diff_args);\n+\trelease_revisions(&rev);\n \treturn 0;\n }\n \ndiff --git a/fmt-merge-msg.c b/fmt-merge-msg.c\nindex baca57d5b64..f48f44f9cd1 100644\n--- a/fmt-merge-msg.c\n+++ b/fmt-merge-msg.c\n@@ -699,6 +699,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,\n \t\t\tshortlog(origins.items[i].string,\n \t\t\t\t origins.items[i].util,\n \t\t\t\t head, &rev, opts, out);\n+\t\trelease_revisions(&rev);\n \t}\n \n \tstrbuf_complete_line(out);\ndiff --git a/merge-ort.c b/merge-ort.c\nindex 8545354dafd..4c0d4eba19b 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -1594,6 +1594,7 @@ static int find_first_merges(struct repository *repo,\n \t}\n \n \tobject_array_clear(&merges);\n+\trelease_revisions(&revs);\n \treturn result->nr;\n }\n \ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 32bbba5fbb1..acd13b2b069 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1160,6 +1160,7 @@ static int find_first_merges(struct repository *repo,\n \t}\n \n \tobject_array_clear(&merges);\n+\trelease_revisions(&revs);\n \treturn result->nr;\n }\n \ndiff --git a/midx.c b/midx.c\nindex 107365d2114..d5724df7d60 100644\n--- a/midx.c\n+++ b/midx.c\n@@ -1061,6 +1061,7 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr\n \tif (indexed_commits_nr_p)\n \t\t*indexed_commits_nr_p = cb.commits_nr;\n \n+\trelease_revisions(&revs);\n \treturn cb.commits;\n }\n \ndiff --git a/pack-bitmap-write.c b/pack-bitmap-write.c\nindex cf681547f2e..c43375bd344 100644\n--- a/pack-bitmap-write.c\n+++ b/pack-bitmap-write.c\n@@ -326,6 +326,7 @@ static void bitmap_builder_init(struct bitmap_builder *bb,\n \ttrace2_data_intmax(\"pack-bitmap-write\", the_repository,\n \t\t\t   \"num_maximal_commits\", num_maximal);\n \n+\trelease_revisions(&revs);\n \tfree_commit_list(reusable);\n }\n \ndiff --git a/ref-filter.c b/ref-filter.c\nindex 7838bd22b8d..a91688bbf17 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -2392,6 +2392,7 @@ static void reach_filter(struct ref_array *array,\n \t\tclear_commit_marks(merge_commit, ALL_REV_FLAGS);\n \t}\n \n+\trelease_revisions(&revs);\n \tfree(to_clear);\n }\n \ndiff --git a/remote.c b/remote.c\nindex 42a4e7106e1..fa3152a5d52 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -2172,6 +2172,7 @@ static int stat_branch_pair(const char *branch_name, const char *base,\n \tclear_commit_marks(theirs, ALL_REV_FLAGS);\n \n \tstrvec_clear(&argv);\n+\trelease_revisions(&revs);\n \treturn 1;\n }\n \ndiff --git a/sequencer.c b/sequencer.c\nindex a1bb39383db..f9d7acd1065 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1347,6 +1347,7 @@ void print_commit_summary(struct repository *r,\n \t\tlog_tree_commit(&rev, commit);\n \t}\n \n+\trelease_revisions(&rev);\n \tstrbuf_release(&format);\n }\n \n@@ -3415,6 +3416,7 @@ static int make_patch(struct repository *r,\n \t\tunuse_commit_buffer(commit, commit_buffer);\n \t}\n \tstrbuf_release(&buf);\n+\trelease_revisions(&log_tree_opt);\n \n \treturn res;\n }\n@@ -4525,6 +4527,7 @@ static int pick_commits(struct repository *r,\n \t\t\t\t\t      &log_tree_opt.diffopt);\n \t\t\t\tlog_tree_diff_flush(&log_tree_opt);\n \t\t\t}\n+\t\t\trelease_revisions(&log_tree_opt);\n \t\t}\n \t\tflush_rewritten_pending();\n \t\tif (!stat(rebase_path_rewritten_list(), &st) &&\ndiff --git a/shallow.c b/shallow.c\nindex e158be58b05..797a593633f 100644\n--- a/shallow.c\n+++ b/shallow.c\n@@ -262,6 +262,7 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,\n \t\tif ((o->flags & both_flags) == both_flags)\n \t\t\to->flags &= ~not_shallow_flag;\n \t}\n+\trelease_revisions(&revs);\n \treturn result;\n }\n \ndiff --git a/submodule.c b/submodule.c\nindex 9b715b4a5cb..7b5d7753930 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -923,9 +923,11 @@ static void collect_changed_submodules(struct repository *r,\n \t\tdiff_rev.diffopt.format_callback_data = &data;\n \t\tdiff_rev.dense_combined_merges = 1;\n \t\tdiff_tree_combined_merge(commit, &diff_rev);\n+\t\trelease_revisions(&diff_rev);\n \t}\n \n \treset_revision_walk();\n+\trelease_revisions(&rev);\n }\n \n static void free_submodules_data(struct string_list *submodules)\ndiff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c\nindex 625b2dbf822..4a45d5bac2a 100644\n--- a/t/helper/test-revision-walking.c\n+++ b/t/helper/test-revision-walking.c\n@@ -43,6 +43,7 @@ static int run_revision_walk(void)\n \t}\n \n \treset_revision_walk();\n+\trelease_revisions(&rev);\n \treturn got_revision;\n }\n \ndiff --git a/wt-status.c b/wt-status.c\nindex 922cf787f95..f9100621375 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1152,6 +1152,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)\n \t\trev.diffopt.b_prefix = \"w/\";\n \t\trun_diff_files(&rev, 0);\n \t}\n+\trelease_revisions(&rev);\n }\n \n static void wt_longstatus_print_tracking(struct wt_status *s)\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452935","messageId":"patch-v5-08.27-003b507e0b7-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 08/27] revision.[ch]: document and move code declared around \"init\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:22Z","receivedAt":"2022-04-02T10:50:07Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"A subsequent commit will add \"REV_INFO_INIT\" macro adjacent to\nrepo_init_revisions(), unfortunately between the \"struct rev_info\"\nitself and that function we've added various miscellaneous code\nbetween the two over the years.\n\nLet's move that code either lower in revision.h, giving it API docs\nwhile we're at it, or in cases where it wasn't public API at all move\nit into revision.c No lines of code are changed here, only moved\naround. The only changes are the addition of new API comments.\n\nThe \"tree_difference\" variable could also be declared like this, which\nI think would be a lot clearer, but let's leave that for now to keep\nthis a move-only change:\n\n\tstatic enum {\n\t\tREV_TREE_SAME,\n\t\tREV_TREE_NEW, /* Only new files */\n\t\tREV_TREE_OLD, /* Only files removed */\n\t\tREV_TREE_DIFFERENT, /* Mixed changes */\n\t} tree_difference = REV_TREE_SAME;\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c |  4 ++++\n revision.h | 50 ++++++++++++++++++++++++--------------------------\n 2 files changed, 28 insertions(+), 26 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex 5dd4b2e910c..472fff1e0a7 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -606,6 +606,10 @@ static struct commit *one_relevant_parent(const struct rev_info *revs,\n  *\n  *   2. We saw anything except REV_TREE_NEW.\n  */\n+#define REV_TREE_SAME\t\t0\n+#define REV_TREE_NEW\t\t1\t/* Only new files */\n+#define REV_TREE_OLD\t\t2\t/* Only files removed */\n+#define REV_TREE_DIFFERENT\t3\t/* Mixed changes */\n static int tree_difference = REV_TREE_SAME;\n \n static void file_add_remove(struct diff_options *options,\ndiff --git a/revision.h b/revision.h\nindex 61c780fc4cd..b9070e43428 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -329,32 +329,6 @@ struct rev_info {\n \tstruct tmp_objdir *remerge_objdir;\n };\n \n-int ref_excluded(struct string_list *, const char *path);\n-void clear_ref_exclusion(struct string_list **);\n-void add_ref_exclusion(struct string_list **, const char *exclude);\n-\n-\n-#define REV_TREE_SAME\t\t0\n-#define REV_TREE_NEW\t\t1\t/* Only new files */\n-#define REV_TREE_OLD\t\t2\t/* Only files removed */\n-#define REV_TREE_DIFFERENT\t3\t/* Mixed changes */\n-\n-/* revision.c */\n-typedef void (*show_early_output_fn_t)(struct rev_info *, struct commit_list *);\n-extern volatile show_early_output_fn_t show_early_output;\n-\n-struct setup_revision_opt {\n-\tconst char *def;\n-\tvoid (*tweak)(struct rev_info *, struct setup_revision_opt *);\n-\tunsigned int\tassume_dashdash:1,\n-\t\t\tallow_exclude_promisor_objects:1;\n-\tunsigned revarg_opt;\n-};\n-\n-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS\n-#define init_revisions(revs, prefix) repo_init_revisions(the_repository, revs, prefix)\n-#endif\n-\n /**\n  * Initialize a rev_info structure with default values. The third parameter may\n  * be NULL or can be prefix path, and then the `.prefix` variable will be set\n@@ -366,6 +340,9 @@ struct setup_revision_opt {\n void repo_init_revisions(struct repository *r,\n \t\t\t struct rev_info *revs,\n \t\t\t const char *prefix);\n+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS\n+#define init_revisions(revs, prefix) repo_init_revisions(the_repository, revs, prefix)\n+#endif\n \n /**\n  * Parse revision information, filling in the `rev_info` structure, and\n@@ -374,6 +351,13 @@ void repo_init_revisions(struct repository *r,\n  * head of the argument list. The last parameter is used in case no\n  * parameter given by the first two arguments.\n  */\n+struct setup_revision_opt {\n+\tconst char *def;\n+\tvoid (*tweak)(struct rev_info *, struct setup_revision_opt *);\n+\tunsigned int\tassume_dashdash:1,\n+\t\t\tallow_exclude_promisor_objects:1;\n+\tunsigned revarg_opt;\n+};\n int setup_revisions(int argc, const char **argv, struct rev_info *revs,\n \t\t    struct setup_revision_opt *);\n \n@@ -423,6 +407,14 @@ void mark_trees_uninteresting_sparse(struct repository *r, struct oidset *trees)\n \n void show_object_with_name(FILE *, struct object *, const char *);\n \n+/**\n+ * Helpers to check if a \"struct string_list\" item matches with\n+ * wildmatch().\n+ */\n+int ref_excluded(struct string_list *, const char *path);\n+void clear_ref_exclusion(struct string_list **);\n+void add_ref_exclusion(struct string_list **, const char *exclude);\n+\n /**\n  * This function can be used if you want to add commit objects as revision\n  * information. You can use the `UNINTERESTING` object flag to indicate if\n@@ -478,4 +470,10 @@ int rewrite_parents(struct rev_info *revs,\n  */\n struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit);\n \n+/**\n+ * Global for the (undocumented) \"--early-output\" flag for \"git log\".\n+ */\n+typedef void (*show_early_output_fn_t)(struct rev_info *, struct commit_list *);\n+extern volatile show_early_output_fn_t show_early_output;\n+\n #endif\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452936","messageId":"patch-v5-09.27-240307bc3c6-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 09/27] revisions API users: use release_revisions() needing REV_INFO_INIT","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:23Z","receivedAt":"2022-04-02T10:50:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use release_revisions() to various users of \"struct rev_list\" which\nneed to have their \"struct rev_info\" zero-initialized before we can\nstart using it.\n\nFor the bundle.c code see the early exit case added in\n3bbbe467f29 (bundle verify: error out if called without an object\ndatabase, 2019-05-27).\n\nFor the relevant bisect.c code see 45b6370812c (bisect: libify\n`check_good_are_ancestors_of_bad` and its dependents, 2020-02-17).\n\nFor the submodule.c code see the \"goto\" on \"(!left || !right || !sub)\"\nadded in 8e6df65015f (submodule: refactor show_submodule_summary with\nhelper function, 2016-08-31).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n bisect.c                    | 18 ++++++++++++------\n builtin/submodule--helper.c |  3 ++-\n bundle.c                    | 12 ++++++++----\n revision.h                  | 21 ++++++++++++++++++++-\n submodule.c                 |  3 ++-\n 5 files changed, 44 insertions(+), 13 deletions(-)\n\ndiff --git a/bisect.c b/bisect.c\nindex cc6b8b6230d..b63669cc9d7 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -1010,7 +1010,7 @@ void read_bisect_terms(const char **read_bad, const char **read_good)\n  */\n enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n {\n-\tstruct rev_info revs;\n+\tstruct rev_info revs = REV_INFO_INIT;\n \tstruct commit_list *tried;\n \tint reaches = 0, all = 0, nr, steps;\n \tenum bisect_error res = BISECT_OK;\n@@ -1035,7 +1035,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \n \tres = check_good_are_ancestors_of_bad(r, prefix, no_checkout);\n \tif (res)\n-\t\treturn res;\n+\t\tgoto cleanup;\n \n \tbisect_rev_setup(r, &revs, prefix, \"%s\", \"^%s\", 1);\n \n@@ -1060,14 +1060,16 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \t\t       term_good,\n \t\t       term_bad);\n \n-\t\treturn BISECT_FAILED;\n+\t\tres = BISECT_FAILED;\n+\t\tgoto cleanup;\n \t}\n \n \tif (!all) {\n \t\tfprintf(stderr, _(\"No testable commit found.\\n\"\n \t\t\t\"Maybe you started with bad path arguments?\\n\"));\n \n-\t\treturn BISECT_NO_TESTABLE_COMMIT;\n+\t\tres = BISECT_NO_TESTABLE_COMMIT;\n+\t\tgoto cleanup;\n \t}\n \n \tbisect_rev = &revs.commits->item->object.oid;\n@@ -1087,7 +1089,8 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \t\t * for negative return values for early returns up\n \t\t * until the cmd_bisect__helper() caller.\n \t\t */\n-\t\treturn BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;\n+\t\tres = BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;\n+\t\tgoto cleanup;\n \t}\n \n \tnr = all - reaches - 1;\n@@ -1106,7 +1109,10 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \t/* Clean up objects used, as they will be reused. */\n \trepo_clear_commit_marks(r, ALL_REV_FLAGS);\n \n-\treturn bisect_checkout(bisect_rev, no_checkout);\n+\tres = bisect_checkout(bisect_rev, no_checkout);\n+cleanup:\n+\trelease_revisions(&revs);\n+\treturn res;\n }\n \n static inline int log2i(int n)\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 24980863f68..cda33ee4d2b 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -766,7 +766,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,\n {\n \tchar *displaypath;\n \tstruct strvec diff_files_args = STRVEC_INIT;\n-\tstruct rev_info rev;\n+\tstruct rev_info rev = REV_INFO_INIT;\n \tint diff_files_result;\n \tstruct strbuf buf = STRBUF_INIT;\n \tconst char *git_dir;\n@@ -853,6 +853,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,\n cleanup:\n \tstrvec_clear(&diff_files_args);\n \tfree(displaypath);\n+\trelease_revisions(&rev);\n }\n \n static void status_submodule_cb(const struct cache_entry *list_item,\ndiff --git a/bundle.c b/bundle.c\nindex d50cfb5aa7e..6a870a6edb7 100644\n--- a/bundle.c\n+++ b/bundle.c\n@@ -196,14 +196,16 @@ int verify_bundle(struct repository *r,\n \t * to be verbose about the errors\n \t */\n \tstruct string_list *p = &header->prerequisites;\n-\tstruct rev_info revs;\n+\tstruct rev_info revs = REV_INFO_INIT;\n \tconst char *argv[] = {NULL, \"--all\", NULL};\n \tstruct commit *commit;\n \tint i, ret = 0, req_nr;\n \tconst char *message = _(\"Repository lacks these prerequisite commits:\");\n \n-\tif (!r || !r->objects || !r->objects->odb)\n-\t\treturn error(_(\"need a repository to verify a bundle\"));\n+\tif (!r || !r->objects || !r->objects->odb) {\n+\t\tret = error(_(\"need a repository to verify a bundle\"));\n+\t\tgoto cleanup;\n+\t}\n \n \trepo_init_revisions(r, &revs, NULL);\n \tfor (i = 0; i < p->nr; i++) {\n@@ -221,7 +223,7 @@ int verify_bundle(struct repository *r,\n \t\terror(\"%s %s\", oid_to_hex(oid), name);\n \t}\n \tif (revs.pending.nr != p->nr)\n-\t\treturn ret;\n+\t\tgoto cleanup;\n \treq_nr = revs.pending.nr;\n \tsetup_revisions(2, argv, &revs, NULL);\n \n@@ -284,6 +286,8 @@ int verify_bundle(struct repository *r,\n \t\t\tprintf_ln(\"The bundle uses this filter: %s\",\n \t\t\t\t  list_objects_filter_spec(&header->filter));\n \t}\n+cleanup:\n+\trelease_revisions(&revs);\n \treturn ret;\n }\n \ndiff --git a/revision.h b/revision.h\nindex b9070e43428..2621eb6d65a 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -329,6 +329,25 @@ struct rev_info {\n \tstruct tmp_objdir *remerge_objdir;\n };\n \n+/**\n+ * Initialize the \"struct rev_info\" structure with a macro.\n+ *\n+ * This will not fully initialize a \"struct rev_info\", the\n+ * repo_init_revisions() function needs to be called before\n+ * setup_revisions() and any revision walking takes place.\n+ *\n+ * Use REV_INFO_INIT to make the \"struct rev_info\" safe for passing to\n+ * release_revisions() when it's inconvenient (e.g. due to a \"goto\n+ * cleanup\" pattern) to arrange for repo_init_revisions() to be called\n+ * before release_revisions() is called.\n+ *\n+ * Initializing with this REV_INFO_INIT is redundant to invoking\n+ * repo_init_revisions(). If repo_init_revisions() is guaranteed to be\n+ * called before release_revisions() the \"struct rev_info\" can be left\n+ * uninitialized.\n+ */\n+#define REV_INFO_INIT { 0 }\n+\n /**\n  * Initialize a rev_info structure with default values. The third parameter may\n  * be NULL or can be prefix path, and then the `.prefix` variable will be set\n@@ -363,7 +382,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs,\n \n /**\n  * Free data allocated in a \"struct rev_info\" after it's been\n- * initialized with repo_init_revisions().\n+ * initialized with repo_init_revisions() or REV_INFO_INIT.\n  */\n void release_revisions(struct rev_info *revs);\n \ndiff --git a/submodule.c b/submodule.c\nindex 7b5d7753930..7923e951e1c 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -619,7 +619,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,\n \t\tstruct object_id *one, struct object_id *two,\n \t\tunsigned dirty_submodule)\n {\n-\tstruct rev_info rev;\n+\tstruct rev_info rev = REV_INFO_INIT;\n \tstruct commit *left = NULL, *right = NULL;\n \tstruct commit_list *merge_bases = NULL;\n \tstruct repository *sub;\n@@ -646,6 +646,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,\n \n out:\n \tfree_commit_list(merge_bases);\n+\trelease_revisions(&rev);\n \tclear_commit_marks(left, ~0);\n \tclear_commit_marks(right, ~0);\n \tif (sub) {\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452937","messageId":"patch-v5-10.27-4c5404912e9-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 10/27] stash: always have the owner of \"stash_info\" free it","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:24Z","receivedAt":"2022-04-02T10:50:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the initialization of the \"revision\" member of \"struct\nstash_info\" to be initialized vi a macro, and more importantly that\nthat initializing function be tasked to free it, usually via a \"goto\ncleanup\" pattern.\n\nDespite the \"revision\" name (and the topic of the series containing\nthis commit) the \"stash info\" has nothing to do with the \"struct\nrev_info\". I'm making this change because in the subsequent commit\nwhen we do want to free the \"struct rev_info\" via a \"goto cleanup\"\npattern we'd otherwise free() uninitialized memory in some cases, as\nwe only strbuf_init() the string in get_stash_info().\n\nSo while it's not the smallest possible change, let's convert all\nusers of this pattern in the file while we're at it.\n\nA good follow-up to this change would be to change all the \"ret = -1;\ngoto done;\" in this file to instead use a \"goto cleanup\", and\ninitialize \"int ret = -1\" at the start of the relevant functions. That\nwould allow us to drop a lot of needless brace verbosity on two-line\n\"if\" statements, but let's leave that alone for now.\n\nTo ensure that there's a 1=1 mapping between owners of the \"struct\nstash_info\" and free_stash_info() change the assert_stash_ref()\nfunction to be a trivial get_stash_info_assert() wrapper. The caller\nwill call free_stash_info(), and by returning -1 we'll eventually (via\n!!ret) exit with status 1 anyway.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/stash.c | 107 ++++++++++++++++++++++++++----------------------\n 1 file changed, 59 insertions(+), 48 deletions(-)\n\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 74fa810cf8c..ba030238939 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -116,6 +116,10 @@ struct stash_info {\n \tint has_u;\n };\n \n+#define STASH_INFO_INIT { \\\n+\t.revision = STRBUF_INIT, \\\n+}\n+\n static void free_stash_info(struct stash_info *info)\n {\n \tstrbuf_release(&info->revision);\n@@ -157,10 +161,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)\n \tif (argc == 1)\n \t\tcommit = argv[0];\n \n-\tstrbuf_init(&info->revision, 0);\n \tif (!commit) {\n \t\tif (!ref_exists(ref_stash)) {\n-\t\t\tfree_stash_info(info);\n \t\t\tfprintf_ln(stderr, _(\"No stash entries found.\"));\n \t\t\treturn -1;\n \t\t}\n@@ -174,11 +176,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)\n \n \trevision = info->revision.buf;\n \n-\tif (get_oid(revision, &info->w_commit)) {\n-\t\terror(_(\"%s is not a valid reference\"), revision);\n-\t\tfree_stash_info(info);\n-\t\treturn -1;\n-\t}\n+\tif (get_oid(revision, &info->w_commit))\n+\t\treturn error(_(\"%s is not a valid reference\"), revision);\n \n \tassert_stash_like(info, revision);\n \n@@ -197,7 +196,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)\n \t\tinfo->is_stash_ref = !strcmp(expanded_ref, ref_stash);\n \t\tbreak;\n \tdefault: /* Invalid or ambiguous */\n-\t\tfree_stash_info(info);\n+\t\tbreak;\n \t}\n \n \tfree(expanded_ref);\n@@ -598,10 +597,10 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,\n \n static int apply_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret;\n+\tint ret = -1;\n \tint quiet = 0;\n \tint index = 0;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n \t\tOPT_BOOL(0, \"index\", &index,\n@@ -613,9 +612,10 @@ static int apply_stash(int argc, const char **argv, const char *prefix)\n \t\t\t     git_stash_apply_usage, 0);\n \n \tif (get_stash_info(&info, argc, argv))\n-\t\treturn -1;\n+\t\tgoto cleanup;\n \n \tret = do_apply_stash(prefix, &info, index, quiet);\n+cleanup:\n \tfree_stash_info(&info);\n \treturn ret;\n }\n@@ -651,20 +651,25 @@ static int do_drop_stash(struct stash_info *info, int quiet)\n \treturn 0;\n }\n \n-static void assert_stash_ref(struct stash_info *info)\n+static int get_stash_info_assert(struct stash_info *info, int argc,\n+\t\t\t\t const char **argv)\n {\n-\tif (!info->is_stash_ref) {\n-\t\terror(_(\"'%s' is not a stash reference\"), info->revision.buf);\n-\t\tfree_stash_info(info);\n-\t\texit(1);\n-\t}\n+\tint ret = get_stash_info(info, argc, argv);\n+\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tif (!info->is_stash_ref)\n+\t\treturn error(_(\"'%s' is not a stash reference\"), info->revision.buf);\n+\n+\treturn 0;\n }\n \n static int drop_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret;\n+\tint ret = -1;\n \tint quiet = 0;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n \t\tOPT_END()\n@@ -673,22 +678,21 @@ static int drop_stash(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, options,\n \t\t\t     git_stash_drop_usage, 0);\n \n-\tif (get_stash_info(&info, argc, argv))\n-\t\treturn -1;\n-\n-\tassert_stash_ref(&info);\n+\tif (get_stash_info_assert(&info, argc, argv))\n+\t\tgoto cleanup;\n \n \tret = do_drop_stash(&info, quiet);\n+cleanup:\n \tfree_stash_info(&info);\n \treturn ret;\n }\n \n static int pop_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret;\n+\tint ret = -1;\n \tint index = 0;\n \tint quiet = 0;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n \t\tOPT_BOOL(0, \"index\", &index,\n@@ -699,25 +703,25 @@ static int pop_stash(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, options,\n \t\t\t     git_stash_pop_usage, 0);\n \n-\tif (get_stash_info(&info, argc, argv))\n-\t\treturn -1;\n+\tif (get_stash_info_assert(&info, argc, argv))\n+\t\tgoto cleanup;\n \n-\tassert_stash_ref(&info);\n \tif ((ret = do_apply_stash(prefix, &info, index, quiet)))\n \t\tprintf_ln(_(\"The stash entry is kept in case \"\n \t\t\t    \"you need it again.\"));\n \telse\n \t\tret = do_drop_stash(&info, quiet);\n \n+cleanup:\n \tfree_stash_info(&info);\n \treturn ret;\n }\n \n static int branch_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret;\n+\tint ret = -1;\n \tconst char *branch = NULL;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tstruct option options[] = {\n \t\tOPT_END()\n@@ -734,7 +738,7 @@ static int branch_stash(int argc, const char **argv, const char *prefix)\n \tbranch = argv[0];\n \n \tif (get_stash_info(&info, argc - 1, argv + 1))\n-\t\treturn -1;\n+\t\tgoto cleanup;\n \n \tcp.git_cmd = 1;\n \tstrvec_pushl(&cp.args, \"checkout\", \"-b\", NULL);\n@@ -746,8 +750,8 @@ static int branch_stash(int argc, const char **argv, const char *prefix)\n \tif (!ret && info.is_stash_ref)\n \t\tret = do_drop_stash(&info, 0);\n \n+cleanup:\n \tfree_stash_info(&info);\n-\n \treturn ret;\n }\n \n@@ -825,8 +829,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op\n static int show_stash(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n-\tint ret = 0;\n-\tstruct stash_info info;\n+\tint ret = -1;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct rev_info rev;\n \tstruct strvec stash_args = STRVEC_INIT;\n \tstruct strvec revision_args = STRVEC_INIT;\n@@ -844,6 +848,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \t\t\t      UNTRACKED_ONLY, PARSE_OPT_NONEG),\n \t\tOPT_END()\n \t};\n+\tint do_usage = 0;\n \n \tinit_diff_ui_defaults();\n \tgit_config(git_diff_ui_config, NULL);\n@@ -861,10 +866,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \t\t\tstrvec_push(&revision_args, argv[i]);\n \t}\n \n-\tret = get_stash_info(&info, stash_args.nr, stash_args.v);\n-\tstrvec_clear(&stash_args);\n-\tif (ret)\n-\t\treturn -1;\n+\tif (get_stash_info(&info, stash_args.nr, stash_args.v))\n+\t\tgoto cleanup;\n \n \t/*\n \t * The config settings are applied only if there are not passed\n@@ -878,16 +881,14 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \t\t\trev.diffopt.output_format |= DIFF_FORMAT_PATCH;\n \n \t\tif (!show_stat && !show_patch) {\n-\t\t\tfree_stash_info(&info);\n-\t\t\treturn 0;\n+\t\t\tret = 0;\n+\t\t\tgoto cleanup;\n \t\t}\n \t}\n \n \targc = setup_revisions(revision_args.nr, revision_args.v, &rev, NULL);\n-\tif (argc > 1) {\n-\t\tfree_stash_info(&info);\n-\t\tusage_with_options(git_stash_show_usage, options);\n-\t}\n+\tif (argc > 1)\n+\t\tgoto usage;\n \tif (!rev.diffopt.output_format) {\n \t\trev.diffopt.output_format = DIFF_FORMAT_PATCH;\n \t\tdiff_setup_done(&rev.diffopt);\n@@ -912,8 +913,16 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \t}\n \tlog_tree_diff_flush(&rev);\n \n+\tret = diff_result_code(&rev.diffopt, 0);\n+cleanup:\n+\tstrvec_clear(&stash_args);\n \tfree_stash_info(&info);\n-\treturn diff_result_code(&rev.diffopt, 0);\n+\tif (do_usage)\n+\t\tusage_with_options(git_stash_show_usage, options);\n+\treturn ret;\n+usage:\n+\tdo_usage = 1;\n+\tgoto cleanup;\n }\n \n static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,\n@@ -1409,9 +1418,9 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b\n \n static int create_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret = 0;\n+\tint ret;\n \tstruct strbuf stash_msg_buf = STRBUF_INIT;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct pathspec ps;\n \n \t/* Starting with argv[1], since argv[0] is \"create\" */\n@@ -1426,6 +1435,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)\n \tif (!ret)\n \t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n \n+\tfree_stash_info(&info);\n \tstrbuf_release(&stash_msg_buf);\n \treturn ret;\n }\n@@ -1434,7 +1444,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q\n \t\t\t int keep_index, int patch_mode, int include_untracked, int only_staged)\n {\n \tint ret = 0;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct strbuf patch = STRBUF_INIT;\n \tstruct strbuf stash_msg_buf = STRBUF_INIT;\n \tstruct strbuf untracked_files = STRBUF_INIT;\n@@ -1633,6 +1643,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q\n \t}\n \n done:\n+\tfree_stash_info(&info);\n \tstrbuf_release(&stash_msg_buf);\n \treturn ret;\n }\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452938","messageId":"patch-v5-11.27-4210232e3a1-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 11/27] revisions API users: add \"goto cleanup\" for release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:25Z","receivedAt":"2022-04-02T10:50:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a release_revisions() to various users of \"struct rev_info\" which\nrequires a minor refactoring to a \"goto cleanup\" pattern to use that\nfunction.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/diff-files.c        |  8 ++++++--\n builtin/rev-list.c          | 19 ++++++++++++-------\n builtin/stash.c             |  1 +\n builtin/submodule--helper.c | 10 +++++++---\n sequencer.c                 | 23 ++++++++++++++++-------\n t/helper/test-fast-rebase.c | 18 +++++++++++++-----\n 6 files changed, 55 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin/diff-files.c b/builtin/diff-files.c\nindex 70103c40952..2bfaf9ba7ae 100644\n--- a/builtin/diff-files.c\n+++ b/builtin/diff-files.c\n@@ -77,8 +77,12 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)\n \n \tif (read_cache_preload(&rev.diffopt.pathspec) < 0) {\n \t\tperror(\"read_cache_preload\");\n-\t\treturn -1;\n+\t\tresult = -1;\n+\t\tgoto cleanup;\n \t}\n+cleanup:\n \tresult = run_diff_files(&rev, options);\n-\treturn diff_result_code(&rev.diffopt, result);\n+\tresult = diff_result_code(&rev.diffopt, result);\n+\trelease_revisions(&rev);\n+\treturn result;\n }\ndiff --git a/builtin/rev-list.c b/builtin/rev-list.c\nindex 07c0ad11d8d..30fd8e83eaf 100644\n--- a/builtin/rev-list.c\n+++ b/builtin/rev-list.c\n@@ -500,6 +500,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \tint use_bitmap_index = 0;\n \tint filter_provided_objects = 0;\n \tconst char *show_progress = NULL;\n+\tint ret = 0;\n \n \tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n \t\tusage(rev_list_usage);\n@@ -583,7 +584,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \t\t}\n \t\tif (!strcmp(arg, \"--test-bitmap\")) {\n \t\t\ttest_bitmap_walk(&revs);\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t\t}\n \t\tif (skip_prefix(arg, \"--progress=\", &arg)) {\n \t\t\tshow_progress = arg;\n@@ -672,11 +673,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \n \tif (use_bitmap_index) {\n \t\tif (!try_bitmap_count(&revs, filter_provided_objects))\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t\tif (!try_bitmap_disk_usage(&revs, filter_provided_objects))\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t\tif (!try_bitmap_traversal(&revs, filter_provided_objects))\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t}\n \n \tif (prepare_revision_walk(&revs))\n@@ -696,8 +697,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \n \t\tfind_bisection(&revs.commits, &reaches, &all, bisect_flags);\n \n-\t\tif (bisect_show_vars)\n-\t\t\treturn show_bisect_vars(&info, reaches, all);\n+\t\tif (bisect_show_vars) {\n+\t\t\tret = show_bisect_vars(&info, reaches, all);\n+\t\t\tgoto cleanup;\n+\t\t}\n \t}\n \n \tif (filter_provided_objects) {\n@@ -752,5 +755,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \tif (show_disk_usage)\n \t\tprintf(\"%\"PRIuMAX\"\\n\", (uintmax_t)total_disk_usage);\n \n-\treturn 0;\n+cleanup:\n+\trelease_revisions(&revs);\n+\treturn ret;\n }\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex ba030238939..fce4393d123 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -917,6 +917,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n cleanup:\n \tstrvec_clear(&stash_args);\n \tfree_stash_info(&info);\n+\trelease_revisions(&rev);\n \tif (do_usage)\n \t\tusage_with_options(git_stash_show_usage, options);\n \treturn ret;\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex cda33ee4d2b..1bd31c85946 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1232,6 +1232,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \tstruct strvec diff_args = STRVEC_INIT;\n \tstruct rev_info rev;\n \tstruct module_cb_list list = MODULE_CB_LIST_INIT;\n+\tint ret = 0;\n \n \tstrvec_push(&diff_args, get_diff_cmd(diff_cmd));\n \tif (info->cached)\n@@ -1257,11 +1258,13 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \t\t\tsetup_work_tree();\n \t\tif (read_cache_preload(&rev.diffopt.pathspec) < 0) {\n \t\t\tperror(\"read_cache_preload\");\n-\t\t\treturn -1;\n+\t\t\tret = -1;\n+\t\t\tgoto cleanup;\n \t\t}\n \t} else if (read_cache() < 0) {\n \t\tperror(\"read_cache\");\n-\t\treturn -1;\n+\t\tret = -1;\n+\t\tgoto cleanup;\n \t}\n \n \tif (diff_cmd == DIFF_INDEX)\n@@ -1269,9 +1272,10 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \telse\n \t\trun_diff_files(&rev, 0);\n \tprepare_submodule_summary(info, &list);\n+cleanup:\n \tstrvec_clear(&diff_args);\n \trelease_revisions(&rev);\n-\treturn 0;\n+\treturn ret;\n }\n \n static int module_summary(int argc, const char **argv, const char *prefix)\ndiff --git a/sequencer.c b/sequencer.c\nindex f9d7acd1065..41ae5e25278 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -5354,6 +5354,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \tint rebase_merges = flags & TODO_LIST_REBASE_MERGES;\n \tint reapply_cherry_picks = flags & TODO_LIST_REAPPLY_CHERRY_PICKS;\n \tint skipped_commit = 0;\n+\tint ret = 0;\n \n \trepo_init_revisions(r, &revs, NULL);\n \trevs.verbose_header = 1;\n@@ -5377,14 +5378,20 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \tpp.fmt = revs.commit_format;\n \tpp.output_encoding = get_log_output_encoding();\n \n-\tif (setup_revisions(argc, argv, &revs, NULL) > 1)\n-\t\treturn error(_(\"make_script: unhandled options\"));\n+\tif (setup_revisions(argc, argv, &revs, NULL) > 1) {\n+\t\tret = error(_(\"make_script: unhandled options\"));\n+\t\tgoto cleanup;\n+\t}\n \n-\tif (prepare_revision_walk(&revs) < 0)\n-\t\treturn error(_(\"make_script: error preparing revisions\"));\n+\tif (prepare_revision_walk(&revs) < 0) {\n+\t\tret = error(_(\"make_script: error preparing revisions\"));\n+\t\tgoto cleanup;\n+\t}\n \n-\tif (rebase_merges)\n-\t\treturn make_script_with_merges(&pp, &revs, out, flags);\n+\tif (rebase_merges) {\n+\t\tret = make_script_with_merges(&pp, &revs, out, flags);\n+\t\tgoto cleanup;\n+\t}\n \n \twhile ((commit = get_revision(&revs))) {\n \t\tint is_empty = is_original_commit_empty(commit);\n@@ -5408,7 +5415,9 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \tif (skipped_commit)\n \t\tadvise_if_enabled(ADVICE_SKIPPED_CHERRY_PICKS,\n \t\t\t\t  _(\"use --reapply-cherry-picks to include skipped commits\"));\n-\treturn 0;\n+cleanup:\n+\trelease_revisions(&revs);\n+\treturn ret;\n }\n \n /*\ndiff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c\nindex 993b90eaedd..4e5553e2024 100644\n--- a/t/helper/test-fast-rebase.c\n+++ b/t/helper/test-fast-rebase.c\n@@ -99,6 +99,7 @@ int cmd__fast_rebase(int argc, const char **argv)\n \tstruct merge_result result;\n \tstruct strbuf reflog_msg = STRBUF_INIT;\n \tstruct strbuf branch_name = STRBUF_INIT;\n+\tint ret = 0;\n \n \t/*\n \t * test-tool stuff doesn't set up the git directory by default; need to\n@@ -137,13 +138,17 @@ int cmd__fast_rebase(int argc, const char **argv)\n \trevs.topo_order = 1;\n \tstrvec_pushl(&rev_walk_args, \"\", argv[4], \"--not\", argv[3], NULL);\n \n-\tif (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1)\n-\t\treturn error(_(\"unhandled options\"));\n+\tif (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) {\n+\t\tret = error(_(\"unhandled options\"));\n+\t\tgoto cleanup;\n+\t}\n \n \tstrvec_clear(&rev_walk_args);\n \n-\tif (prepare_revision_walk(&revs) < 0)\n-\t\treturn error(_(\"error preparing revisions\"));\n+\tif (prepare_revision_walk(&revs) < 0) {\n+\t\tret = error(_(\"error preparing revisions\"));\n+\t\tgoto cleanup;\n+\t}\n \n \tinit_merge_options(&merge_opt, the_repository);\n \tmemset(&result, 0, sizeof(result));\n@@ -220,7 +225,10 @@ int cmd__fast_rebase(int argc, const char **argv)\n \t\t\t       COMMIT_LOCK | SKIP_IF_UNCHANGED))\n \t\tdie(_(\"unable to write %s\"), get_index_file());\n \n+\tret = (result.clean == 0);\n+cleanup:\n \tstrbuf_release(&reflog_msg);\n \tstrbuf_release(&branch_name);\n-\treturn (result.clean == 0);\n+\trelease_revisions(&revs);\n+\treturn ret;\n }\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452939","messageId":"patch-v5-12.27-1918ca0a85d-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 12/27] revisions API users: use release_revisions() in http-push.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:26Z","receivedAt":"2022-04-02T10:50:17Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In the case of cmd_main() in http-push.c we need to move the\ndeceleration of the \"struct rev-list\" into the loop over the\n\"remote_refs\" when adding a release_revisions().\n\nWe'd previously set up the \"revs\" for each remote, but would\npotentially leak memory on each one.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n http-push.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/http-push.c b/http-push.c\nindex f0c044dcf76..01e7c2ac5c8 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -1689,7 +1689,6 @@ int cmd_main(int argc, const char **argv)\n \tstruct refspec rs = REFSPEC_INIT_PUSH;\n \tstruct remote_lock *ref_lock = NULL;\n \tstruct remote_lock *info_ref_lock = NULL;\n-\tstruct rev_info revs;\n \tint delete_branch = 0;\n \tint force_delete = 0;\n \tint objects_to_send;\n@@ -1825,6 +1824,7 @@ int cmd_main(int argc, const char **argv)\n \n \tnew_refs = 0;\n \tfor (ref = remote_refs; ref; ref = ref->next) {\n+\t\tstruct rev_info revs;\n \t\tstruct strvec commit_argv = STRVEC_INIT;\n \n \t\tif (!ref->peer_ref)\n@@ -1941,6 +1941,7 @@ int cmd_main(int argc, const char **argv)\n \t\tunlock_remote(ref_lock);\n \t\tcheck_locks();\n \t\tstrvec_clear(&commit_argv);\n+\t\trelease_revisions(&revs);\n \t}\n \n \t/* Update remote server info if appropriate */\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452940","messageId":"patch-v5-13.27-6a02b7aae4e-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 13/27] revisions API users: use release_revisions() in builtin/log.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:27Z","receivedAt":"2022-04-02T10:50:19Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In preparation for having the \"log\" family of functions make wider use\nof release_revisions() let's have them call it just before\nexiting. This changes the \"log\", \"whatchanged\", \"show\",\n\"format-patch\", etc. commands, all of which live in this file.\n\nThe release_revisions() API still only frees the \"pending\" member, but\nwill learn to release more members of \"struct rev_info\" in subsequent\ncommits.\n\nIn the case of \"format-patch\" revert the addition of UNLEAK() in\ndee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16),\nwhich will cause several tests that previously passed under\n\"TEST_PASSES_SANITIZE_LEAK=true\" to start failing.\n\nIn subsequent commits we'll now be able to use those tests to check\nwhether that part of the API is really leaking memory, and will fix\nall of those memory leaks. Removing the UNLEAK() allows us to make\nincremental progress in that direction. See [1] for further details\nabout this approach.\n\nNote that the release_revisions() will not be sufficient to deal with\nthe code in cmd_show() added in 5d7eeee2ac6 (git-show: grok blobs,\ntrees and tags, too, 2006-12-14) which clobbers the \"pending\" array in\nthe case of \"OBJ_COMMIT\". That will need to be dealt with by some\nfuture follow-up work.\n\n1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/log.c          | 20 ++++++++++++--------\n t/t4126-apply-empty.sh |  2 --\n 2 files changed, 12 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 6f9928fabfe..c40ebe1c3f4 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -295,6 +295,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,\n \tcmd_log_init_finish(argc, argv, prefix, rev, opt);\n }\n \n+static int cmd_log_deinit(int ret, struct rev_info *rev)\n+{\n+\trelease_revisions(rev);\n+\treturn ret;\n+}\n+\n /*\n  * This gives a rough estimate for how many commits we\n  * will print out in the list.\n@@ -558,7 +564,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)\n \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n \tif (!rev.diffopt.output_format)\n \t\trev.diffopt.output_format = DIFF_FORMAT_RAW;\n-\treturn cmd_log_walk(&rev);\n+\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n }\n \n static void show_tagger(const char *buf, struct rev_info *rev)\n@@ -677,7 +683,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n \n \tif (!rev.no_walk)\n-\t\treturn cmd_log_walk(&rev);\n+\t\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n \n \tcount = rev.pending.nr;\n \tobjects = rev.pending.objects;\n@@ -732,8 +738,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \t\t\tret = error(_(\"unknown type: %d\"), o->type);\n \t\t}\n \t}\n-\tfree(objects);\n-\treturn ret;\n+\treturn cmd_log_deinit(ret, &rev);\n }\n \n /*\n@@ -761,7 +766,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)\n \trev.always_show_header = 1;\n \tcmd_log_init_finish(argc, argv, prefix, &rev, &opt);\n \n-\treturn cmd_log_walk(&rev);\n+\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n }\n \n static void log_setup_revisions_tweak(struct rev_info *rev,\n@@ -792,7 +797,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)\n \topt.revarg_opt = REVARG_COMMITTISH;\n \topt.tweak = log_setup_revisions_tweak;\n \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n-\treturn cmd_log_walk(&rev);\n+\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n }\n \n /* format-patch */\n@@ -2289,8 +2294,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tif (rev.ref_message_ids)\n \t\tstring_list_clear(rev.ref_message_ids, 0);\n \tfree(rev.ref_message_ids);\n-\tUNLEAK(rev);\n-\treturn 0;\n+\treturn cmd_log_deinit(0, &rev);\n }\n \n static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)\ndiff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh\nindex 33860d38290..66a7ba8ab8f 100755\n--- a/t/t4126-apply-empty.sh\n+++ b/t/t4126-apply-empty.sh\n@@ -2,8 +2,6 @@\n \n test_description='apply empty'\n \n-\n-TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452941","messageId":"patch-v5-14.27-ddc7402b054-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 14/27] revisions API users: use release_revisions() with UNLEAK()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:28Z","receivedAt":"2022-04-02T10:50:22Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use a release_revisions() with those \"struct rev_list\" users which\nalready \"UNLEAK\" the struct. It may seem odd to simultaneously attempt\nto free() memory, but also to explicitly ignore whether we have memory\nleaks in the same.\n\nAs explained in preceding commits this is being done to use the\nbuilt-in commands as a guinea pig for whether the release_revisions()\nfunction works as expected, we'd like to test e.g. whether we segfault\nas we change it. In subsequent commits we'll then remove these\nUNLEAK() as the function is made to free the memory that caused us to\nadd them in the first place.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/diff-index.c | 4 +++-\n builtin/diff.c       | 1 +\n 2 files changed, 4 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/diff-index.c b/builtin/diff-index.c\nindex 5fd23ab5b6c..3a83183c312 100644\n--- a/builtin/diff-index.c\n+++ b/builtin/diff-index.c\n@@ -71,5 +71,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)\n \t}\n \tresult = run_diff_index(&rev, option);\n \tUNLEAK(rev);\n-\treturn diff_result_code(&rev.diffopt, result);\n+\tresult = diff_result_code(&rev.diffopt, result);\n+\trelease_revisions(&rev);\n+\treturn result;\n }\ndiff --git a/builtin/diff.c b/builtin/diff.c\nindex bb7fafca618..dd48336da56 100644\n--- a/builtin/diff.c\n+++ b/builtin/diff.c\n@@ -595,6 +595,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \tif (1 < rev.diffopt.skip_stat_unmatch)\n \t\trefresh_index_quietly();\n \tUNLEAK(rev);\n+\trelease_revisions(&rev);\n \tUNLEAK(ent);\n \tUNLEAK(blob);\n \treturn result;\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452942","messageId":"patch-v5-15.27-03e9bd73d4b-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 15/27] revisions API users: use release_revisions() for \"prune_data\" users","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:29Z","receivedAt":"2022-04-02T10:50:25Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use release_revisions() for users of \"struct rev_list\" that reach into\nthe \"struct rev_info\" and clear the \"prune_data\" already.\n\nIn a subsequent commit we'll teach release_revisions() to clear this\nitself, but in the meantime let's invoke release_revisions() here to\nclear anything else we may have missed, and for reasons of having\nconsistent boilerplate.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/add.c | 1 +\n diff-lib.c    | 1 +\n wt-status.c   | 1 +\n 3 files changed, 3 insertions(+)\n\ndiff --git a/builtin/add.c b/builtin/add.c\nindex f507d2191cd..115a26ea633 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -143,6 +143,7 @@ int add_files_to_cache(const char *prefix,\n \trev.max_count = 0; /* do not compare unmerged paths with stage #2 */\n \trun_diff_files(&rev, DIFF_RACY_IS_MODIFIED);\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n \treturn !!data.add_errors;\n }\n \ndiff --git a/diff-lib.c b/diff-lib.c\nindex d6800274bd5..0f16281253f 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -642,6 +642,7 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)\n \tif (diff_cache(&revs, tree_oid, NULL, 1))\n \t\texit(128);\n \tclear_pathspec(&revs.prune_data);\n+\trelease_revisions(&revs);\n \treturn 0;\n }\n \ndiff --git a/wt-status.c b/wt-status.c\nindex f9100621375..a14fad1e03a 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -617,6 +617,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)\n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_files(&rev, 0);\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n }\n \n static void wt_status_collect_changes_index(struct wt_status *s)\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452943","messageId":"patch-v5-16.27-285673e40bf-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 16/27] revisions API: have release_revisions() release \"commits\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:30Z","receivedAt":"2022-04-02T10:50:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"commits\" in the \"struct rev_info\".\n\nWe don't expect to use this \"struct rev_info\" again, so there's no\nreason to NULL out revs->commits, as e.g. simplify_merges() and\ncreate_boundary_commit_list() do.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/revision.c b/revision.c\nindex 472fff1e0a7..553f7de8250 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2928,6 +2928,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \n void release_revisions(struct rev_info *revs)\n {\n+\tfree_commit_list(revs->commits);\n \tobject_array_clear(&revs->pending);\n }\n \n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452944","messageId":"patch-v5-17.27-ca659940ee5-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 17/27] revisions API: have release_revisions() release \"mailmap\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:31Z","receivedAt":"2022-04-02T10:50:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"mailmap\" in the \"struct rev_info\".\n\nThe log family of functions now calls the clear_mailmap() function\nadded in fa8afd18e5a (revisions API: provide and use a\nrelease_revisions(), 2021-09-19), allowing us to whitelist some tests\nwith \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nUnfortunately having a pointer to a mailmap in \"struct rev_info\"\ninstead of an embedded member that we \"own\" get a bit messy, as can be\nseen in the change to builtin/commit.c.\n\nWhen we free() this data we won't be able to tell apart a pointer to a\n\"mailmap\" on the heap from one on the stack. As seen in\nea57bc0d41b (log: add --use-mailmap option, 2013-01-05) the \"log\"\nfamily allocates it on the heap, but in the find_author_by_nickname()\ncode added in ea16794e430 (commit: search author pattern against\nmailmap, 2013-08-23) we allocated it on the stack instead.\n\nIdeally we'd simply change that member to a \"struct string_list\nmailmap\" and never free() the \"mailmap\" itself, but that would be a\nmuch larger change to the revisions API.\n\nWe have code that needs to hand an existing \"mailmap\" to a \"struct\nrev_info\", while we could change all of that, let's not go there\nnow.\n\nThe complexity isn't in the ownership of the \"mailmap\" per-se, but\nthat various things assume a \"rev_info.mailmap == NULL\" means \"doesn't\nwant mailmap\", if we changed that to an init'd \"struct string_list\nwe'd need to carefully refactor things to change those assumptions.\n\nLet's instead always free() it, and simply declare that if you add\nsuch a \"mailmap\" it must be allocated on the heap. Any modern libc\nwill correctly panic if we free() a stack variable, so this should be\nsafe going forward.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/commit.c                   | 5 ++---\n revision.c                         | 9 +++++++++\n t/t0056-git-C.sh                   | 1 +\n t/t3302-notes-index-expensive.sh   | 1 +\n t/t4055-diff-context.sh            | 1 +\n t/t4066-diff-emit-delay.sh         | 1 +\n t/t7008-filter-branch-null-sha1.sh | 1 +\n 7 files changed, 16 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex c7eda9bbb72..cd6cebcf8c8 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1100,7 +1100,6 @@ static const char *find_author_by_nickname(const char *name)\n \tstruct rev_info revs;\n \tstruct commit *commit;\n \tstruct strbuf buf = STRBUF_INIT;\n-\tstruct string_list mailmap = STRING_LIST_INIT_NODUP;\n \tconst char *av[20];\n \tint ac = 0;\n \n@@ -1111,7 +1110,8 @@ static const char *find_author_by_nickname(const char *name)\n \tav[++ac] = buf.buf;\n \tav[++ac] = NULL;\n \tsetup_revisions(ac, av, &revs, NULL);\n-\trevs.mailmap = &mailmap;\n+\trevs.mailmap = xmalloc(sizeof(struct string_list));\n+\tstring_list_init_nodup(revs.mailmap);\n \tread_mailmap(revs.mailmap);\n \n \tif (prepare_revision_walk(&revs))\n@@ -1122,7 +1122,6 @@ static const char *find_author_by_nickname(const char *name)\n \t\tctx.date_mode.type = DATE_NORMAL;\n \t\tstrbuf_release(&buf);\n \t\tformat_commit_message(commit, \"%aN <%aE>\", &buf, &ctx);\n-\t\tclear_mailmap(&mailmap);\n \t\trelease_revisions(&revs);\n \t\treturn strbuf_detach(&buf, NULL);\n \t}\ndiff --git a/revision.c b/revision.c\nindex 553f7de8250..622f0faecc4 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2926,10 +2926,19 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \treturn left;\n }\n \n+static void release_revisions_mailmap(struct string_list *mailmap)\n+{\n+\tif (!mailmap)\n+\t\treturn;\n+\tclear_mailmap(mailmap);\n+\tfree(mailmap);\n+}\n+\n void release_revisions(struct rev_info *revs)\n {\n \tfree_commit_list(revs->commits);\n \tobject_array_clear(&revs->pending);\n+\trelease_revisions_mailmap(revs->mailmap);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\ndiff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh\nindex 2630e756dab..752aa8c9454 100755\n--- a/t/t0056-git-C.sh\n+++ b/t/t0056-git-C.sh\n@@ -2,6 +2,7 @@\n \n test_description='\"-C <path>\" option and its effects on other path-related options'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success '\"git -C <path>\" runs git from the directory <path>' '\ndiff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh\nindex bb5fea02a03..d0c4d38b4d4 100755\n--- a/t/t3302-notes-index-expensive.sh\n+++ b/t/t3302-notes-index-expensive.sh\n@@ -8,6 +8,7 @@ test_description='Test commit notes index (expensive!)'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n create_repo () {\ndiff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh\nindex 741e0803c1a..73048d0a526 100755\n--- a/t/t4055-diff-context.sh\n+++ b/t/t4055-diff-context.sh\n@@ -5,6 +5,7 @@\n \n test_description='diff.context configuration'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t4066-diff-emit-delay.sh b/t/t4066-diff-emit-delay.sh\nindex a1de63b77f8..0ecb3915412 100755\n--- a/t/t4066-diff-emit-delay.sh\n+++ b/t/t4066-diff-emit-delay.sh\n@@ -4,6 +4,7 @@ test_description='test combined/stat/moved interaction'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # This test covers a weird 3-way interaction between \"--cc -p\", which will run\ndiff --git a/t/t7008-filter-branch-null-sha1.sh b/t/t7008-filter-branch-null-sha1.sh\nindex 9ba9f24ad2f..93fbc92b8db 100755\n--- a/t/t7008-filter-branch-null-sha1.sh\n+++ b/t/t7008-filter-branch-null-sha1.sh\n@@ -1,6 +1,7 @@\n #!/bin/sh\n \n test_description='filter-branch removal of trees with null sha1'\n+\n . ./test-lib.sh\n \n test_expect_success 'setup: base commits' '\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452945","messageId":"patch-v5-18.27-7987c748ba4-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 18/27] revisions API: have release_revisions() release \"cmdline\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:32Z","receivedAt":"2022-04-02T10:50:53Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"cmdline\" in the \"struct rev_info\". This in combination with a\npreceding change to free \"commits\" and \"mailmap\" means that we can\nwhitelist another test under \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nThere was a proposal in [1] to do away with xstrdup()-ing this\nadd_rev_cmdline(), perhaps that would be worthwhile, but for now let's\njust free() it.\n\nWe could also make that a \"char *\" in \"struct rev_cmdline_entry\"\nitself, but since we own it let's expose it as a constant to outside\ncallers. I proposed that in [2] but have since changed my mind. See\n14d30cdfc04 (ref-filter: fix memory leak in `free_array_item()`,\n2019-07-10), c514c62a4fd (checkout: fix leak of non-existent branch\nnames, 2020-08-14) and other log history hits for \"free((char *)\" for\nprior art.\n\nThis includes the tests we had false-positive passes on before my\n6798b08e848 (perl Git.pm: don't ignore signalled failure in\n_cmd_close(), 2022-02-01), now they pass for real.\n\nSince there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to\nlist those that don't pass than to touch most of those 66. So let's\nintroduce a \"TEST_FAILS_SANITIZE_LEAK=true\", which if set in the tests\nwon't cause lib-git-svn.sh to set \"TEST_PASSES_SANITIZE_LEAK=true.\n\nThis change also marks all the tests that we removed\n\"TEST_FAILS_SANITIZE_LEAK=true\" from in an earlier commit due to\nremoving the UNLEAK() from cmd_format_patch(), we can now assert that\nits API use doesn't leak any \"struct rev_info\" memory.\n\nThis change also made commit \"t5503-tagfollow.sh\" pass on current\nmaster, but that would regress when combined with\nps/fetch-atomic-fixup's de004e848a9 (t5503: simplify setup of test\nwhich exercises failure of backfill, 2022-03-03) (through no fault of\nthat topic, that change started using \"git clone\" in the test, which\nhas an outstanding leak). Let's leave that test out for now to avoid\nin-flight semantic conflicts.\n\n1. https://lore.kernel.org/git/YUj%2FgFRh6pwrZalY@carlos-mbp.lan/\n2. https://lore.kernel.org/git/87o88obkb1.fsf@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c                                 | 12 ++++++++++++\n t/lib-git-svn.sh                           |  4 ++++\n t/t0062-revision-walking.sh                |  1 +\n t/t0101-at-syntax.sh                       |  2 ++\n t/t1060-object-corruption.sh               |  1 +\n t/t3303-notes-subtrees.sh                  |  1 +\n t/t3305-notes-fanout.sh                    |  1 +\n t/t3408-rebase-multi-line.sh               |  1 +\n t/t4027-diff-submodule.sh                  |  1 +\n t/t4128-apply-root.sh                      |  1 +\n t/t4212-log-corrupt.sh                     |  1 +\n t/t5515-fetch-merge-logic.sh               |  1 +\n t/t5518-fetch-exit-status.sh               |  1 +\n t/t6002-rev-list-bisect.sh                 |  1 +\n t/t6003-rev-list-topo-order.sh             |  1 +\n t/t6005-rev-list-count.sh                  |  1 +\n t/t6018-rev-list-glob.sh                   |  1 +\n t/t6100-rev-list-in-order.sh               |  1 +\n t/t9100-git-svn-basic.sh                   |  1 +\n t/t9101-git-svn-props.sh                   |  2 ++\n t/t9104-git-svn-follow-parent.sh           |  2 ++\n t/t9106-git-svn-commit-diff-clobber.sh     |  2 ++\n t/t9115-git-svn-dcommit-funky-renames.sh   |  1 +\n t/t9116-git-svn-log.sh                     |  2 ++\n t/t9122-git-svn-author.sh                  |  2 ++\n t/t9127-git-svn-partial-rebuild.sh         |  2 ++\n t/t9129-git-svn-i18n-commitencoding.sh     |  1 +\n t/t9132-git-svn-broken-symlink.sh          |  1 +\n t/t9139-git-svn-non-utf8-commitencoding.sh |  1 +\n t/t9146-git-svn-empty-dirs.sh              |  2 ++\n t/t9148-git-svn-propset.sh                 |  1 +\n t/t9151-svn-mergeinfo.sh                   |  1 +\n t/t9160-git-svn-preserve-empty-dirs.sh     |  1 +\n t/t9162-git-svn-dcommit-interactive.sh     |  2 ++\n t/t9164-git-svn-dcommit-concurrent.sh      |  2 ++\n t/t9501-gitweb-standalone-http-status.sh   |  1 +\n 36 files changed, 60 insertions(+)\n\ndiff --git a/revision.c b/revision.c\nindex 622f0faecc4..c81506fa22f 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2926,6 +2926,17 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \treturn left;\n }\n \n+static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)\n+{\n+\tunsigned int i;\n+\n+\tif (!cmdline)\n+\t\treturn;\n+\tfor (i = 0; i < cmdline->nr; i++)\n+\t\tfree((char *)cmdline->rev[i].name);\n+\tfree(cmdline->rev);\n+}\n+\n static void release_revisions_mailmap(struct string_list *mailmap)\n {\n \tif (!mailmap)\n@@ -2938,6 +2949,7 @@ void release_revisions(struct rev_info *revs)\n {\n \tfree_commit_list(revs->commits);\n \tobject_array_clear(&revs->pending);\n+\trelease_revisions_cmdline(&revs->cmdline);\n \trelease_revisions_mailmap(revs->mailmap);\n }\n \ndiff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh\nindex 2fde2353fd3..ea28971e8ee 100644\n--- a/t/lib-git-svn.sh\n+++ b/t/lib-git-svn.sh\n@@ -1,3 +1,7 @@\n+if test -z \"$TEST_FAILS_SANITIZE_LEAK\"\n+then\n+\tTEST_PASSES_SANITIZE_LEAK=true\n+fi\n . ./test-lib.sh\n \n if test -n \"$NO_SVN_TESTS\"\ndiff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh\nindex 8e215867b8c..b9480c81781 100755\n--- a/t/t0062-revision-walking.sh\n+++ b/t/t0062-revision-walking.sh\n@@ -5,6 +5,7 @@\n \n test_description='Test revision walking api'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n cat >run_twice_expected <<-EOF\ndiff --git a/t/t0101-at-syntax.sh b/t/t0101-at-syntax.sh\nindex a1998b558f9..878aadd64c9 100755\n--- a/t/t0101-at-syntax.sh\n+++ b/t/t0101-at-syntax.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='various @{whatever} syntax tests'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh\nindex bc89371f534..e8a58b15897 100755\n--- a/t/t1060-object-corruption.sh\n+++ b/t/t1060-object-corruption.sh\n@@ -1,6 +1,7 @@\n #!/bin/sh\n \n test_description='see how we handle various forms of corruption'\n+\n . ./test-lib.sh\n \n # convert \"1234abcd\" to \".git/objects/12/34abcd\"\ndiff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh\nindex eac193757bf..bc9b791d1b9 100755\n--- a/t/t3303-notes-subtrees.sh\n+++ b/t/t3303-notes-subtrees.sh\n@@ -5,6 +5,7 @@ test_description='Test commit notes organized in subtrees'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n number_of_commits=100\ndiff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh\nindex 9976d787f47..64a9915761a 100755\n--- a/t/t3305-notes-fanout.sh\n+++ b/t/t3305-notes-fanout.sh\n@@ -2,6 +2,7 @@\n \n test_description='Test that adding/removing many notes triggers automatic fanout restructuring'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n path_has_fanout() {\ndiff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh\nindex cde3562e3a6..7b4607d72f2 100755\n--- a/t/t3408-rebase-multi-line.sh\n+++ b/t/t3408-rebase-multi-line.sh\n@@ -5,6 +5,7 @@ test_description='rebasing a commit with multi-line first paragraph.'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh\nindex 295da987cce..40164ae07d2 100755\n--- a/t/t4027-diff-submodule.sh\n+++ b/t/t4027-diff-submodule.sh\n@@ -2,6 +2,7 @@\n \n test_description='difference in submodules'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-diff.sh\n \ndiff --git a/t/t4128-apply-root.sh b/t/t4128-apply-root.sh\nindex f6db5a79dd9..ed94c90204e 100755\n--- a/t/t4128-apply-root.sh\n+++ b/t/t4128-apply-root.sh\n@@ -2,6 +2,7 @@\n \n test_description='apply same filename'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh\nindex 0244888a5a7..30a219894bb 100755\n--- a/t/t4212-log-corrupt.sh\n+++ b/t/t4212-log-corrupt.sh\n@@ -2,6 +2,7 @@\n \n test_description='git log with invalid commit headers'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh\nindex 320d26796d2..c100a809c5e 100755\n--- a/t/t5515-fetch-merge-logic.sh\n+++ b/t/t5515-fetch-merge-logic.sh\n@@ -14,6 +14,7 @@ export GIT_TEST_PROTOCOL_VERSION\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n build_script () {\ndiff --git a/t/t5518-fetch-exit-status.sh b/t/t5518-fetch-exit-status.sh\nindex 5c4ac2556e7..c13120088fa 100755\n--- a/t/t5518-fetch-exit-status.sh\n+++ b/t/t5518-fetch-exit-status.sh\n@@ -8,6 +8,7 @@ test_description='fetch exit status test'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh\nindex b95a0212adf..162cf50778d 100755\n--- a/t/t6002-rev-list-bisect.sh\n+++ b/t/t6002-rev-list-bisect.sh\n@@ -4,6 +4,7 @@\n #\n test_description='Tests git rev-list --bisect functionality'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-t6000.sh # t6xxx specific functions\n \ndiff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh\nindex 24d1836f417..1f7d7dd20c1 100755\n--- a/t/t6003-rev-list-topo-order.sh\n+++ b/t/t6003-rev-list-topo-order.sh\n@@ -5,6 +5,7 @@\n \n test_description='Tests git rev-list --topo-order functionality'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-t6000.sh # t6xxx specific functions\n \ndiff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh\nindex e960049f647..0729f800c3c 100755\n--- a/t/t6005-rev-list-count.sh\n+++ b/t/t6005-rev-list-count.sh\n@@ -2,6 +2,7 @@\n \n test_description='git rev-list --max-count and --skip test'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh\nindex 24b34add833..e1abc5c2b32 100755\n--- a/t/t6018-rev-list-glob.sh\n+++ b/t/t6018-rev-list-glob.sh\n@@ -5,6 +5,7 @@ test_description='rev-list/rev-parse --glob'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n commit () {\ndiff --git a/t/t6100-rev-list-in-order.sh b/t/t6100-rev-list-in-order.sh\nindex e934bc239c5..88ed7bd75a7 100755\n--- a/t/t6100-rev-list-in-order.sh\n+++ b/t/t6100-rev-list-in-order.sh\n@@ -2,6 +2,7 @@\n \n test_description='rev-list testing in-commit-order'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup a commit history with trees, blobs' '\ndiff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh\nindex fea41b3c360..7c5b847f584 100755\n--- a/t/t9100-git-svn-basic.sh\n+++ b/t/t9100-git-svn-basic.sh\n@@ -8,6 +8,7 @@ test_description='git svn basic tests'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n prepare_utf8_locale\ndiff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh\nindex 8b5681dd68a..d043e80fc34 100755\n--- a/t/t9101-git-svn-props.sh\n+++ b/t/t9101-git-svn-props.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn property tests'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n mkdir import\ndiff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh\nindex c7d8e0bf00f..5cf2ef4b8b0 100755\n--- a/t/t9104-git-svn-follow-parent.sh\n+++ b/t/t9104-git-svn-follow-parent.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn fetching'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh\nindex aec45bca3b7..3cab0b9720a 100755\n--- a/t/t9106-git-svn-commit-diff-clobber.sh\n+++ b/t/t9106-git-svn-commit-diff-clobber.sh\n@@ -2,6 +2,8 @@\n #\n # Copyright (c) 2006 Eric Wong\n test_description='git svn commit-diff clobber'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh\nindex 743fbe1fe46..419f055721d 100755\n--- a/t/t9115-git-svn-dcommit-funky-renames.sh\n+++ b/t/t9115-git-svn-dcommit-funky-renames.sh\n@@ -5,6 +5,7 @@\n \n test_description='git svn dcommit can commit renames of files with ugly names'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'load repository with strange names' '\ndiff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh\nindex 0a9f1ef366d..34f6c80dea3 100755\n--- a/t/t9116-git-svn-log.sh\n+++ b/t/t9116-git-svn-log.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn log tests'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup repository and import' '\ndiff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh\nindex 9e8fe38e7ef..527ba3d2932 100755\n--- a/t/t9122-git-svn-author.sh\n+++ b/t/t9122-git-svn-author.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='git svn authorship'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup svn repository' '\ndiff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh\nindex 2e4789d061f..90b1b30dde5 100755\n--- a/t/t9127-git-svn-partial-rebuild.sh\n+++ b/t/t9127-git-svn-partial-rebuild.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn partial-rebuild tests'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize svnrepo' '\ndiff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh\nindex 01e1e8a8f76..185248a4cd7 100755\n--- a/t/t9129-git-svn-i18n-commitencoding.sh\n+++ b/t/t9129-git-svn-i18n-commitencoding.sh\n@@ -4,6 +4,7 @@\n \n test_description='git svn honors i18n.commitEncoding in config'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n compare_git_head_with () {\ndiff --git a/t/t9132-git-svn-broken-symlink.sh b/t/t9132-git-svn-broken-symlink.sh\nindex aeceffaf7b0..4d8d0584b79 100755\n--- a/t/t9132-git-svn-broken-symlink.sh\n+++ b/t/t9132-git-svn-broken-symlink.sh\n@@ -2,6 +2,7 @@\n \n test_description='test that git handles an svn repository with empty symlinks'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n test_expect_success 'load svn dumpfile' '\n \tsvnadmin load \"$rawsvnrepo\" <<EOF\ndiff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh\nindex 22d80b0be2b..b7f756b2b7f 100755\n--- a/t/t9139-git-svn-non-utf8-commitencoding.sh\n+++ b/t/t9139-git-svn-non-utf8-commitencoding.sh\n@@ -4,6 +4,7 @@\n \n test_description='git svn refuses to dcommit non-UTF8 messages'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n # ISO-2022-JP can pass for valid UTF-8, so skipping that in this test\ndiff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh\nindex 80cb55fee70..79c26ed69c1 100755\n--- a/t/t9146-git-svn-empty-dirs.sh\n+++ b/t/t9146-git-svn-empty-dirs.sh\n@@ -3,6 +3,8 @@\n # Copyright (c) 2009 Eric Wong\n \n test_description='git svn creates empty directories'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9148-git-svn-propset.sh b/t/t9148-git-svn-propset.sh\nindex aebb28995e5..6cc76a07b39 100755\n--- a/t/t9148-git-svn-propset.sh\n+++ b/t/t9148-git-svn-propset.sh\n@@ -5,6 +5,7 @@\n \n test_description='git svn propset tests'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup propset via import' '\ndiff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh\nindex c93a5beab25..85221d439bd 100755\n--- a/t/t9151-svn-mergeinfo.sh\n+++ b/t/t9151-svn-mergeinfo.sh\n@@ -5,6 +5,7 @@\n \n test_description='git-svn svn mergeinfo properties'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'load svn dump' \"\ndiff --git a/t/t9160-git-svn-preserve-empty-dirs.sh b/t/t9160-git-svn-preserve-empty-dirs.sh\nindex 36c6b1a12ff..9cf7a1427ab 100755\n--- a/t/t9160-git-svn-preserve-empty-dirs.sh\n+++ b/t/t9160-git-svn-preserve-empty-dirs.sh\n@@ -9,6 +9,7 @@ This test uses git to clone a Subversion repository that contains empty\n directories, and checks that corresponding directories are created in the\n local Git repository with placeholder files.'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n GIT_REPO=git-svn-repo\ndiff --git a/t/t9162-git-svn-dcommit-interactive.sh b/t/t9162-git-svn-dcommit-interactive.sh\nindex e38d9fa37b5..e2aa8ed88a9 100755\n--- a/t/t9162-git-svn-dcommit-interactive.sh\n+++ b/t/t9162-git-svn-dcommit-interactive.sh\n@@ -3,6 +3,8 @@\n # Copyright (c) 2011 Frédéric Heitzmann\n \n test_description='git svn dcommit --interactive series'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9164-git-svn-dcommit-concurrent.sh b/t/t9164-git-svn-dcommit-concurrent.sh\nindex 8466269bf50..1465156072e 100755\n--- a/t/t9164-git-svn-dcommit-concurrent.sh\n+++ b/t/t9164-git-svn-dcommit-concurrent.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='concurrent git svn dcommit'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n \ndiff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh\nindex 32814e75df5..c900231079c 100755\n--- a/t/t9501-gitweb-standalone-http-status.sh\n+++ b/t/t9501-gitweb-standalone-http-status.sh\n@@ -13,6 +13,7 @@ code and message.'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./lib-gitweb.sh\n \n #\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452946","messageId":"patch-v5-27.27-ec2c77ce869-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 27/27] revisions API: add a TODO for diff_free(&revs->diffopt)","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:41Z","receivedAt":"2022-04-02T10:51:21Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a TODO comment indicating that we should release \"diffopt\" in\nrelease_revisions(). In a preceding commit we started releasing the\n\"pruning\" member of the same type, but handling \"diffopt\" will require\nus to untangle the \"no_free\" conditions I added in e900d494dcf (diff:\nadd an API for deferred freeing, 2021-02-11).\n\nLet's leave a TODO comment to that effect, and so that we don't forget\nrefactor code that was changed to use release_revisions() in earlier\ncommits to stop using the \"diffopt\" member after a call to\nrelease_revisions(). This works currently, but would become a logic\nerror as soon as we started freeing \"diffopt\". Doing that change now\ndoesn't harm anything, and future-proofs us against a later change to\nrelease_revisions().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n diff-lib.c  | 4 +++-\n revision.c  | 1 +\n wt-status.c | 6 ++++--\n 3 files changed, 8 insertions(+), 3 deletions(-)\n\ndiff --git a/diff-lib.c b/diff-lib.c\nindex 298265e5b54..7eb66a417aa 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -651,6 +651,7 @@ int index_differs_from(struct repository *r,\n {\n \tstruct rev_info rev;\n \tstruct setup_revision_opt opt;\n+\tunsigned has_changes;\n \n \trepo_init_revisions(r, &rev, NULL);\n \tmemset(&opt, 0, sizeof(opt));\n@@ -662,8 +663,9 @@ int index_differs_from(struct repository *r,\n \t\tdiff_flags_or(&rev.diffopt.flags, flags);\n \trev.diffopt.ita_invisible_in_index = ita_invisible_in_index;\n \trun_diff_index(&rev, 1);\n+\thas_changes = rev.diffopt.flags.has_changes;\n \trelease_revisions(&rev);\n-\treturn (rev.diffopt.flags.has_changes != 0);\n+\treturn (has_changes != 0);\n }\n \n static struct strbuf *idiff_prefix_cb(struct diff_options *opt, void *data)\ndiff --git a/revision.c b/revision.c\nindex 28c3c3ff9b4..812bf709bc3 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2958,6 +2958,7 @@ void release_revisions(struct rev_info *revs)\n \tdate_mode_release(&revs->date_mode);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n+\t/* TODO (need to handle \"no_free\"): diff_free(&revs->diffopt) */\n \tdiff_free(&revs->pruning);\n \treflog_walk_info_release(revs->reflog_info);\n \trelease_revisions_topo_walk_info(revs->topo_walk_info);\ndiff --git a/wt-status.c b/wt-status.c\nindex 61e0c1022f5..102d904adcb 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -2545,8 +2545,9 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)\n \trev_info.diffopt.flags.quick = 1;\n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_files(&rev_info, 0);\n+\tresult = diff_result_code(&rev_info.diffopt, result);\n \trelease_revisions(&rev_info);\n-\treturn diff_result_code(&rev_info.diffopt, result);\n+\treturn result;\n }\n \n /**\n@@ -2578,8 +2579,9 @@ int has_uncommitted_changes(struct repository *r,\n \n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_index(&rev_info, 1);\n+\tresult = diff_result_code(&rev_info.diffopt, result);\n \trelease_revisions(&rev_info);\n-\treturn diff_result_code(&rev_info.diffopt, result);\n+\treturn result;\n }\n \n /**\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452947","messageId":"patch-v5-22.27-d96642d3502-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 22/27] revisions API: clear \"boundary_commits\" in release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:36Z","receivedAt":"2022-04-02T10:51:27Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Clear the \"boundary_commits\" object_array in release_revisions(). This\nmakes a few more tests pass under SANITIZE=leak, including\n\"t/t4126-apply-empty.sh\" which started failed as an UNLEAK() in\ncmd_format_patch() was removed in a preceding commit.\n\nThis also re-marks the various tests relying on \"git format-patch\" as\npassing under \"SANITIZE=leak\", in the preceding \"revisions API users:\nuse release_revisions() in builtin/log.c\" commit those were marked as\nfailing as we removed the UNLEAK(rev) from cmd_format_patch() in\n\"builtin/log.c\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c                           | 1 +\n t/t4021-format-patch-numbered.sh     | 1 +\n t/t4028-format-patch-mime-headers.sh | 2 ++\n t/t4036-format-patch-signer-mime.sh  | 1 +\n t/t4122-apply-symlink-inside.sh      | 1 +\n t/t4126-apply-empty.sh               | 1 +\n t/t6110-rev-list-sparse.sh           | 1 +\n t/t9001-send-email.sh                | 1 +\n t/t9116-git-svn-log.sh               | 1 -\n 9 files changed, 9 insertions(+), 1 deletion(-)\n\ndiff --git a/revision.c b/revision.c\nindex 9fb18924cbf..40f59c441f7 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2949,6 +2949,7 @@ void release_revisions(struct rev_info *revs)\n {\n \tfree_commit_list(revs->commits);\n \tobject_array_clear(&revs->pending);\n+\tobject_array_clear(&revs->boundary_commits);\n \trelease_revisions_cmdline(&revs->cmdline);\n \tlist_objects_filter_release(&revs->filter);\n \tclear_pathspec(&revs->prune_data);\ndiff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh\nindex 9be65fd4440..1219aa226dc 100755\n--- a/t/t4021-format-patch-numbered.sh\n+++ b/t/t4021-format-patch-numbered.sh\n@@ -5,6 +5,7 @@\n \n test_description='Format-patch numbering options'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4028-format-patch-mime-headers.sh b/t/t4028-format-patch-mime-headers.sh\nindex 204ba673cb5..60cb819c42e 100755\n--- a/t/t4028-format-patch-mime-headers.sh\n+++ b/t/t4028-format-patch-mime-headers.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='format-patch mime headers and extra headers do not conflict'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'create commit with utf-8 body' '\ndiff --git a/t/t4036-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh\nindex 98d9713d8b2..48655bcc789 100755\n--- a/t/t4036-format-patch-signer-mime.sh\n+++ b/t/t4036-format-patch-signer-mime.sh\n@@ -2,6 +2,7 @@\n \n test_description='format-patch -s should force MIME encoding as needed'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh\nindex aa52de401b9..96965373036 100755\n--- a/t/t4122-apply-symlink-inside.sh\n+++ b/t/t4122-apply-symlink-inside.sh\n@@ -4,6 +4,7 @@ test_description='apply to deeper directory without getting fooled with symlink'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh\nindex 66a7ba8ab8f..ece9fae207d 100755\n--- a/t/t4126-apply-empty.sh\n+++ b/t/t4126-apply-empty.sh\n@@ -2,6 +2,7 @@\n \n test_description='apply empty'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t6110-rev-list-sparse.sh b/t/t6110-rev-list-sparse.sh\nindex 13c1da53528..ddefc7f24ee 100755\n--- a/t/t6110-rev-list-sparse.sh\n+++ b/t/t6110-rev-list-sparse.sh\n@@ -4,6 +4,7 @@ test_description='operations that cull histories in unusual ways'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh\nindex 42694fe5841..01c74b8b075 100755\n--- a/t/t9001-send-email.sh\n+++ b/t/t9001-send-email.sh\n@@ -4,6 +4,7 @@ test_description='git send-email'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # May be altered later in the test\ndiff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh\nindex 34f6c80dea3..d74d7b2de68 100755\n--- a/t/t9116-git-svn-log.sh\n+++ b/t/t9116-git-svn-log.sh\n@@ -5,7 +5,6 @@\n \n test_description='git svn log tests'\n \n-TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup repository and import' '\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452948","messageId":"patch-v5-23.27-0e9745d9ee1-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 23/27] revisions API: release \"reflog_info\" in release revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:37Z","receivedAt":"2022-04-02T10:51:28Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a missing reflog_walk_info_release() to \"reflog-walk.c\" and use it\nin release_revisions().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n reflog-walk.c            | 24 +++++++++++++++++++++++-\n reflog-walk.h            |  1 +\n revision.c               |  1 +\n t/t0100-previous.sh      |  1 +\n t/t1401-symbolic-ref.sh  |  2 ++\n t/t1411-reflog-show.sh   |  1 +\n t/t1412-reflog-loop.sh   |  2 ++\n t/t1415-worktree-refs.sh |  1 +\n 8 files changed, 32 insertions(+), 1 deletion(-)\n\ndiff --git a/reflog-walk.c b/reflog-walk.c\nindex 8ac4b284b6b..7aa6595a51f 100644\n--- a/reflog-walk.c\n+++ b/reflog-walk.c\n@@ -8,7 +8,7 @@\n \n struct complete_reflogs {\n \tchar *ref;\n-\tconst char *short_ref;\n+\tchar *short_ref;\n \tstruct reflog_info {\n \t\tstruct object_id ooid, noid;\n \t\tchar *email;\n@@ -51,9 +51,16 @@ static void free_complete_reflog(struct complete_reflogs *array)\n \t}\n \tfree(array->items);\n \tfree(array->ref);\n+\tfree(array->short_ref);\n \tfree(array);\n }\n \n+static void complete_reflogs_clear(void *util, const char *str)\n+{\n+\tstruct complete_reflogs *array = util;\n+\tfree_complete_reflog(array);\n+}\n+\n static struct complete_reflogs *read_complete_reflog(const char *ref)\n {\n \tstruct complete_reflogs *reflogs =\n@@ -116,6 +123,21 @@ void init_reflog_walk(struct reflog_walk_info **info)\n \t(*info)->complete_reflogs.strdup_strings = 1;\n }\n \n+void reflog_walk_info_release(struct reflog_walk_info *info)\n+{\n+\tsize_t i;\n+\n+\tif (!info)\n+\t\treturn;\n+\n+\tfor (i = 0; i < info->nr; i++)\n+\t\tfree(info->logs[i]);\n+\tstring_list_clear_func(&info->complete_reflogs,\n+\t\t\t       complete_reflogs_clear);\n+\tfree(info->logs);\n+\tfree(info);\n+}\n+\n int add_reflog_for_walk(struct reflog_walk_info *info,\n \t\tstruct commit *commit, const char *name)\n {\ndiff --git a/reflog-walk.h b/reflog-walk.h\nindex e9e00ffd479..8076f10d9fb 100644\n--- a/reflog-walk.h\n+++ b/reflog-walk.h\n@@ -8,6 +8,7 @@ struct reflog_walk_info;\n struct date_mode;\n \n void init_reflog_walk(struct reflog_walk_info **info);\n+void reflog_walk_info_release(struct reflog_walk_info *info);\n int add_reflog_for_walk(struct reflog_walk_info *info,\n \t\t\tstruct commit *commit, const char *name);\n void show_reflog_message(struct reflog_walk_info *info, int,\ndiff --git a/revision.c b/revision.c\nindex 40f59c441f7..e972addd8fc 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2955,6 +2955,7 @@ void release_revisions(struct rev_info *revs)\n \tclear_pathspec(&revs->prune_data);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n+\treflog_walk_info_release(revs->reflog_info);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\ndiff --git a/t/t0100-previous.sh b/t/t0100-previous.sh\nindex 69beb59f627..a16cc3d2983 100755\n--- a/t/t0100-previous.sh\n+++ b/t/t0100-previous.sh\n@@ -5,6 +5,7 @@ test_description='previous branch syntax @{-n}'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'branch -d @{-1}' '\ndiff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh\nindex 132a1b885ac..9fb0b90f252 100755\n--- a/t/t1401-symbolic-ref.sh\n+++ b/t/t1401-symbolic-ref.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='basic symbolic-ref tests'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # If the tests munging HEAD fail, they can break detection of\ndiff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh\nindex 0bb319b944a..3770ceffafd 100755\n--- a/t/t1411-reflog-show.sh\n+++ b/t/t1411-reflog-show.sh\n@@ -4,6 +4,7 @@ test_description='Test reflog display routines'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t1412-reflog-loop.sh b/t/t1412-reflog-loop.sh\nindex 977603f7f1c..ff30874f940 100755\n--- a/t/t1412-reflog-loop.sh\n+++ b/t/t1412-reflog-loop.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='reflog walk shows repeated commits again'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup commits' '\ndiff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh\nindex a3e6ea08088..3b531842dd4 100755\n--- a/t/t1415-worktree-refs.sh\n+++ b/t/t1415-worktree-refs.sh\n@@ -2,6 +2,7 @@\n \n test_description='per-worktree refs'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452949","messageId":"patch-v5-20.27-ebdcdc88d79-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 20/27] revisions API: have release_revisions() release \"grep_filter\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:34Z","receivedAt":"2022-04-02T10:52:07Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"grep_filter\" in the \"struct rev_info\".This allows us to mark a test\nas passing under \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c               | 1 +\n t/t9151-svn-mergeinfo.sh | 1 -\n 2 files changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/revision.c b/revision.c\nindex 2484e14d26b..2c23de60093 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2952,6 +2952,7 @@ void release_revisions(struct rev_info *revs)\n \trelease_revisions_cmdline(&revs->cmdline);\n \tlist_objects_filter_release(&revs->filter);\n \trelease_revisions_mailmap(revs->mailmap);\n+\tfree_grep_patterns(&revs->grep_filter);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\ndiff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh\nindex 85221d439bd..c93a5beab25 100755\n--- a/t/t9151-svn-mergeinfo.sh\n+++ b/t/t9151-svn-mergeinfo.sh\n@@ -5,7 +5,6 @@\n \n test_description='git-svn svn mergeinfo properties'\n \n-TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'load svn dump' \"\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452950","messageId":"patch-v5-24.27-c7f3e92a8d1-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:38Z","receivedAt":"2022-04-02T10:52:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Call diff_free() on the \"pruning\" member of \"struct rev_info\".  Doing\nso makes several tests pass under SANITIZE=leak.\n\nThis was also the last missing piece that allows us to remove the\nUNLEAK() in \"cmd_diff\" and \"cmd_diff_index\", which allows us to use\nthose commands as a canary for general leaks in the revisions API. See\n[1] for further rationale, and 886e1084d78 (builtin/: add UNLEAKs,\n2017-10-01) for the commit that added the UNLEAK() there.\n\n1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/diff-index.c                | 1 -\n builtin/diff.c                      | 1 -\n revision.c                          | 1 +\n t/t1001-read-tree-m-2way.sh         | 1 +\n t/t1002-read-tree-m-u-2way.sh       | 1 +\n t/t2200-add-update.sh               | 1 +\n t/t4039-diff-assume-unchanged.sh    | 1 +\n t/t4206-log-follow-harder-copies.sh | 2 ++\n t/t6131-pathspec-icase.sh           | 2 ++\n 9 files changed, 9 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/diff-index.c b/builtin/diff-index.c\nindex 3a83183c312..7d158af6b6d 100644\n--- a/builtin/diff-index.c\n+++ b/builtin/diff-index.c\n@@ -70,7 +70,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)\n \t\treturn -1;\n \t}\n \tresult = run_diff_index(&rev, option);\n-\tUNLEAK(rev);\n \tresult = diff_result_code(&rev.diffopt, result);\n \trelease_revisions(&rev);\n \treturn result;\ndiff --git a/builtin/diff.c b/builtin/diff.c\nindex dd48336da56..f539132ac68 100644\n--- a/builtin/diff.c\n+++ b/builtin/diff.c\n@@ -594,7 +594,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \tresult = diff_result_code(&rev.diffopt, result);\n \tif (1 < rev.diffopt.skip_stat_unmatch)\n \t\trefresh_index_quietly();\n-\tUNLEAK(rev);\n \trelease_revisions(&rev);\n \tUNLEAK(ent);\n \tUNLEAK(blob);\ndiff --git a/revision.c b/revision.c\nindex e972addd8fc..8bc777da828 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2955,6 +2955,7 @@ void release_revisions(struct rev_info *revs)\n \tclear_pathspec(&revs->prune_data);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n+\tdiff_free(&revs->pruning);\n \treflog_walk_info_release(revs->reflog_info);\n }\n \ndiff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh\nindex 0710b1fb1e9..516a6112fdc 100755\n--- a/t/t1001-read-tree-m-2way.sh\n+++ b/t/t1001-read-tree-m-2way.sh\n@@ -21,6 +21,7 @@ In the test, these paths are used:\n \tyomin   - not in H or M\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-read-tree.sh\n \ndiff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh\nindex 46cbd5514a6..bd5313caec9 100755\n--- a/t/t1002-read-tree-m-u-2way.sh\n+++ b/t/t1002-read-tree-m-u-2way.sh\n@@ -9,6 +9,7 @@ This is identical to t1001, but uses -u to update the work tree as well.\n \n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-read-tree.sh\n \ndiff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh\nindex 0c38f8e3569..be394f1131a 100755\n--- a/t/t2200-add-update.sh\n+++ b/t/t2200-add-update.sh\n@@ -14,6 +14,7 @@ only the updates to dir/sub.\n Also tested are \"git add -u\" without limiting, and \"git add -u\"\n without contents changes, and other conditions'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh\nindex 0eb0314a8b3..78090e6852d 100755\n--- a/t/t4039-diff-assume-unchanged.sh\n+++ b/t/t4039-diff-assume-unchanged.sh\n@@ -2,6 +2,7 @@\n \n test_description='diff with assume-unchanged entries'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # external diff has been tested in t4020-diff-external.sh\ndiff --git a/t/t4206-log-follow-harder-copies.sh b/t/t4206-log-follow-harder-copies.sh\nindex 4871a5dc92f..33ecf82c7f9 100755\n--- a/t/t4206-log-follow-harder-copies.sh\n+++ b/t/t4206-log-follow-harder-copies.sh\n@@ -6,6 +6,8 @@\n test_description='Test --follow should always find copies hard in git log.\n \n '\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-diff.sh\n \ndiff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh\nindex 39fc3f6769b..770cce026cb 100755\n--- a/t/t6131-pathspec-icase.sh\n+++ b/t/t6131-pathspec-icase.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='test case insensitive pathspec limiting'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n if test_have_prereq CASE_INSENSITIVE_FS\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452951","messageId":"patch-v5-26.27-fbf233ec768-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 26/27] revisions API: have release_revisions() release \"topo_walk_info\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:40Z","receivedAt":"2022-04-02T10:52:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor the existing reset_topo_walk() into a thin wrapper for a\nrelease_revisions_topo_walk_info() + resetting the member to \"NULL\",\nand call release_revisions_topo_walk_info() from release_revisions().\n\nThis fixes memory leaks that have been with us ever since\n\"topo_walk_info\" was added to revision.[ch] in\nf0d9cc4196a (revision.c: begin refactoring --topo-order logic,\n2018-11-01).\n\nDue to various other leaks this makes no tests pass in their entirety,\nbut e.g. before this running this on git.git:\n\n    ./git -P log --pretty=tformat:\"%P   %H | %s\" --parents --full-history --topo-order -3 -- README.md\n\nWould report under SANITIZE=leak:\n\n    SUMMARY: LeakSanitizer: 531064 byte(s) leaked in 6 allocation(s).\n\nNow we'll free all of that memory.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 16 ++++++++++++----\n 1 file changed, 12 insertions(+), 4 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex ef98e4711f3..28c3c3ff9b4 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2945,6 +2945,8 @@ static void release_revisions_mailmap(struct string_list *mailmap)\n \tfree(mailmap);\n }\n \n+static void release_revisions_topo_walk_info(struct topo_walk_info *info);\n+\n void release_revisions(struct rev_info *revs)\n {\n \tfree_commit_list(revs->commits);\n@@ -2958,6 +2960,7 @@ void release_revisions(struct rev_info *revs)\n \tfree_grep_patterns(&revs->grep_filter);\n \tdiff_free(&revs->pruning);\n \treflog_walk_info_release(revs->reflog_info);\n+\trelease_revisions_topo_walk_info(revs->topo_walk_info);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n@@ -3470,17 +3473,22 @@ static void compute_indegrees_to_depth(struct rev_info *revs,\n \t\tindegree_walk_step(revs);\n }\n \n-static void reset_topo_walk(struct rev_info *revs)\n+static void release_revisions_topo_walk_info(struct topo_walk_info *info)\n {\n-\tstruct topo_walk_info *info = revs->topo_walk_info;\n-\n+\tif (!info)\n+\t\treturn;\n \tclear_prio_queue(&info->explore_queue);\n \tclear_prio_queue(&info->indegree_queue);\n \tclear_prio_queue(&info->topo_queue);\n \tclear_indegree_slab(&info->indegree);\n \tclear_author_date_slab(&info->author_date);\n+\tfree(info);\n+}\n \n-\tFREE_AND_NULL(revs->topo_walk_info);\n+static void reset_topo_walk(struct rev_info *revs)\n+{\n+\trelease_revisions_topo_walk_info(revs->topo_walk_info);\n+\trevs->topo_walk_info = NULL;\n }\n \n static void init_topo_walk(struct rev_info *revs)\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452952","messageId":"patch-v5-25.27-d428d752462-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 25/27] revisions API: have release_revisions() release \"date_mode\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:39Z","receivedAt":"2022-04-02T10:52:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"date_mode\" in the \"struct ref_info\".\n\nThis uses the date_mode_release() function added in 974c919d36d (date\nAPI: add and use a date_mode_release(), 2022-02-16). As that commit\nnotes \"t7004-tag.sh\" tests for the leaks that are being fixed\nhere. That test now fails \"only\" 44 tests, instead of the 46 it failed\nbefore this change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/revision.c b/revision.c\nindex 8bc777da828..ef98e4711f3 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2953,6 +2953,7 @@ void release_revisions(struct rev_info *revs)\n \trelease_revisions_cmdline(&revs->cmdline);\n \tlist_objects_filter_release(&revs->filter);\n \tclear_pathspec(&revs->prune_data);\n+\tdate_mode_release(&revs->date_mode);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n \tdiff_free(&revs->pruning);\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452953","messageId":"patch-v5-21.27-84c8714571a-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 21/27] revisions API: have release_revisions() release \"prune_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:35Z","receivedAt":"2022-04-02T10:52:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"prune_data\" in the \"struct rev_info\". This means that any code that\ncalls \"release_revisions()\" already can get rid of adjacent calls to\nclear_pathspec().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n add-interactive.c | 2 --\n builtin/add.c     | 1 -\n builtin/stash.c   | 2 --\n diff-lib.c        | 1 -\n revision.c        | 1 +\n wt-status.c       | 2 --\n 6 files changed, 1 insertion(+), 8 deletions(-)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex 54cdfc82017..6047e8f6489 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -568,8 +568,6 @@ static int get_modified_files(struct repository *r,\n \t\t\trun_diff_files(&rev, 0);\n \t\t}\n \n-\t\tif (ps)\n-\t\t\tclear_pathspec(&rev.prune_data);\n \t\trelease_revisions(&rev);\n \t}\n \thashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 115a26ea633..fc729e14c17 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -142,7 +142,6 @@ int add_files_to_cache(const char *prefix,\n \trev.diffopt.flags.override_submodule_config = 1;\n \trev.max_count = 0; /* do not compare unmerged paths with stage #2 */\n \trun_diff_files(&rev, DIFF_RACY_IS_MODIFIED);\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n \treturn !!data.add_errors;\n }\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex fce4393d123..16aad4b4d35 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1064,7 +1064,6 @@ static int check_changes_tracked_files(const struct pathspec *ps)\n \t}\n \n done:\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n \treturn ret;\n }\n@@ -1276,7 +1275,6 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps\n \n done:\n \tdiscard_index(&istate);\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n \tstrbuf_release(&diff_output);\n \tremove_path(stash_index_path.buf);\ndiff --git a/diff-lib.c b/diff-lib.c\nindex 0f16281253f..298265e5b54 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -641,7 +641,6 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)\n \n \tif (diff_cache(&revs, tree_oid, NULL, 1))\n \t\texit(128);\n-\tclear_pathspec(&revs.prune_data);\n \trelease_revisions(&revs);\n \treturn 0;\n }\ndiff --git a/revision.c b/revision.c\nindex 2c23de60093..9fb18924cbf 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2951,6 +2951,7 @@ void release_revisions(struct rev_info *revs)\n \tobject_array_clear(&revs->pending);\n \trelease_revisions_cmdline(&revs->cmdline);\n \tlist_objects_filter_release(&revs->filter);\n+\tclear_pathspec(&revs->prune_data);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n }\ndiff --git a/wt-status.c b/wt-status.c\nindex a14fad1e03a..61e0c1022f5 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -616,7 +616,6 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)\n \trev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;\n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_files(&rev, 0);\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n }\n \n@@ -664,7 +663,6 @@ static void wt_status_collect_changes_index(struct wt_status *s)\n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_index(&rev, 1);\n \trelease_revisions(&rev);\n-\tclear_pathspec(&rev.prune_data);\n }\n \n static int add_file_to_list(const struct object_id *oid,\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452954","messageId":"patch-v5-19.27-e6e87ce3608-20220402T102002Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v5 19/27] revisions API: have release_revisions() release \"filter\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-02T10:49:33Z","receivedAt":"2022-04-02T10:52:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"filter\" in the \"struct rev_info\". This in combination with a\npreceding change to free \"cmdline\" means that we can mark another set\nof tests as passing under \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nThe \"filter\" member was added recently in ffaa137f646 (revision: put\nobject filter into struct rev_info, 2022-03-09), and this fixes leaks\nintruded in the subsequent leak 7940941de1f (pack-objects: use\nrev.filter when possible, 2022-03-09) and 105c6f14ad3 (bundle: parse\nfilter capability, 2022-03-09).\n\nThe \"builtin/pack-objects.c\" leak in 7940941de1f was effectively with\nus already, but the variable was referred to by a \"static\" file-scoped\nvariable. The \"bundle.c \" leak in 105c6f14ad3 was newly introduced\nwith the new \"filter\" feature for bundles.\n\nThe \"t5600-clone-fail-cleanup.sh\" change here to add\n\"TEST_PASSES_SANITIZE_LEAK=true\" is one of the cases where\nrun-command.c in not carrying the abort() exit code upwards would have\nhad that test passing before, but now it *actually* passes[1]. We\nshould fix the lack of 1=1 mapping of SANITIZE=leak testing to actual\nleaks some other time, but it's an existing edge case, let's just mark\nthe really-passing test as passing for now.\n\n1. https://lore.kernel.org/git/220303.86fsnz5o9w.gmgdl@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c                         | 1 +\n t/t1060-object-corruption.sh       | 1 +\n t/t2015-checkout-unborn.sh         | 1 +\n t/t4207-log-decoration-colors.sh   | 1 +\n t/t5301-sliding-window.sh          | 2 ++\n t/t5313-pack-bounds-checks.sh      | 2 ++\n t/t5316-pack-delta-depth.sh        | 2 ++\n t/t5320-delta-islands.sh           | 2 ++\n t/t5322-pack-objects-sparse.sh     | 1 +\n t/t5506-remote-groups.sh           | 1 +\n t/t5513-fetch-track.sh             | 1 +\n t/t5532-fetch-proxy.sh             | 2 ++\n t/t5600-clone-fail-cleanup.sh      | 1 +\n t/t5900-repo-selection.sh          | 2 ++\n t/t6101-rev-parse-parents.sh       | 1 +\n t/t6114-keep-packs.sh              | 2 ++\n t/t7702-repack-cyclic-alternate.sh | 2 ++\n t/t9127-git-svn-partial-rebuild.sh | 1 -\n 18 files changed, 25 insertions(+), 1 deletion(-)\n\ndiff --git a/revision.c b/revision.c\nindex c81506fa22f..2484e14d26b 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2950,6 +2950,7 @@ void release_revisions(struct rev_info *revs)\n \tfree_commit_list(revs->commits);\n \tobject_array_clear(&revs->pending);\n \trelease_revisions_cmdline(&revs->cmdline);\n+\tlist_objects_filter_release(&revs->filter);\n \trelease_revisions_mailmap(revs->mailmap);\n }\n \ndiff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh\nindex e8a58b15897..5b8e47e346c 100755\n--- a/t/t1060-object-corruption.sh\n+++ b/t/t1060-object-corruption.sh\n@@ -2,6 +2,7 @@\n \n test_description='see how we handle various forms of corruption'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # convert \"1234abcd\" to \".git/objects/12/34abcd\"\ndiff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh\nindex a9721215fab..9425aae6395 100755\n--- a/t/t2015-checkout-unborn.sh\n+++ b/t/t2015-checkout-unborn.sh\n@@ -4,6 +4,7 @@ test_description='checkout from unborn branch'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh\nindex b8709424981..36ac6aff1e4 100755\n--- a/t/t4207-log-decoration-colors.sh\n+++ b/t/t4207-log-decoration-colors.sh\n@@ -8,6 +8,7 @@ test_description='Test for \"git log --decorate\" colors'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t5301-sliding-window.sh b/t/t5301-sliding-window.sh\nindex 76f9798ab95..3ccaaeb3977 100755\n--- a/t/t5301-sliding-window.sh\n+++ b/t/t5301-sliding-window.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='mmap sliding window tests'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success \\\ndiff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh\nindex 535313e4dc8..cc4cfaa9d37 100755\n--- a/t/t5313-pack-bounds-checks.sh\n+++ b/t/t5313-pack-bounds-checks.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='bounds-checking of access to mmapped on-disk file formats'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n clear_base () {\ndiff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh\nindex e9045009a11..eb4ef3dda4d 100755\n--- a/t/t5316-pack-delta-depth.sh\n+++ b/t/t5316-pack-delta-depth.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='pack-objects breaks long cross-pack delta chains'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # This mirrors a repeated push setup:\ndiff --git a/t/t5320-delta-islands.sh b/t/t5320-delta-islands.sh\nindex fea92a5777f..124d47603df 100755\n--- a/t/t5320-delta-islands.sh\n+++ b/t/t5320-delta-islands.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='exercise delta islands'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # returns true iff $1 is a delta based on $2\ndiff --git a/t/t5322-pack-objects-sparse.sh b/t/t5322-pack-objects-sparse.sh\nindex d39958c066d..770695c9278 100755\n--- a/t/t5322-pack-objects-sparse.sh\n+++ b/t/t5322-pack-objects-sparse.sh\n@@ -4,6 +4,7 @@ test_description='pack-objects object selection using sparse algorithm'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup repo' '\ndiff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh\nindex 8f150c0793e..5bac03ede81 100755\n--- a/t/t5506-remote-groups.sh\n+++ b/t/t5506-remote-groups.sh\n@@ -4,6 +4,7 @@ test_description='git remote group handling'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n mark() {\ndiff --git a/t/t5513-fetch-track.sh b/t/t5513-fetch-track.sh\nindex 65d1e05bd62..c46c4dbaefc 100755\n--- a/t/t5513-fetch-track.sh\n+++ b/t/t5513-fetch-track.sh\n@@ -2,6 +2,7 @@\n \n test_description='fetch follows remote-tracking branches correctly'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh\nindex 9c2798603b4..d664912799b 100755\n--- a/t/t5532-fetch-proxy.sh\n+++ b/t/t5532-fetch-proxy.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='fetching via git:// using core.gitproxy'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup remote repo' '\ndiff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh\nindex 34b3df40275..c814afa5656 100755\n--- a/t/t5600-clone-fail-cleanup.sh\n+++ b/t/t5600-clone-fail-cleanup.sh\n@@ -13,6 +13,7 @@ Unless the directory already exists, in which case we clean up only what we\n wrote.\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n corrupt_repo () {\ndiff --git a/t/t5900-repo-selection.sh b/t/t5900-repo-selection.sh\nindex 14e59c5b3e4..a84faac242d 100755\n--- a/t/t5900-repo-selection.sh\n+++ b/t/t5900-repo-selection.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='selecting remote repo in ambiguous cases'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n reset() {\ndiff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh\nindex c571fa51797..a3a41c7a3e4 100755\n--- a/t/t6101-rev-parse-parents.sh\n+++ b/t/t6101-rev-parse-parents.sh\n@@ -8,6 +8,7 @@ test_description='Test git rev-parse with different parent options'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_cmp_rev_output () {\ndiff --git a/t/t6114-keep-packs.sh b/t/t6114-keep-packs.sh\nindex 9239d8aa46d..44246f8a63e 100755\n--- a/t/t6114-keep-packs.sh\n+++ b/t/t6114-keep-packs.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='rev-list with .keep packs'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t7702-repack-cyclic-alternate.sh b/t/t7702-repack-cyclic-alternate.sh\nindex 93b74867ac8..f3cdb98eec2 100755\n--- a/t/t7702-repack-cyclic-alternate.sh\n+++ b/t/t7702-repack-cyclic-alternate.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='repack involving cyclic alternate'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh\nindex 90b1b30dde5..97f495bd49b 100755\n--- a/t/t9127-git-svn-partial-rebuild.sh\n+++ b/t/t9127-git-svn-partial-rebuild.sh\n@@ -5,7 +5,6 @@\n \n test_description='git svn partial-rebuild tests'\n \n-TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize svnrepo' '\n-- \n2.35.1.1585.gd85f8dcb745\n\n"},{"id":"452973","messageId":"2e75adb7-e52d-62a9-0304-4076816f82e1@gmail.com","threadId":"57525","inReplyTo":"patch-v5-14.27-ddc7402b054-20220402T102002Z-avarab@gmail.com","subject":"Re: [PATCH v5 14/27] revisions API users: use release_revisions() with UNLEAK()","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2022-04-03T09:27:06Z","receivedAt":"2022-04-03T09:27:20Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Ævar\n\n[continuing with v5 from where I left off with v4]\n\nOn 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:\n> Use a release_revisions() with those \"struct rev_list\" users which\n> already \"UNLEAK\" the struct. It may seem odd to simultaneously attempt\n> to free() memory, but also to explicitly ignore whether we have memory\n> leaks in the same.\n> \n> As explained in preceding commits this is being done to use the\n> built-in commands as a guinea pig for whether the release_revisions()\n> function works as expected, we'd like to test e.g. whether we segfault\n> as we change it. In subsequent commits we'll then remove these\n> UNLEAK() as the function is made to free the memory that caused us to\n> add them in the first place.\n\nI'm a bit confused by this, the previous commit argued in favor of \nremoving UNLEAK() so would could see the leaks and fix them, this is \nsaying we should hide the leaks.\n\nBest Wishes\n\nPhillip\n\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>   builtin/diff-index.c | 4 +++-\n>   builtin/diff.c       | 1 +\n>   2 files changed, 4 insertions(+), 1 deletion(-)\n> \n> diff --git a/builtin/diff-index.c b/builtin/diff-index.c\n> index 5fd23ab5b6c..3a83183c312 100644\n> --- a/builtin/diff-index.c\n> +++ b/builtin/diff-index.c\n> @@ -71,5 +71,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)\n>   \t}\n>   \tresult = run_diff_index(&rev, option);\n>   \tUNLEAK(rev);\n> -\treturn diff_result_code(&rev.diffopt, result);\n> +\tresult = diff_result_code(&rev.diffopt, result);\n> +\trelease_revisions(&rev);\n> +\treturn result;\n>   }\n> diff --git a/builtin/diff.c b/builtin/diff.c\n> index bb7fafca618..dd48336da56 100644\n> --- a/builtin/diff.c\n> +++ b/builtin/diff.c\n> @@ -595,6 +595,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n>   \tif (1 < rev.diffopt.skip_stat_unmatch)\n>   \t\trefresh_index_quietly();\n>   \tUNLEAK(rev);\n> +\trelease_revisions(&rev);\n>   \tUNLEAK(ent);\n>   \tUNLEAK(blob);\n>   \treturn result;\n"},{"id":"452974","messageId":"5044b964-0605-356c-3e03-1aad8cbbe619@gmail.com","threadId":"57525","inReplyTo":"patch-v5-17.27-ca659940ee5-20220402T102002Z-avarab@gmail.com","subject":"Re: [PATCH v5 17/27] revisions API: have release_revisions() release \"mailmap\"","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2022-04-03T09:33:45Z","receivedAt":"2022-04-03T09:34:01Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Ævar\n\nOn 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:\n> Extend the the release_revisions() function so that it frees the\n> \"mailmap\" in the \"struct rev_info\".\n> \n> The log family of functions now calls the clear_mailmap() function\n> added in fa8afd18e5a (revisions API: provide and use a\n> release_revisions(), 2021-09-19), allowing us to whitelist some tests\n> with \"TEST_PASSES_SANITIZE_LEAK=true\".\n> \n> Unfortunately having a pointer to a mailmap in \"struct rev_info\"\n> instead of an embedded member that we \"own\" get a bit messy, as can be\n> seen in the change to builtin/commit.c.\n> \n> When we free() this data we won't be able to tell apart a pointer to a\n> \"mailmap\" on the heap from one on the stack. As seen in\n> ea57bc0d41b (log: add --use-mailmap option, 2013-01-05) the \"log\"\n> family allocates it on the heap, but in the find_author_by_nickname()\n> code added in ea16794e430 (commit: search author pattern against\n> mailmap, 2013-08-23) we allocated it on the stack instead.\n> \n> Ideally we'd simply change that member to a \"struct string_list\n> mailmap\" and never free() the \"mailmap\" itself, but that would be a\n> much larger change to the revisions API.\n\nI agree it makes sense to leave that for now\n\n> We have code that needs to hand an existing \"mailmap\" to a \"struct\n> rev_info\", while we could change all of that, let's not go there\n> now.\n> \n> The complexity isn't in the ownership of the \"mailmap\" per-se, but\n> that various things assume a \"rev_info.mailmap == NULL\" means \"doesn't\n> want mailmap\", if we changed that to an init'd \"struct string_list\n> we'd need to carefully refactor things to change those assumptions.\n> \n> Let's instead always free() it, and simply declare that if you add\n> such a \"mailmap\" it must be allocated on the heap. Any modern libc\n> will correctly panic if we free() a stack variable, so this should be\n> safe going forward.\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>   builtin/commit.c                   | 5 ++---\n>   revision.c                         | 9 +++++++++\n>   t/t0056-git-C.sh                   | 1 +\n>   t/t3302-notes-index-expensive.sh   | 1 +\n>   t/t4055-diff-context.sh            | 1 +\n>   t/t4066-diff-emit-delay.sh         | 1 +\n>   t/t7008-filter-branch-null-sha1.sh | 1 +\n>   7 files changed, 16 insertions(+), 3 deletions(-)\n> \n> diff --git a/builtin/commit.c b/builtin/commit.c\n> index c7eda9bbb72..cd6cebcf8c8 100644\n> --- a/builtin/commit.c\n> +++ b/builtin/commit.c\n> @@ -1100,7 +1100,6 @@ static const char *find_author_by_nickname(const char *name)\n>   \tstruct rev_info revs;\n>   \tstruct commit *commit;\n>   \tstruct strbuf buf = STRBUF_INIT;\n> -\tstruct string_list mailmap = STRING_LIST_INIT_NODUP;\n>   \tconst char *av[20];\n>   \tint ac = 0;\n>   \n> @@ -1111,7 +1110,8 @@ static const char *find_author_by_nickname(const char *name)\n>   \tav[++ac] = buf.buf;\n>   \tav[++ac] = NULL;\n>   \tsetup_revisions(ac, av, &revs, NULL);\n> -\trevs.mailmap = &mailmap;\n> +\trevs.mailmap = xmalloc(sizeof(struct string_list));\n> +\tstring_list_init_nodup(revs.mailmap);\n\nThis is a common pattern in one of the previous patches, is it worth \nadding helpers to allocate and initialize a struct string_list? Maybe \nstring_list_new_nodup() and string_list_new_dup().\n\n>   \tread_mailmap(revs.mailmap);\n>   \n>   \tif (prepare_revision_walk(&revs))\n> @@ -1122,7 +1122,6 @@ static const char *find_author_by_nickname(const char *name)\n>   \t\tctx.date_mode.type = DATE_NORMAL;\n>   \t\tstrbuf_release(&buf);\n>   \t\tformat_commit_message(commit, \"%aN <%aE>\", &buf, &ctx);\n> -\t\tclear_mailmap(&mailmap);\n>   \t\trelease_revisions(&revs);\n>   \t\treturn strbuf_detach(&buf, NULL);\n>   \t}\n> diff --git a/revision.c b/revision.c\n> index 553f7de8250..622f0faecc4 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -2926,10 +2926,19 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n>   \treturn left;\n>   }\n>   \n> +static void release_revisions_mailmap(struct string_list *mailmap)\n> +{\n> +\tif (!mailmap)\n> +\t\treturn;\n> +\tclear_mailmap(mailmap);\n> +\tfree(mailmap);\n> +}\n\nIt's not a big issue but if there are no other users of this then it \ncould just go inside release_revisions, my impression is that this \nseries builds a collection of very small functions whose only caller is \nrelease_revisions()\n\nBest Wishes\n\nPhillip\n"},{"id":"452975","messageId":"02da5550-3137-b579-93c5-bf245a8cbc2a@gmail.com","threadId":"57525","inReplyTo":"patch-v5-18.27-7987c748ba4-20220402T102002Z-avarab@gmail.com","subject":"Re: [PATCH v5 18/27] revisions API: have release_revisions() release \"cmdline\"","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2022-04-03T12:54:34Z","receivedAt":"2022-04-03T12:54:52Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Ævar\n\nOn 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:\n> Extend the the release_revisions() function so that it frees the\n> \"cmdline\" in the \"struct rev_info\". This in combination with a\n> preceding change to free \"commits\" and \"mailmap\" means that we can\n> whitelist another test under \"TEST_PASSES_SANITIZE_LEAK=true\".\n> \n> There was a proposal in [1] to do away with xstrdup()-ing this\n> add_rev_cmdline(), perhaps that would be worthwhile, but for now let's\n> just free() it.\n> \n> We could also make that a \"char *\" in \"struct rev_cmdline_entry\"\n> itself, but since we own it let's expose it as a constant to outside\n> callers. I proposed that in [2] but have since changed my mind. See\n> 14d30cdfc04 (ref-filter: fix memory leak in `free_array_item()`,\n> 2019-07-10), c514c62a4fd (checkout: fix leak of non-existent branch\n> names, 2020-08-14) and other log history hits for \"free((char *)\" for\n> prior art.\n> \n> This includes the tests we had false-positive passes on before my\n> 6798b08e848 (perl Git.pm: don't ignore signalled failure in\n> _cmd_close(), 2022-02-01), now they pass for real.\n> \n> Since there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to\n> list those that don't pass than to touch most of those 66. So let's\n> introduce a \"TEST_FAILS_SANITIZE_LEAK=true\", which if set in the tests\n> won't cause lib-git-svn.sh to set \"TEST_PASSES_SANITIZE_LEAK=true.\n> \n> This change also marks all the tests that we removed\n> \"TEST_FAILS_SANITIZE_LEAK=true\" from in an earlier commit due to\n> removing the UNLEAK() from cmd_format_patch(), we can now assert that\n> its API use doesn't leak any \"struct rev_info\" memory.\n> \n> This change also made commit \"t5503-tagfollow.sh\" pass on current\n> master, but that would regress when combined with\n> ps/fetch-atomic-fixup's de004e848a9 (t5503: simplify setup of test\n> which exercises failure of backfill, 2022-03-03) (through no fault of\n> that topic, that change started using \"git clone\" in the test, which\n> has an outstanding leak). Let's leave that test out for now to avoid\n> in-flight semantic conflicts.\n> \n> 1. https://lore.kernel.org/git/YUj%2FgFRh6pwrZalY@carlos-mbp.lan/\n> 2. https://lore.kernel.org/git/87o88obkb1.fsf@evledraar.gmail.com/\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>   revision.c                                 | 12 ++++++++++++\n>   t/lib-git-svn.sh                           |  4 ++++\n>   t/t0062-revision-walking.sh                |  1 +\n>   t/t0101-at-syntax.sh                       |  2 ++\n>   t/t1060-object-corruption.sh               |  1 +\n>   t/t3303-notes-subtrees.sh                  |  1 +\n>   t/t3305-notes-fanout.sh                    |  1 +\n>   t/t3408-rebase-multi-line.sh               |  1 +\n>   t/t4027-diff-submodule.sh                  |  1 +\n>   t/t4128-apply-root.sh                      |  1 +\n>   t/t4212-log-corrupt.sh                     |  1 +\n>   t/t5515-fetch-merge-logic.sh               |  1 +\n>   t/t5518-fetch-exit-status.sh               |  1 +\n>   t/t6002-rev-list-bisect.sh                 |  1 +\n>   t/t6003-rev-list-topo-order.sh             |  1 +\n>   t/t6005-rev-list-count.sh                  |  1 +\n>   t/t6018-rev-list-glob.sh                   |  1 +\n>   t/t6100-rev-list-in-order.sh               |  1 +\n>   t/t9100-git-svn-basic.sh                   |  1 +\n>   t/t9101-git-svn-props.sh                   |  2 ++\n>   t/t9104-git-svn-follow-parent.sh           |  2 ++\n>   t/t9106-git-svn-commit-diff-clobber.sh     |  2 ++\n>   t/t9115-git-svn-dcommit-funky-renames.sh   |  1 +\n>   t/t9116-git-svn-log.sh                     |  2 ++\n>   t/t9122-git-svn-author.sh                  |  2 ++\n>   t/t9127-git-svn-partial-rebuild.sh         |  2 ++\n>   t/t9129-git-svn-i18n-commitencoding.sh     |  1 +\n>   t/t9132-git-svn-broken-symlink.sh          |  1 +\n>   t/t9139-git-svn-non-utf8-commitencoding.sh |  1 +\n>   t/t9146-git-svn-empty-dirs.sh              |  2 ++\n>   t/t9148-git-svn-propset.sh                 |  1 +\n>   t/t9151-svn-mergeinfo.sh                   |  1 +\n>   t/t9160-git-svn-preserve-empty-dirs.sh     |  1 +\n>   t/t9162-git-svn-dcommit-interactive.sh     |  2 ++\n>   t/t9164-git-svn-dcommit-concurrent.sh      |  2 ++\n>   t/t9501-gitweb-standalone-http-status.sh   |  1 +\n>   36 files changed, 60 insertions(+)\n> \n> diff --git a/revision.c b/revision.c\n> index 622f0faecc4..c81506fa22f 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -2926,6 +2926,17 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n>   \treturn left;\n>   }\n>   \n> +static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)\n> +{\n> +\tunsigned int i;\n> +\n> +\tif (!cmdline)\n> +\t\treturn;\n\nI don't think we need this guard, the only instances of struct \nrev_cmdline_info exist within struct rev_info, as far as I can see it is \nnever created on its own.\n\n> +\tfor (i = 0; i < cmdline->nr; i++)\n> +\t\tfree((char *)cmdline->rev[i].name);\n> +\tfree(cmdline->rev);\n\nThis could just be in release_revisions()\n\nBest Wishes\n\nPhillip\n> +}\n> +\n>   static void release_revisions_mailmap(struct string_list *mailmap)\n>   {\n>   \tif (!mailmap)\n> @@ -2938,6 +2949,7 @@ void release_revisions(struct rev_info *revs)\n>   {\n>   \tfree_commit_list(revs->commits);\n>   \tobject_array_clear(&revs->pending);\n> +\trelease_revisions_cmdline(&revs->cmdline);\n>   \trelease_revisions_mailmap(revs->mailmap);\n>   }\n>   \n> diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh\n> index 2fde2353fd3..ea28971e8ee 100644\n> --- a/t/lib-git-svn.sh\n> +++ b/t/lib-git-svn.sh\n> @@ -1,3 +1,7 @@\n> +if test -z \"$TEST_FAILS_SANITIZE_LEAK\"\n> +then\n> +\tTEST_PASSES_SANITIZE_LEAK=true\n> +fi\n>   . ./test-lib.sh\n>   \n>   if test -n \"$NO_SVN_TESTS\"\n> diff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh\n> index 8e215867b8c..b9480c81781 100755\n> --- a/t/t0062-revision-walking.sh\n> +++ b/t/t0062-revision-walking.sh\n> @@ -5,6 +5,7 @@\n>   \n>   test_description='Test revision walking api'\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   \n>   cat >run_twice_expected <<-EOF\n> diff --git a/t/t0101-at-syntax.sh b/t/t0101-at-syntax.sh\n> index a1998b558f9..878aadd64c9 100755\n> --- a/t/t0101-at-syntax.sh\n> +++ b/t/t0101-at-syntax.sh\n> @@ -1,6 +1,8 @@\n>   #!/bin/sh\n>   \n>   test_description='various @{whatever} syntax tests'\n> +\n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   \n>   test_expect_success 'setup' '\n> diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh\n> index bc89371f534..e8a58b15897 100755\n> --- a/t/t1060-object-corruption.sh\n> +++ b/t/t1060-object-corruption.sh\n> @@ -1,6 +1,7 @@\n>   #!/bin/sh\n>   \n>   test_description='see how we handle various forms of corruption'\n> +\n>   . ./test-lib.sh\n>   \n>   # convert \"1234abcd\" to \".git/objects/12/34abcd\"\n> diff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh\n> index eac193757bf..bc9b791d1b9 100755\n> --- a/t/t3303-notes-subtrees.sh\n> +++ b/t/t3303-notes-subtrees.sh\n> @@ -5,6 +5,7 @@ test_description='Test commit notes organized in subtrees'\n>   GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n>   export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   \n>   number_of_commits=100\n> diff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh\n> index 9976d787f47..64a9915761a 100755\n> --- a/t/t3305-notes-fanout.sh\n> +++ b/t/t3305-notes-fanout.sh\n> @@ -2,6 +2,7 @@\n>   \n>   test_description='Test that adding/removing many notes triggers automatic fanout restructuring'\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   \n>   path_has_fanout() {\n> diff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh\n> index cde3562e3a6..7b4607d72f2 100755\n> --- a/t/t3408-rebase-multi-line.sh\n> +++ b/t/t3408-rebase-multi-line.sh\n> @@ -5,6 +5,7 @@ test_description='rebasing a commit with multi-line first paragraph.'\n>   GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n>   export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   \n>   test_expect_success setup '\n> diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh\n> index 295da987cce..40164ae07d2 100755\n> --- a/t/t4027-diff-submodule.sh\n> +++ b/t/t4027-diff-submodule.sh\n> @@ -2,6 +2,7 @@\n>   \n>   test_description='difference in submodules'\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   . \"$TEST_DIRECTORY\"/lib-diff.sh\n>   \n> diff --git a/t/t4128-apply-root.sh b/t/t4128-apply-root.sh\n> index f6db5a79dd9..ed94c90204e 100755\n> --- a/t/t4128-apply-root.sh\n> +++ b/t/t4128-apply-root.sh\n> @@ -2,6 +2,7 @@\n>   \n>   test_description='apply same filename'\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   \n>   test_expect_success 'setup' '\n> diff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh\n> index 0244888a5a7..30a219894bb 100755\n> --- a/t/t4212-log-corrupt.sh\n> +++ b/t/t4212-log-corrupt.sh\n> @@ -2,6 +2,7 @@\n>   \n>   test_description='git log with invalid commit headers'\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   \n>   test_expect_success 'setup' '\n> diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh\n> index 320d26796d2..c100a809c5e 100755\n> --- a/t/t5515-fetch-merge-logic.sh\n> +++ b/t/t5515-fetch-merge-logic.sh\n> @@ -14,6 +14,7 @@ export GIT_TEST_PROTOCOL_VERSION\n>   GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n>   export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   \n>   build_script () {\n> diff --git a/t/t5518-fetch-exit-status.sh b/t/t5518-fetch-exit-status.sh\n> index 5c4ac2556e7..c13120088fa 100755\n> --- a/t/t5518-fetch-exit-status.sh\n> +++ b/t/t5518-fetch-exit-status.sh\n> @@ -8,6 +8,7 @@ test_description='fetch exit status test'\n>   GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n>   export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   \n>   test_expect_success setup '\n> diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh\n> index b95a0212adf..162cf50778d 100755\n> --- a/t/t6002-rev-list-bisect.sh\n> +++ b/t/t6002-rev-list-bisect.sh\n> @@ -4,6 +4,7 @@\n>   #\n>   test_description='Tests git rev-list --bisect functionality'\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   . \"$TEST_DIRECTORY\"/lib-t6000.sh # t6xxx specific functions\n>   \n> diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh\n> index 24d1836f417..1f7d7dd20c1 100755\n> --- a/t/t6003-rev-list-topo-order.sh\n> +++ b/t/t6003-rev-list-topo-order.sh\n> @@ -5,6 +5,7 @@\n>   \n>   test_description='Tests git rev-list --topo-order functionality'\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   . \"$TEST_DIRECTORY\"/lib-t6000.sh # t6xxx specific functions\n>   \n> diff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh\n> index e960049f647..0729f800c3c 100755\n> --- a/t/t6005-rev-list-count.sh\n> +++ b/t/t6005-rev-list-count.sh\n> @@ -2,6 +2,7 @@\n>   \n>   test_description='git rev-list --max-count and --skip test'\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   \n>   test_expect_success 'setup' '\n> diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh\n> index 24b34add833..e1abc5c2b32 100755\n> --- a/t/t6018-rev-list-glob.sh\n> +++ b/t/t6018-rev-list-glob.sh\n> @@ -5,6 +5,7 @@ test_description='rev-list/rev-parse --glob'\n>   GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n>   export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   \n>   commit () {\n> diff --git a/t/t6100-rev-list-in-order.sh b/t/t6100-rev-list-in-order.sh\n> index e934bc239c5..88ed7bd75a7 100755\n> --- a/t/t6100-rev-list-in-order.sh\n> +++ b/t/t6100-rev-list-in-order.sh\n> @@ -2,6 +2,7 @@\n>   \n>   test_description='rev-list testing in-commit-order'\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   \n>   test_expect_success 'setup a commit history with trees, blobs' '\n> diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh\n> index fea41b3c360..7c5b847f584 100755\n> --- a/t/t9100-git-svn-basic.sh\n> +++ b/t/t9100-git-svn-basic.sh\n> @@ -8,6 +8,7 @@ test_description='git svn basic tests'\n>   GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n>   export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>   \n> +TEST_FAILS_SANITIZE_LEAK=true\n>   . ./lib-git-svn.sh\n>   \n>   prepare_utf8_locale\n> diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh\n> index 8b5681dd68a..d043e80fc34 100755\n> --- a/t/t9101-git-svn-props.sh\n> +++ b/t/t9101-git-svn-props.sh\n> @@ -4,6 +4,8 @@\n>   #\n>   \n>   test_description='git svn property tests'\n> +\n> +TEST_FAILS_SANITIZE_LEAK=true\n>   . ./lib-git-svn.sh\n>   \n>   mkdir import\n> diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh\n> index c7d8e0bf00f..5cf2ef4b8b0 100755\n> --- a/t/t9104-git-svn-follow-parent.sh\n> +++ b/t/t9104-git-svn-follow-parent.sh\n> @@ -4,6 +4,8 @@\n>   #\n>   \n>   test_description='git svn fetching'\n> +\n> +TEST_FAILS_SANITIZE_LEAK=true\n>   . ./lib-git-svn.sh\n>   \n>   test_expect_success 'initialize repo' '\n> diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh\n> index aec45bca3b7..3cab0b9720a 100755\n> --- a/t/t9106-git-svn-commit-diff-clobber.sh\n> +++ b/t/t9106-git-svn-commit-diff-clobber.sh\n> @@ -2,6 +2,8 @@\n>   #\n>   # Copyright (c) 2006 Eric Wong\n>   test_description='git svn commit-diff clobber'\n> +\n> +TEST_FAILS_SANITIZE_LEAK=true\n>   . ./lib-git-svn.sh\n>   \n>   test_expect_success 'initialize repo' '\n> diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh\n> index 743fbe1fe46..419f055721d 100755\n> --- a/t/t9115-git-svn-dcommit-funky-renames.sh\n> +++ b/t/t9115-git-svn-dcommit-funky-renames.sh\n> @@ -5,6 +5,7 @@\n>   \n>   test_description='git svn dcommit can commit renames of files with ugly names'\n>   \n> +TEST_FAILS_SANITIZE_LEAK=true\n>   . ./lib-git-svn.sh\n>   \n>   test_expect_success 'load repository with strange names' '\n> diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh\n> index 0a9f1ef366d..34f6c80dea3 100755\n> --- a/t/t9116-git-svn-log.sh\n> +++ b/t/t9116-git-svn-log.sh\n> @@ -4,6 +4,8 @@\n>   #\n>   \n>   test_description='git svn log tests'\n> +\n> +TEST_FAILS_SANITIZE_LEAK=true\n>   . ./lib-git-svn.sh\n>   \n>   test_expect_success 'setup repository and import' '\n> diff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh\n> index 9e8fe38e7ef..527ba3d2932 100755\n> --- a/t/t9122-git-svn-author.sh\n> +++ b/t/t9122-git-svn-author.sh\n> @@ -1,6 +1,8 @@\n>   #!/bin/sh\n>   \n>   test_description='git svn authorship'\n> +\n> +TEST_FAILS_SANITIZE_LEAK=true\n>   . ./lib-git-svn.sh\n>   \n>   test_expect_success 'setup svn repository' '\n> diff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh\n> index 2e4789d061f..90b1b30dde5 100755\n> --- a/t/t9127-git-svn-partial-rebuild.sh\n> +++ b/t/t9127-git-svn-partial-rebuild.sh\n> @@ -4,6 +4,8 @@\n>   #\n>   \n>   test_description='git svn partial-rebuild tests'\n> +\n> +TEST_FAILS_SANITIZE_LEAK=true\n>   . ./lib-git-svn.sh\n>   \n>   test_expect_success 'initialize svnrepo' '\n> diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh\n> index 01e1e8a8f76..185248a4cd7 100755\n> --- a/t/t9129-git-svn-i18n-commitencoding.sh\n> +++ b/t/t9129-git-svn-i18n-commitencoding.sh\n> @@ -4,6 +4,7 @@\n>   \n>   test_description='git svn honors i18n.commitEncoding in config'\n>   \n> +TEST_FAILS_SANITIZE_LEAK=true\n>   . ./lib-git-svn.sh\n>   \n>   compare_git_head_with () {\n> diff --git a/t/t9132-git-svn-broken-symlink.sh b/t/t9132-git-svn-broken-symlink.sh\n> index aeceffaf7b0..4d8d0584b79 100755\n> --- a/t/t9132-git-svn-broken-symlink.sh\n> +++ b/t/t9132-git-svn-broken-symlink.sh\n> @@ -2,6 +2,7 @@\n>   \n>   test_description='test that git handles an svn repository with empty symlinks'\n>   \n> +TEST_FAILS_SANITIZE_LEAK=true\n>   . ./lib-git-svn.sh\n>   test_expect_success 'load svn dumpfile' '\n>   \tsvnadmin load \"$rawsvnrepo\" <<EOF\n> diff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh\n> index 22d80b0be2b..b7f756b2b7f 100755\n> --- a/t/t9139-git-svn-non-utf8-commitencoding.sh\n> +++ b/t/t9139-git-svn-non-utf8-commitencoding.sh\n> @@ -4,6 +4,7 @@\n>   \n>   test_description='git svn refuses to dcommit non-UTF8 messages'\n>   \n> +TEST_FAILS_SANITIZE_LEAK=true\n>   . ./lib-git-svn.sh\n>   \n>   # ISO-2022-JP can pass for valid UTF-8, so skipping that in this test\n> diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh\n> index 80cb55fee70..79c26ed69c1 100755\n> --- a/t/t9146-git-svn-empty-dirs.sh\n> +++ b/t/t9146-git-svn-empty-dirs.sh\n> @@ -3,6 +3,8 @@\n>   # Copyright (c) 2009 Eric Wong\n>   \n>   test_description='git svn creates empty directories'\n> +\n> +TEST_FAILS_SANITIZE_LEAK=true\n>   . ./lib-git-svn.sh\n>   \n>   test_expect_success 'initialize repo' '\n> diff --git a/t/t9148-git-svn-propset.sh b/t/t9148-git-svn-propset.sh\n> index aebb28995e5..6cc76a07b39 100755\n> --- a/t/t9148-git-svn-propset.sh\n> +++ b/t/t9148-git-svn-propset.sh\n> @@ -5,6 +5,7 @@\n>   \n>   test_description='git svn propset tests'\n>   \n> +TEST_FAILS_SANITIZE_LEAK=true\n>   . ./lib-git-svn.sh\n>   \n>   test_expect_success 'setup propset via import' '\n> diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh\n> index c93a5beab25..85221d439bd 100755\n> --- a/t/t9151-svn-mergeinfo.sh\n> +++ b/t/t9151-svn-mergeinfo.sh\n> @@ -5,6 +5,7 @@\n>   \n>   test_description='git-svn svn mergeinfo properties'\n>   \n> +TEST_FAILS_SANITIZE_LEAK=true\n>   . ./lib-git-svn.sh\n>   \n>   test_expect_success 'load svn dump' \"\n> diff --git a/t/t9160-git-svn-preserve-empty-dirs.sh b/t/t9160-git-svn-preserve-empty-dirs.sh\n> index 36c6b1a12ff..9cf7a1427ab 100755\n> --- a/t/t9160-git-svn-preserve-empty-dirs.sh\n> +++ b/t/t9160-git-svn-preserve-empty-dirs.sh\n> @@ -9,6 +9,7 @@ This test uses git to clone a Subversion repository that contains empty\n>   directories, and checks that corresponding directories are created in the\n>   local Git repository with placeholder files.'\n>   \n> +TEST_FAILS_SANITIZE_LEAK=true\n>   . ./lib-git-svn.sh\n>   \n>   GIT_REPO=git-svn-repo\n> diff --git a/t/t9162-git-svn-dcommit-interactive.sh b/t/t9162-git-svn-dcommit-interactive.sh\n> index e38d9fa37b5..e2aa8ed88a9 100755\n> --- a/t/t9162-git-svn-dcommit-interactive.sh\n> +++ b/t/t9162-git-svn-dcommit-interactive.sh\n> @@ -3,6 +3,8 @@\n>   # Copyright (c) 2011 Frédéric Heitzmann\n>   \n>   test_description='git svn dcommit --interactive series'\n> +\n> +TEST_FAILS_SANITIZE_LEAK=true\n>   . ./lib-git-svn.sh\n>   \n>   test_expect_success 'initialize repo' '\n> diff --git a/t/t9164-git-svn-dcommit-concurrent.sh b/t/t9164-git-svn-dcommit-concurrent.sh\n> index 8466269bf50..1465156072e 100755\n> --- a/t/t9164-git-svn-dcommit-concurrent.sh\n> +++ b/t/t9164-git-svn-dcommit-concurrent.sh\n> @@ -4,6 +4,8 @@\n>   #\n>   \n>   test_description='concurrent git svn dcommit'\n> +\n> +TEST_FAILS_SANITIZE_LEAK=true\n>   . ./lib-git-svn.sh\n>   \n>   \n> diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh\n> index 32814e75df5..c900231079c 100755\n> --- a/t/t9501-gitweb-standalone-http-status.sh\n> +++ b/t/t9501-gitweb-standalone-http-status.sh\n> @@ -13,6 +13,7 @@ code and message.'\n>   GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n>   export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./lib-gitweb.sh\n>   \n>   #\n"},{"id":"452976","messageId":"5b93870c-39eb-39eb-e6a5-92b4e1cf8f12@gmail.com","threadId":"57525","inReplyTo":"patch-v5-24.27-c7f3e92a8d1-20220402T102002Z-avarab@gmail.com","subject":"Re: [PATCH v5 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release()","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2022-04-03T13:00:16Z","receivedAt":"2022-04-03T13:00:46Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Ævar\n\nOn 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:\n> Call diff_free() on the \"pruning\" member of \"struct rev_info\".  Doing\n> so makes several tests pass under SANITIZE=leak.\n> \n> This was also the last missing piece that allows us to remove the\n> UNLEAK() in \"cmd_diff\" and \"cmd_diff_index\", which allows us to use\n> those commands as a canary for general leaks in the revisions API. See\n> [1] for further rationale, and 886e1084d78 (builtin/: add UNLEAKs,\n> 2017-10-01) for the commit that added the UNLEAK() there.\n\nOh is the the answer to my confusion about patch 14? I wonder if the \nchange could come earlier so we can remove the UNLEAK()s all at once?\n\nBest Wishes\n\nPhillip\n\n> 1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>   builtin/diff-index.c                | 1 -\n>   builtin/diff.c                      | 1 -\n>   revision.c                          | 1 +\n>   t/t1001-read-tree-m-2way.sh         | 1 +\n>   t/t1002-read-tree-m-u-2way.sh       | 1 +\n>   t/t2200-add-update.sh               | 1 +\n>   t/t4039-diff-assume-unchanged.sh    | 1 +\n>   t/t4206-log-follow-harder-copies.sh | 2 ++\n>   t/t6131-pathspec-icase.sh           | 2 ++\n>   9 files changed, 9 insertions(+), 2 deletions(-)\n> \n> diff --git a/builtin/diff-index.c b/builtin/diff-index.c\n> index 3a83183c312..7d158af6b6d 100644\n> --- a/builtin/diff-index.c\n> +++ b/builtin/diff-index.c\n> @@ -70,7 +70,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)\n>   \t\treturn -1;\n>   \t}\n>   \tresult = run_diff_index(&rev, option);\n> -\tUNLEAK(rev);\n>   \tresult = diff_result_code(&rev.diffopt, result);\n>   \trelease_revisions(&rev);\n>   \treturn result;\n> diff --git a/builtin/diff.c b/builtin/diff.c\n> index dd48336da56..f539132ac68 100644\n> --- a/builtin/diff.c\n> +++ b/builtin/diff.c\n> @@ -594,7 +594,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n>   \tresult = diff_result_code(&rev.diffopt, result);\n>   \tif (1 < rev.diffopt.skip_stat_unmatch)\n>   \t\trefresh_index_quietly();\n> -\tUNLEAK(rev);\n>   \trelease_revisions(&rev);\n>   \tUNLEAK(ent);\n>   \tUNLEAK(blob);\n> diff --git a/revision.c b/revision.c\n> index e972addd8fc..8bc777da828 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -2955,6 +2955,7 @@ void release_revisions(struct rev_info *revs)\n>   \tclear_pathspec(&revs->prune_data);\n>   \trelease_revisions_mailmap(revs->mailmap);\n>   \tfree_grep_patterns(&revs->grep_filter);\n> +\tdiff_free(&revs->pruning);\n>   \treflog_walk_info_release(revs->reflog_info);\n>   }\n>   \n> diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh\n> index 0710b1fb1e9..516a6112fdc 100755\n> --- a/t/t1001-read-tree-m-2way.sh\n> +++ b/t/t1001-read-tree-m-2way.sh\n> @@ -21,6 +21,7 @@ In the test, these paths are used:\n>   \tyomin   - not in H or M\n>   '\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   . \"$TEST_DIRECTORY\"/lib-read-tree.sh\n>   \n> diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh\n> index 46cbd5514a6..bd5313caec9 100755\n> --- a/t/t1002-read-tree-m-u-2way.sh\n> +++ b/t/t1002-read-tree-m-u-2way.sh\n> @@ -9,6 +9,7 @@ This is identical to t1001, but uses -u to update the work tree as well.\n>   \n>   '\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   . \"$TEST_DIRECTORY\"/lib-read-tree.sh\n>   \n> diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh\n> index 0c38f8e3569..be394f1131a 100755\n> --- a/t/t2200-add-update.sh\n> +++ b/t/t2200-add-update.sh\n> @@ -14,6 +14,7 @@ only the updates to dir/sub.\n>   Also tested are \"git add -u\" without limiting, and \"git add -u\"\n>   without contents changes, and other conditions'\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   \n>   test_expect_success setup '\n> diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh\n> index 0eb0314a8b3..78090e6852d 100755\n> --- a/t/t4039-diff-assume-unchanged.sh\n> +++ b/t/t4039-diff-assume-unchanged.sh\n> @@ -2,6 +2,7 @@\n>   \n>   test_description='diff with assume-unchanged entries'\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   \n>   # external diff has been tested in t4020-diff-external.sh\n> diff --git a/t/t4206-log-follow-harder-copies.sh b/t/t4206-log-follow-harder-copies.sh\n> index 4871a5dc92f..33ecf82c7f9 100755\n> --- a/t/t4206-log-follow-harder-copies.sh\n> +++ b/t/t4206-log-follow-harder-copies.sh\n> @@ -6,6 +6,8 @@\n>   test_description='Test --follow should always find copies hard in git log.\n>   \n>   '\n> +\n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   . \"$TEST_DIRECTORY\"/lib-diff.sh\n>   \n> diff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh\n> index 39fc3f6769b..770cce026cb 100755\n> --- a/t/t6131-pathspec-icase.sh\n> +++ b/t/t6131-pathspec-icase.sh\n> @@ -1,6 +1,8 @@\n>   #!/bin/sh\n>   \n>   test_description='test case insensitive pathspec limiting'\n> +\n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   \n>   if test_have_prereq CASE_INSENSITIVE_FS\n"},{"id":"452977","messageId":"5d3345d9-3c7c-a761-9c7a-03468a653e48@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"Re: [PATCH v5 00/27] revision.[ch]: add and use release_revisions()","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2022-04-03T13:07:16Z","receivedAt":"2022-04-03T13:07:34Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Ævar\n\nI've left a few comments on this series for the patches that I had not \nlooked at in v4. There's nothing major, the changes mostly make sense to \nme (though I'm not familiar with the revisions api)\n\nOn 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:\n> This series reduces the memory leaks spotted by our tests by at least\n> half, see v3[1] for a more general overview.\n> \n> Thanks a lot to Phillip Wood and Junio for the v4 review! I think this\n> v5 should address all of the things brought up in v4 in one way or\n> another, changes:\n> \n>   * The \"extra_headers\" or \"ref_message_ids\" freeing is the same, but\n>     the commit message is altered to explain that commit.\n\nI'm still not that convinced that these are owned by the code in \nbuiltin/log.c but lets leave that for now. The rest of the changes look \ngood. Thanks for working on this, when I use LSAN on my patches before \nsubmitting them I often see a lot of existing leaks coming from struct \nrev_info.\n\nBest Wishes\n\nPhillip\n\n>   * The \"revision.[ch]: split freeing of revs->commit into a function\"\n>     is gone, instead I applied a coccinelle rule to free_commit_list()\n>     as suggested by Phillip Wood (well, the change in pattern, not the\n>     rule was the suggestion).\n> \n>   * The release_revisions() is now simply using free_commit_list()\n>     without a NULL assignment to the member, and I changed some\n>     FREE_AND_NULL() that I'd added (for e.g. cmdline) to just free().\n> \n>     The \"and NULL\" was just cargo-culted from elswhere, but for\n>     release_revisions() we not only aren't using the struct again (it's\n>     not a \"reset\"), but it it wouldn't have worked as a reset before\n>     anyway (some things free'd without NULL-ing).\n> \n>     We could trivially have a \"reset\" variant of release_revisions(),\n>     but since no current API user wanted it let's leave it, leving the\n>     memory uninitialized will help to catch any invalid API usage.\n> \n>   * I missed a object_array_clear() in builtin/stash.c that\n>     could/should be free'd by release_revisions().\n> \n>   * An obscure memory leak in stash.c (on \"master\") around\n>     assert_stash_ref() has been fixed.\n> \n>   * Add a \"goto usage\" pattern for a memory leak new in stash.c that\n>     this series introduced, i.e. because strvec_clear() was moved to a\n>     \"cleanup\" block if we invoked usage_with_options() between the two\n>     we'd exit without the cleanup. Now we'll cleanup on \"usage\" too.\n> \n>   * Updated commit message & docs around REV_INFO_INIT, hopefully\n>     addressing the thread left off at\n>     https://lore.kernel.org/git/220401.86h77dnkpw.gmgdl@evledraar.gmail.com/\n> \n> 1. https://lore.kernel.org/git/cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com/\n> \n> Ævar Arnfjörð Bjarmason (27):\n>    t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"\n>    blame: use \"goto cleanup\" for cleanup_scoreboard()\n>    string_list API users: use string_list_init_{no,}dup\n>    format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"\n>    cocci: add and apply free_commit_list() rules\n>    revision.[ch]: provide and start using a release_revisions()\n>    revisions API users: add straightforward release_revisions()\n>    revision.[ch]: document and move code declared around \"init\"\n>    revisions API users: use release_revisions() needing REV_INFO_INIT\n>    stash: always have the owner of \"stash_info\" free it\n>    revisions API users: add \"goto cleanup\" for release_revisions()\n>    revisions API users: use release_revisions() in http-push.c\n>    revisions API users: use release_revisions() in builtin/log.c\n>    revisions API users: use release_revisions() with UNLEAK()\n>    revisions API users: use release_revisions() for \"prune_data\" users\n>    revisions API: have release_revisions() release \"commits\"\n>    revisions API: have release_revisions() release \"mailmap\"\n>    revisions API: have release_revisions() release \"cmdline\"\n>    revisions API: have release_revisions() release \"filter\"\n>    revisions API: have release_revisions() release \"grep_filter\"\n>    revisions API: have release_revisions() release \"prune_data\"\n>    revisions API: clear \"boundary_commits\" in release_revisions()\n>    revisions API: release \"reflog_info\" in release revisions()\n>    revisions API: call diff_free(&revs->pruning) in revisions_release()\n>    revisions API: have release_revisions() release \"date_mode\"\n>    revisions API: have release_revisions() release \"topo_walk_info\"\n>    revisions API: add a TODO for diff_free(&revs->diffopt)\n> \n>   add-interactive.c                          |   3 +-\n>   bisect.c                                   |  20 ++--\n>   builtin/add.c                              |   3 +-\n>   builtin/am.c                               |   3 +\n>   builtin/bisect--helper.c                   |   2 +\n>   builtin/blame.c                            |   4 +-\n>   builtin/checkout.c                         |   3 +-\n>   builtin/commit.c                           |   6 +-\n>   builtin/describe.c                         |   2 +\n>   builtin/diff-files.c                       |   8 +-\n>   builtin/diff-index.c                       |   5 +-\n>   builtin/diff.c                             |   2 +-\n>   builtin/fast-export.c                      |   1 +\n>   builtin/log.c                              |  36 ++++---\n>   builtin/merge.c                            |   2 +\n>   builtin/pack-objects.c                     |   2 +\n>   builtin/prune.c                            |   1 +\n>   builtin/reflog.c                           |   1 +\n>   builtin/rev-list.c                         |  25 +++--\n>   builtin/shortlog.c                         |   8 +-\n>   builtin/stash.c                            | 115 +++++++++++----------\n>   builtin/submodule--helper.c                |  14 ++-\n>   bundle.c                                   |  12 ++-\n>   commit.c                                   |  19 ++--\n>   contrib/coccinelle/free.cocci              |  27 +++++\n>   diff-lib.c                                 |   8 +-\n>   fmt-merge-msg.c                            |   1 +\n>   http-push.c                                |   3 +-\n>   merge-ort.c                                |   1 +\n>   merge-recursive.c                          |   5 +-\n>   midx.c                                     |   1 +\n>   pack-bitmap-write.c                        |   1 +\n>   range-diff.c                               |   2 +-\n>   ref-filter.c                               |   1 +\n>   reflog-walk.c                              |  24 ++++-\n>   reflog-walk.h                              |   1 +\n>   remote.c                                   |   1 +\n>   revision.c                                 |  72 ++++++++++---\n>   revision.h                                 |  73 ++++++++-----\n>   sequencer.c                                |  26 +++--\n>   shallow.c                                  |   1 +\n>   submodule.c                                |  11 +-\n>   t/helper/test-fast-rebase.c                |  23 +++--\n>   t/helper/test-revision-walking.c           |   1 +\n>   t/lib-git-svn.sh                           |   4 +\n>   t/t0056-git-C.sh                           |   1 +\n>   t/t0062-revision-walking.sh                |   1 +\n>   t/t0100-previous.sh                        |   1 +\n>   t/t0101-at-syntax.sh                       |   2 +\n>   t/t1001-read-tree-m-2way.sh                |   1 +\n>   t/t1002-read-tree-m-u-2way.sh              |   1 +\n>   t/t1060-object-corruption.sh               |   2 +\n>   t/t1401-symbolic-ref.sh                    |   2 +\n>   t/t1411-reflog-show.sh                     |   1 +\n>   t/t1412-reflog-loop.sh                     |   2 +\n>   t/t1415-worktree-refs.sh                   |   1 +\n>   t/t2015-checkout-unborn.sh                 |   1 +\n>   t/t2200-add-update.sh                      |   1 +\n>   t/t3302-notes-index-expensive.sh           |   1 +\n>   t/t3303-notes-subtrees.sh                  |   1 +\n>   t/t3305-notes-fanout.sh                    |   1 +\n>   t/t3408-rebase-multi-line.sh               |   1 +\n>   t/t4021-format-patch-numbered.sh           |   1 +\n>   t/t4027-diff-submodule.sh                  |   1 +\n>   t/t4028-format-patch-mime-headers.sh       |   2 +\n>   t/t4036-format-patch-signer-mime.sh        |   1 +\n>   t/t4039-diff-assume-unchanged.sh           |   1 +\n>   t/t4055-diff-context.sh                    |   1 +\n>   t/t4066-diff-emit-delay.sh                 |   1 +\n>   t/t4122-apply-symlink-inside.sh            |   1 +\n>   t/t4126-apply-empty.sh                     |   1 -\n>   t/t4128-apply-root.sh                      |   1 +\n>   t/t4206-log-follow-harder-copies.sh        |   2 +\n>   t/t4207-log-decoration-colors.sh           |   1 +\n>   t/t4212-log-corrupt.sh                     |   1 +\n>   t/t5301-sliding-window.sh                  |   2 +\n>   t/t5313-pack-bounds-checks.sh              |   2 +\n>   t/t5316-pack-delta-depth.sh                |   2 +\n>   t/t5320-delta-islands.sh                   |   2 +\n>   t/t5322-pack-objects-sparse.sh             |   1 +\n>   t/t5506-remote-groups.sh                   |   1 +\n>   t/t5513-fetch-track.sh                     |   1 +\n>   t/t5515-fetch-merge-logic.sh               |   1 +\n>   t/t5518-fetch-exit-status.sh               |   1 +\n>   t/t5532-fetch-proxy.sh                     |   2 +\n>   t/t5600-clone-fail-cleanup.sh              |   1 +\n>   t/t5900-repo-selection.sh                  |   2 +\n>   t/t6002-rev-list-bisect.sh                 |   1 +\n>   t/t6003-rev-list-topo-order.sh             |   1 +\n>   t/t6005-rev-list-count.sh                  |   1 +\n>   t/t6018-rev-list-glob.sh                   |   1 +\n>   t/t6100-rev-list-in-order.sh               |   1 +\n>   t/t6101-rev-parse-parents.sh               |   1 +\n>   t/t6110-rev-list-sparse.sh                 |   1 +\n>   t/t6114-keep-packs.sh                      |   2 +\n>   t/t6131-pathspec-icase.sh                  |   2 +\n>   t/t7008-filter-branch-null-sha1.sh         |   1 +\n>   t/t7702-repack-cyclic-alternate.sh         |   2 +\n>   t/t9001-send-email.sh                      |   1 +\n>   t/t9100-git-svn-basic.sh                   |   1 +\n>   t/t9101-git-svn-props.sh                   |   2 +\n>   t/t9104-git-svn-follow-parent.sh           |   2 +\n>   t/t9106-git-svn-commit-diff-clobber.sh     |   2 +\n>   t/t9115-git-svn-dcommit-funky-renames.sh   |   1 +\n>   t/t9116-git-svn-log.sh                     |   1 +\n>   t/t9122-git-svn-author.sh                  |   2 +\n>   t/t9127-git-svn-partial-rebuild.sh         |   1 +\n>   t/t9129-git-svn-i18n-commitencoding.sh     |   1 +\n>   t/t9132-git-svn-broken-symlink.sh          |   1 +\n>   t/t9139-git-svn-non-utf8-commitencoding.sh |   1 +\n>   t/t9146-git-svn-empty-dirs.sh              |   2 +\n>   t/t9148-git-svn-propset.sh                 |   1 +\n>   t/t9160-git-svn-preserve-empty-dirs.sh     |   1 +\n>   t/t9162-git-svn-dcommit-interactive.sh     |   2 +\n>   t/t9164-git-svn-dcommit-concurrent.sh      |   2 +\n>   t/t9501-gitweb-standalone-http-status.sh   |   1 +\n>   wt-status.c                                |  15 +--\n>   117 files changed, 502 insertions(+), 190 deletions(-)\n> \n> Range-diff against v4:\n>   1:  b99a07f98ee =  1:  e20f951a64c t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"\n>   2:  da5be507c0f =  2:  de2e08a14e6 blame: use \"goto cleanup\" for cleanup_scoreboard()\n>   3:  243f7c8df96 =  3:  364aa7d8990 string_list API users: use string_list_init_{no,}dup\n>   4:  69f0aabe38f !  4:  5d4ad92d47b format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"\n>      @@ Commit message\n>           cmd_format_patch(). These two are unusual special-cases in being in\n>           the \"struct rev_info\", but not being \"owned\" by the code in\n>           revision.c. I.e. they're members of the struct so that this code in\n>      -    \"builtin/log.c\" can pass information code in log-tree.c.\n>      +    \"builtin/log.c\" can conveniently pass information code in\n>      +    \"log-tree.c\".\n>      +\n>      +    See e.g. the make_cover_letter() caller of log_write_email_headers()\n>      +    here in \"builtin/log.c\", and [1] for a demonstration of where the\n>      +    \"extra_headers\" and \"ref_message_ids\" struct members are used.\n>       \n>           See 20ff06805c6 (format-patch: resurrect extra headers from config,\n>           2006-06-02) and d1566f7883f (git-format-patch: Make the second and\n>      @@ Commit message\n>           variable to hold it, to avoid the eventual cast from \"const char *\"\n>           when we free() it.\n>       \n>      +    1. https://lore.kernel.org/git/220401.868rsoogxf.gmgdl@evledraar.gmail.com/\n>      +\n>           Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>       \n>        ## builtin/log.c ##\n>   5:  2d0ed57ec2e <  -:  ----------- revision.[ch]: split freeing of revs->commit into a function\n>   -:  ----------- >  5:  c38db28f920 cocci: add and apply free_commit_list() rules\n>   6:  b9beaba16bf !  6:  b99f96599bb revision.[ch]: provide and start using a release_revisions()\n>      @@ Commit message\n>           turn adds a lot of noise when trying to mark up tests with\n>           \"TEST_PASSES_SANITIZE_LEAK=true\".\n>       \n>      -    The users of that API are largely one-shot, e.g. \"git log\", so\n>      -    arguably freeing the memory is a waste of time, but in many cases\n>      -    they've actually been trying to free the memory, and just doing that\n>      -    in a buggy manner.\n>      +    The users of that API are largely one-shot, e.g. \"git rev-list\" or\n>      +    \"git log\", or the \"git checkout\" and \"git stash\" being modified here\n>      +\n>      +    For these callers freeing the memory is arguably a waste of time, but\n>      +    in many cases they've actually been trying to free the memory, and\n>      +    just doing that in a buggy manner.\n>       \n>           Let's provide a release_revisions() function for these users, and\n>           start migrating them over per the plan outlined in [1]. Right now this\n>      @@ builtin/checkout.c: static void show_local_changes(struct object *head,\n>        static void describe_detached_head(const char *msg, struct commit *commit)\n>       \n>        ## builtin/stash.c ##\n>      +@@ builtin/stash.c: static int check_changes_tracked_files(const struct pathspec *ps)\n>      + \t\tgoto done;\n>      + \t}\n>      +\n>      +-\tobject_array_clear(&rev.pending);\n>      + \tresult = run_diff_files(&rev, 0);\n>      + \tif (diff_result_code(&rev.diffopt, result)) {\n>      + \t\tret = 1;\n>      +@@ builtin/stash.c: static int check_changes_tracked_files(const struct pathspec *ps)\n>      +\n>      + done:\n>      + \tclear_pathspec(&rev.prune_data);\n>      ++\trelease_revisions(&rev);\n>      + \treturn ret;\n>      + }\n>      +\n>       @@ builtin/stash.c: static int stash_working_tree(struct stash_info *info, const struct pathspec *ps\n>        \n>        done:\n>      @@ range-diff.c: int is_range_diff_range(const char *arg)\n>        }\n>       \n>        ## revision.c ##\n>      -@@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)\n>      - \t}\n>      +@@ revision.c: int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n>      + \treturn left;\n>        }\n>        \n>       +void release_revisions(struct rev_info *revs)\n>   7:  80ec9a131c0 =  7:  521f6967b64 revisions API users: add straightforward release_revisions()\n>   8:  086cec742b4 !  8:  003b507e0b7 revision.[ch]: document and move code declared around \"init\"\n>      @@ revision.h: void mark_trees_uninteresting_sparse(struct repository *r, struct oi\n>        void show_object_with_name(FILE *, struct object *, const char *);\n>        \n>       +/**\n>      -+ * Helpers to check if a \"struct string_list\" item matches wild\n>      ++ * Helpers to check if a \"struct string_list\" item matches with\n>       + * wildmatch().\n>       + */\n>       +int ref_excluded(struct string_list *, const char *path);\n>   9:  2f4e65fb534 !  9:  240307bc3c6 revisions API users: use release_revisions() needing REV_INFO_INIT\n>      @@ Commit message\n>           need to have their \"struct rev_info\" zero-initialized before we can\n>           start using it.\n>       \n>      -    To do this add a stub \"REV_INFO_INIT\" macro, ideally macro would be\n>      -    able to fully initialize a \"struct rev_info\", but all it does is the\n>      -    equivalent of assigning \"{ 0 }\" to the struct, the API user will still\n>      -    need to use repo_init_revisions(). In some future follow-up work we'll\n>      -    hopefully make REV_INFO_INIT be a \"stand-alone\" init likke STRBUF_INIT\n>      -    and other similar macros.\n>      -\n>           For the bundle.c code see the early exit case added in\n>           3bbbe467f29 (bundle verify: error out if called without an object\n>           database, 2019-05-27).\n>      @@ revision.h: struct rev_info {\n>       +/**\n>       + * Initialize the \"struct rev_info\" structure with a macro.\n>       + *\n>      -+ * This is not sufficient (yet!) to initialize a \"struct rev_info\",\n>      -+ * but it's OK (but redundant) to use it before a call to\n>      -+ * repo_init_revisions(), which does the real initialization. By using\n>      -+ * this it's safe to call release_revisions() on the \"struct rev_info\"\n>      -+ * without having called repo_init_revisions().\n>      ++ * This will not fully initialize a \"struct rev_info\", the\n>      ++ * repo_init_revisions() function needs to be called before\n>      ++ * setup_revisions() and any revision walking takes place.\n>      ++ *\n>      ++ * Use REV_INFO_INIT to make the \"struct rev_info\" safe for passing to\n>      ++ * release_revisions() when it's inconvenient (e.g. due to a \"goto\n>      ++ * cleanup\" pattern) to arrange for repo_init_revisions() to be called\n>      ++ * before release_revisions() is called.\n>      ++ *\n>      ++ * Initializing with this REV_INFO_INIT is redundant to invoking\n>      ++ * repo_init_revisions(). If repo_init_revisions() is guaranteed to be\n>      ++ * called before release_revisions() the \"struct rev_info\" can be left\n>      ++ * uninitialized.\n>       + */\n>       +#define REV_INFO_INIT { 0 }\n>       +\n>      @@ submodule.c: void show_submodule_diff_summary(struct diff_options *o, const char\n>        \tstruct commit_list *merge_bases = NULL;\n>        \tstruct repository *sub;\n>       @@ submodule.c: void show_submodule_diff_summary(struct diff_options *o, const char *path,\n>      - \tprint_submodule_diff_summary(sub, &rev, o);\n>        \n>        out:\n>      + \tfree_commit_list(merge_bases);\n>       +\trelease_revisions(&rev);\n>      - \tif (merge_bases)\n>      - \t\tfree_commit_list(merge_bases);\n>        \tclear_commit_marks(left, ~0);\n>      + \tclear_commit_marks(right, ~0);\n>      + \tif (sub) {\n> 10:  145a0f74b6a ! 10:  4c5404912e9 stash: always have the owner of \"stash_info\" free it\n>      @@ Commit message\n>           pattern we'd otherwise free() uninitialized memory in some cases, as\n>           we only strbuf_init() the string in get_stash_info().\n>       \n>      -    So while it's the smallest possible change, let's convert all users of\n>      -    this pattern in the file while we're at it.\n>      +    So while it's not the smallest possible change, let's convert all\n>      +    users of this pattern in the file while we're at it.\n>       \n>           A good follow-up to this change would be to change all the \"ret = -1;\n>           goto done;\" in this file to instead use a \"goto cleanup\", and\n>      @@ Commit message\n>           would allow us to drop a lot of needless brace verbosity on two-line\n>           \"if\" statements, but let's leave that alone for now.\n>       \n>      +    To ensure that there's a 1=1 mapping between owners of the \"struct\n>      +    stash_info\" and free_stash_info() change the assert_stash_ref()\n>      +    function to be a trivial get_stash_info_assert() wrapper. The caller\n>      +    will call free_stash_info(), and by returning -1 we'll eventually (via\n>      +    !!ret) exit with status 1 anyway.\n>      +\n>           Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>       \n>        ## builtin/stash.c ##\n>      @@ builtin/stash.c: static int apply_stash(int argc, const char **argv, const char\n>        \tfree_stash_info(&info);\n>        \treturn ret;\n>        }\n>      -@@ builtin/stash.c: static void assert_stash_ref(struct stash_info *info)\n>      +@@ builtin/stash.c: static int do_drop_stash(struct stash_info *info, int quiet)\n>      + \treturn 0;\n>      + }\n>      +\n>      +-static void assert_stash_ref(struct stash_info *info)\n>      ++static int get_stash_info_assert(struct stash_info *info, int argc,\n>      ++\t\t\t\t const char **argv)\n>      + {\n>      +-\tif (!info->is_stash_ref) {\n>      +-\t\terror(_(\"'%s' is not a stash reference\"), info->revision.buf);\n>      +-\t\tfree_stash_info(info);\n>      +-\t\texit(1);\n>      +-\t}\n>      ++\tint ret = get_stash_info(info, argc, argv);\n>      ++\n>      ++\tif (ret < 0)\n>      ++\t\treturn ret;\n>      ++\n>      ++\tif (!info->is_stash_ref)\n>      ++\t\treturn error(_(\"'%s' is not a stash reference\"), info->revision.buf);\n>      ++\n>      ++\treturn 0;\n>      + }\n>        \n>        static int drop_stash(int argc, const char **argv, const char *prefix)\n>        {\n>      @@ builtin/stash.c: static void assert_stash_ref(struct stash_info *info)\n>        \t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n>        \t\tOPT_END()\n>       @@ builtin/stash.c: static int drop_stash(int argc, const char **argv, const char *prefix)\n>      + \targc = parse_options(argc, argv, prefix, options,\n>        \t\t\t     git_stash_drop_usage, 0);\n>        \n>      - \tif (get_stash_info(&info, argc, argv))\n>      +-\tif (get_stash_info(&info, argc, argv))\n>       -\t\treturn -1;\n>      +-\n>      +-\tassert_stash_ref(&info);\n>      ++\tif (get_stash_info_assert(&info, argc, argv))\n>       +\t\tgoto cleanup;\n>        \n>      - \tassert_stash_ref(&info);\n>      -\n>        \tret = do_drop_stash(&info, quiet);\n>       +cleanup:\n>        \tfree_stash_info(&info);\n>      @@ builtin/stash.c: static int drop_stash(int argc, const char **argv, const char *\n>        \t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n>        \t\tOPT_BOOL(0, \"index\", &index,\n>       @@ builtin/stash.c: static int pop_stash(int argc, const char **argv, const char *prefix)\n>      + \targc = parse_options(argc, argv, prefix, options,\n>        \t\t\t     git_stash_pop_usage, 0);\n>        \n>      - \tif (get_stash_info(&info, argc, argv))\n>      +-\tif (get_stash_info(&info, argc, argv))\n>       -\t\treturn -1;\n>      ++\tif (get_stash_info_assert(&info, argc, argv))\n>       +\t\tgoto cleanup;\n>        \n>      - \tassert_stash_ref(&info);\n>      +-\tassert_stash_ref(&info);\n>        \tif ((ret = do_apply_stash(prefix, &info, index, quiet)))\n>      -@@ builtin/stash.c: static int pop_stash(int argc, const char **argv, const char *prefix)\n>      + \t\tprintf_ln(_(\"The stash entry is kept in case \"\n>      + \t\t\t    \"you need it again.\"));\n>        \telse\n>        \t\tret = do_drop_stash(&info, quiet);\n>        \n>      @@ builtin/stash.c: static void diff_include_untracked(const struct stash_info *inf\n>        \tstruct rev_info rev;\n>        \tstruct strvec stash_args = STRVEC_INIT;\n>        \tstruct strvec revision_args = STRVEC_INIT;\n>      +@@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *prefix)\n>      + \t\t\t      UNTRACKED_ONLY, PARSE_OPT_NONEG),\n>      + \t\tOPT_END()\n>      + \t};\n>      ++\tint do_usage = 0;\n>      +\n>      + \tinit_diff_ui_defaults();\n>      + \tgit_config(git_diff_ui_config, NULL);\n>       @@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *prefix)\n>        \t\t\tstrvec_push(&revision_args, argv[i]);\n>        \t}\n>      @@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *\n>        \t\t}\n>        \t}\n>        \n>      + \targc = setup_revisions(revision_args.nr, revision_args.v, &rev, NULL);\n>      +-\tif (argc > 1) {\n>      +-\t\tfree_stash_info(&info);\n>      +-\t\tusage_with_options(git_stash_show_usage, options);\n>      +-\t}\n>      ++\tif (argc > 1)\n>      ++\t\tgoto usage;\n>      + \tif (!rev.diffopt.output_format) {\n>      + \t\trev.diffopt.output_format = DIFF_FORMAT_PATCH;\n>      + \t\tdiff_setup_done(&rev.diffopt);\n>       @@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *prefix)\n>        \t}\n>        \tlog_tree_diff_flush(&rev);\n>        \n>      -+\tret = diff_result_code(&rev.diffopt, 0);;\n>      ++\tret = diff_result_code(&rev.diffopt, 0);\n>       +cleanup:\n>       +\tstrvec_clear(&stash_args);\n>        \tfree_stash_info(&info);\n>       -\treturn diff_result_code(&rev.diffopt, 0);\n>      ++\tif (do_usage)\n>      ++\t\tusage_with_options(git_stash_show_usage, options);\n>       +\treturn ret;\n>      ++usage:\n>      ++\tdo_usage = 1;\n>      ++\tgoto cleanup;\n>        }\n>        \n>        static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,\n>      @@ builtin/stash.c: static int do_push_stash(const struct pathspec *ps, const char\n>        \tstruct strbuf patch = STRBUF_INIT;\n>        \tstruct strbuf stash_msg_buf = STRBUF_INIT;\n>        \tstruct strbuf untracked_files = STRBUF_INIT;\n>      +@@ builtin/stash.c: static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q\n>      + \t}\n>      +\n>      + done:\n>      ++\tfree_stash_info(&info);\n>      + \tstrbuf_release(&stash_msg_buf);\n>      + \treturn ret;\n>      + }\n> 11:  c9898a5a96b ! 11:  4210232e3a1 revisions API users: add \"goto cleanup\" for release_revisions()\n>      @@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *\n>        \tstrvec_clear(&stash_args);\n>        \tfree_stash_info(&info);\n>       +\trelease_revisions(&rev);\n>      + \tif (do_usage)\n>      + \t\tusage_with_options(git_stash_show_usage, options);\n>        \treturn ret;\n>      - }\n>      -\n>       \n>        ## builtin/submodule--helper.c ##\n>       @@ builtin/submodule--helper.c: static int compute_summary_module_list(struct object_id *head_oid,\n> 12:  6e41d666223 = 12:  1918ca0a85d revisions API users: use release_revisions() in http-push.c\n> 13:  02ca92660af = 13:  6a02b7aae4e revisions API users: use release_revisions() in builtin/log.c\n> 14:  1aeb1127f34 = 14:  ddc7402b054 revisions API users: use release_revisions() with UNLEAK()\n> 15:  c12b3d5a035 ! 15:  03e9bd73d4b revisions API users: use release_revisions() for \"prune_data\" users\n>      @@ builtin/add.c: int add_files_to_cache(const char *prefix,\n>        }\n>        \n>       \n>      - ## builtin/stash.c ##\n>      -@@ builtin/stash.c: static int check_changes_tracked_files(const struct pathspec *ps)\n>      -\n>      - done:\n>      - \tclear_pathspec(&rev.prune_data);\n>      -+\trelease_revisions(&rev);\n>      - \treturn ret;\n>      - }\n>      -\n>      -\n>        ## diff-lib.c ##\n>       @@ diff-lib.c: int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)\n>        \tif (diff_cache(&revs, tree_oid, NULL, 1))\n> 16:  2e351702677 <  -:  ----------- revisions API: have release_revisions() release \"commits\"\n>   -:  ----------- > 16:  285673e40bf revisions API: have release_revisions() release \"commits\"\n> 17:  f67f1f5a575 ! 17:  ca659940ee5 revisions API: have release_revisions() release \"mailmap\"\n>      @@ builtin/commit.c: static const char *find_author_by_nickname(const char *name)\n>        \t}\n>       \n>        ## revision.c ##\n>      -@@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)\n>      - \t}\n>      +@@ revision.c: int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n>      + \treturn left;\n>        }\n>        \n>       +static void release_revisions_mailmap(struct string_list *mailmap)\n>      @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)\n>       +\n>        void release_revisions(struct rev_info *revs)\n>        {\n>      - \trelease_revisions_commit_list(revs);\n>      + \tfree_commit_list(revs->commits);\n>        \tobject_array_clear(&revs->pending);\n>       +\trelease_revisions_mailmap(revs->mailmap);\n>        }\n> 18:  1df61550e5a ! 18:  7987c748ba4 revisions API: have release_revisions() release \"cmdline\"\n>      @@ Commit message\n>           Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>       \n>        ## revision.c ##\n>      -@@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)\n>      - \t}\n>      +@@ revision.c: int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n>      + \treturn left;\n>        }\n>        \n>       +static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)\n>      @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)\n>       +\t\treturn;\n>       +\tfor (i = 0; i < cmdline->nr; i++)\n>       +\t\tfree((char *)cmdline->rev[i].name);\n>      -+\tFREE_AND_NULL(cmdline->rev);\n>      ++\tfree(cmdline->rev);\n>       +}\n>       +\n>        static void release_revisions_mailmap(struct string_list *mailmap)\n>      @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)\n>        \tif (!mailmap)\n>       @@ revision.c: void release_revisions(struct rev_info *revs)\n>        {\n>      - \trelease_revisions_commit_list(revs);\n>      + \tfree_commit_list(revs->commits);\n>        \tobject_array_clear(&revs->pending);\n>       +\trelease_revisions_cmdline(&revs->cmdline);\n>        \trelease_revisions_mailmap(revs->mailmap);\n> 19:  065c3cc91b2 ! 19:  e6e87ce3608 revisions API: have release_revisions() release \"filter\"\n>      @@ Commit message\n>       \n>        ## revision.c ##\n>       @@ revision.c: void release_revisions(struct rev_info *revs)\n>      - \trelease_revisions_commit_list(revs);\n>      + \tfree_commit_list(revs->commits);\n>        \tobject_array_clear(&revs->pending);\n>        \trelease_revisions_cmdline(&revs->cmdline);\n>       +\tlist_objects_filter_release(&revs->filter);\n> 20:  806fef06b87 = 20:  ebdcdc88d79 revisions API: have release_revisions() release \"grep_filter\"\n> 21:  fabbe452a14 = 21:  84c8714571a revisions API: have release_revisions() release \"prune_data\"\n> 22:  cb201fa1914 ! 22:  d96642d3502 revisions API: clear \"boundary_commits\" in release_revisions()\n>      @@ Commit message\n>        ## revision.c ##\n>       @@ revision.c: void release_revisions(struct rev_info *revs)\n>        {\n>      - \trelease_revisions_commit_list(revs);\n>      + \tfree_commit_list(revs->commits);\n>        \tobject_array_clear(&revs->pending);\n>       +\tobject_array_clear(&revs->boundary_commits);\n>        \trelease_revisions_cmdline(&revs->cmdline);\n> 23:  0363a69073c = 23:  0e9745d9ee1 revisions API: release \"reflog_info\" in release revisions()\n> 24:  bad359b2983 = 24:  c7f3e92a8d1 revisions API: call diff_free(&revs->pruning) in revisions_release()\n> 25:  9a51c1ae1d1 = 25:  d428d752462 revisions API: have release_revisions() release \"date_mode\"\n> 26:  b56db37940d ! 26:  fbf233ec768 revisions API: have release_revisions() release \"topo_walk_info\"\n>      @@ revision.c: static void release_revisions_mailmap(struct string_list *mailmap)\n>       +\n>        void release_revisions(struct rev_info *revs)\n>        {\n>      - \trelease_revisions_commit_list(revs);\n>      + \tfree_commit_list(revs->commits);\n>       @@ revision.c: void release_revisions(struct rev_info *revs)\n>        \tfree_grep_patterns(&revs->grep_filter);\n>        \tdiff_free(&revs->pruning);\n> 27:  b02cb8fcded = 27:  ec2c77ce869 revisions API: add a TODO for diff_free(&revs->diffopt)\n"},{"id":"452983","messageId":"220403.86mth2mfrh.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"2e75adb7-e52d-62a9-0304-4076816f82e1@gmail.com","subject":"Re: [PATCH v5 14/27] revisions API users: use release_revisions() with UNLEAK()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-03T13:55:10Z","receivedAt":"2022-04-03T13:57:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, Apr 03 2022, Phillip Wood wrote:\n\n> Hi Ævar\n>\n> [continuing with v5 from where I left off with v4]\n>\n> On 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:\n>> Use a release_revisions() with those \"struct rev_list\" users which\n>> already \"UNLEAK\" the struct. It may seem odd to simultaneously attempt\n>> to free() memory, but also to explicitly ignore whether we have memory\n>> leaks in the same.\n>> As explained in preceding commits this is being done to use the\n>> built-in commands as a guinea pig for whether the release_revisions()\n>> function works as expected, we'd like to test e.g. whether we segfault\n>> as we change it. In subsequent commits we'll then remove these\n>> UNLEAK() as the function is made to free the memory that caused us to\n>> add them in the first place.\n>\n> I'm a bit confused by this, the previous commit argued in favor of\n> removing UNLEAK() so would could see the leaks and fix them, this is \n> saying we should hide the leaks.\n\n..until the UNLEAK() is removed later in the sier.s\n\nAll commits in this series pass with the SANITIZE=leak CI check. In this\ncase it would result in too much churn to remove the UNLEAK() before we\nhave this solving some of the more meaningful leaks, and if we do this\nlater we don't have all codepaths that can use release_revisions() using\nit as we free things one at a time.\n\n> Best Wishes\n>\n> Phillip\n>\n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>>   builtin/diff-index.c | 4 +++-\n>>   builtin/diff.c       | 1 +\n>>   2 files changed, 4 insertions(+), 1 deletion(-)\n>> diff --git a/builtin/diff-index.c b/builtin/diff-index.c\n>> index 5fd23ab5b6c..3a83183c312 100644\n>> --- a/builtin/diff-index.c\n>> +++ b/builtin/diff-index.c\n>> @@ -71,5 +71,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)\n>>   \t}\n>>   \tresult = run_diff_index(&rev, option);\n>>   \tUNLEAK(rev);\n>> -\treturn diff_result_code(&rev.diffopt, result);\n>> +\tresult = diff_result_code(&rev.diffopt, result);\n>> +\trelease_revisions(&rev);\n>> +\treturn result;\n>>   }\n>> diff --git a/builtin/diff.c b/builtin/diff.c\n>> index bb7fafca618..dd48336da56 100644\n>> --- a/builtin/diff.c\n>> +++ b/builtin/diff.c\n>> @@ -595,6 +595,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n>>   \tif (1 < rev.diffopt.skip_stat_unmatch)\n>>   \t\trefresh_index_quietly();\n>>   \tUNLEAK(rev);\n>> +\trelease_revisions(&rev);\n>>   \tUNLEAK(ent);\n>>   \tUNLEAK(blob);\n>>   \treturn result;\n\n"},{"id":"452984","messageId":"220403.86ilrqmflb.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"5044b964-0605-356c-3e03-1aad8cbbe619@gmail.com","subject":"Re: [PATCH v5 17/27] revisions API: have release_revisions() release \"mailmap\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-03T13:57:48Z","receivedAt":"2022-04-03T14:01:06Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, Apr 03 2022, Phillip Wood wrote:\n\n> Hi Ævar\n>\n> On 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:\n>> Extend the the release_revisions() function so that it frees the\n>> \"mailmap\" in the \"struct rev_info\".\n>> The log family of functions now calls the clear_mailmap() function\n>> added in fa8afd18e5a (revisions API: provide and use a\n>> release_revisions(), 2021-09-19), allowing us to whitelist some tests\n>> with \"TEST_PASSES_SANITIZE_LEAK=true\".\n>> Unfortunately having a pointer to a mailmap in \"struct rev_info\"\n>> instead of an embedded member that we \"own\" get a bit messy, as can be\n>> seen in the change to builtin/commit.c.\n>> When we free() this data we won't be able to tell apart a pointer to\n>> a\n>> \"mailmap\" on the heap from one on the stack. As seen in\n>> ea57bc0d41b (log: add --use-mailmap option, 2013-01-05) the \"log\"\n>> family allocates it on the heap, but in the find_author_by_nickname()\n>> code added in ea16794e430 (commit: search author pattern against\n>> mailmap, 2013-08-23) we allocated it on the stack instead.\n>> Ideally we'd simply change that member to a \"struct string_list\n>> mailmap\" and never free() the \"mailmap\" itself, but that would be a\n>> much larger change to the revisions API.\n>\n> I agree it makes sense to leave that for now\n>\n>> We have code that needs to hand an existing \"mailmap\" to a \"struct\n>> rev_info\", while we could change all of that, let's not go there\n>> now.\n>> The complexity isn't in the ownership of the \"mailmap\" per-se, but\n>> that various things assume a \"rev_info.mailmap == NULL\" means \"doesn't\n>> want mailmap\", if we changed that to an init'd \"struct string_list\n>> we'd need to carefully refactor things to change those assumptions.\n>> Let's instead always free() it, and simply declare that if you add\n>> such a \"mailmap\" it must be allocated on the heap. Any modern libc\n>> will correctly panic if we free() a stack variable, so this should be\n>> safe going forward.\n>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n>> ---\n>>   builtin/commit.c                   | 5 ++---\n>>   revision.c                         | 9 +++++++++\n>>   t/t0056-git-C.sh                   | 1 +\n>>   t/t3302-notes-index-expensive.sh   | 1 +\n>>   t/t4055-diff-context.sh            | 1 +\n>>   t/t4066-diff-emit-delay.sh         | 1 +\n>>   t/t7008-filter-branch-null-sha1.sh | 1 +\n>>   7 files changed, 16 insertions(+), 3 deletions(-)\n>> diff --git a/builtin/commit.c b/builtin/commit.c\n>> index c7eda9bbb72..cd6cebcf8c8 100644\n>> --- a/builtin/commit.c\n>> +++ b/builtin/commit.c\n>> @@ -1100,7 +1100,6 @@ static const char *find_author_by_nickname(const char *name)\n>>   \tstruct rev_info revs;\n>>   \tstruct commit *commit;\n>>   \tstruct strbuf buf = STRBUF_INIT;\n>> -\tstruct string_list mailmap = STRING_LIST_INIT_NODUP;\n>>   \tconst char *av[20];\n>>   \tint ac = 0;\n>>   @@ -1111,7 +1110,8 @@ static const char\n>> *find_author_by_nickname(const char *name)\n>>   \tav[++ac] = buf.buf;\n>>   \tav[++ac] = NULL;\n>>   \tsetup_revisions(ac, av, &revs, NULL);\n>> -\trevs.mailmap = &mailmap;\n>> +\trevs.mailmap = xmalloc(sizeof(struct string_list));\n>> +\tstring_list_init_nodup(revs.mailmap);\n>\n> This is a common pattern in one of the previous patches, is it worth\n> adding helpers to allocate and initialize a struct string_list? Maybe \n> string_list_new_nodup() and string_list_new_dup().\n\nMaybe, but generally in the git codebase things malloc and then init(),\nif we're going to add something like this *_new() that would be a change\nfor a lot more APIs than just mailmap.\n\nAnd if it's just for mailmap I don't see how the inconsistency with\nother code would be worth it.\n\n>>   \tread_mailmap(revs.mailmap);\n>>     \tif (prepare_revision_walk(&revs))\n>> @@ -1122,7 +1122,6 @@ static const char *find_author_by_nickname(const char *name)\n>>   \t\tctx.date_mode.type = DATE_NORMAL;\n>>   \t\tstrbuf_release(&buf);\n>>   \t\tformat_commit_message(commit, \"%aN <%aE>\", &buf, &ctx);\n>> -\t\tclear_mailmap(&mailmap);\n>>   \t\trelease_revisions(&revs);\n>>   \t\treturn strbuf_detach(&buf, NULL);\n>>   \t}\n>> diff --git a/revision.c b/revision.c\n>> index 553f7de8250..622f0faecc4 100644\n>> --- a/revision.c\n>> +++ b/revision.c\n>> @@ -2926,10 +2926,19 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n>>   \treturn left;\n>>   }\n>>   +static void release_revisions_mailmap(struct string_list\n>> *mailmap)\n>> +{\n>> +\tif (!mailmap)\n>> +\t\treturn;\n>> +\tclear_mailmap(mailmap);\n>> +\tfree(mailmap);\n>> +}\n>\n> It's not a big issue but if there are no other users of this then it\n> could just go inside release_revisions, my impression is that this \n> series builds a collection of very small functions whose only caller\n> is release_revisions()\n\nYes, these are just trivial static helpers so that each line in\nrelease_revisions() corresponds to a member of the struct, without\nloops, indentation for \"don't free this\" etc.\n\nTo the machine code it makes no difference at higher optimization\nlevels.\n"},{"id":"452985","messageId":"220403.86ee2emfgs.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"02da5550-3137-b579-93c5-bf245a8cbc2a@gmail.com","subject":"Re: [PATCH v5 18/27] revisions API: have release_revisions() release \"cmdline\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-03T14:01:39Z","receivedAt":"2022-04-03T14:03:39Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, Apr 03 2022, Phillip Wood wrote:\n\n> Hi Ævar\n>\n> On 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:\n>>   +static void release_revisions_cmdline(struct rev_cmdline_info\n>> *cmdline)\n>> +{\n>> +\tunsigned int i;\n>> +\n>> +\tif (!cmdline)\n>> +\t\treturn;\n>\n> I don't think we need this guard, the only instances of struct\n> rev_cmdline_info exist within struct rev_info, as far as I can see it\n> is never created on its own.\n\nYes, it won't ever be NULL. I'll fix that (missed it with the other NULL\ncheck cargo-culting).\n\n>> +\tfor (i = 0; i < cmdline->nr; i++)\n>> +\t\tfree((char *)cmdline->rev[i].name);\n>> +\tfree(cmdline->rev);\n>\n> This could just be in release_revisions()\n\nDitto <220403.86ilrqmflb.gmgdl@evledraar.gmail.com>. I.e. I'd prefer to\nkeep these for readability\n"},{"id":"452986","messageId":"220403.86a6d2mfbf.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"5b93870c-39eb-39eb-e6a5-92b4e1cf8f12@gmail.com","subject":"Re: [PATCH v5 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-03T14:04:42Z","receivedAt":"2022-04-03T14:06:50Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, Apr 03 2022, Phillip Wood wrote:\n\n> Hi Ævar\n>\n> On 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:\n>> Call diff_free() on the \"pruning\" member of \"struct rev_info\".  Doing\n>> so makes several tests pass under SANITIZE=leak.\n>> This was also the last missing piece that allows us to remove the\n>> UNLEAK() in \"cmd_diff\" and \"cmd_diff_index\", which allows us to use\n>> those commands as a canary for general leaks in the revisions API. See\n>> [1] for further rationale, and 886e1084d78 (builtin/: add UNLEAKs,\n>> 2017-10-01) for the commit that added the UNLEAK() there.\n>\n> Oh is the the answer to my confusion about patch 14? I wonder if the\n> change could come earlier so we can remove the UNLEAK()s all at once?\n\nAs noted in <220403.86mth2mfrh.gmgdl@evledraar.gmail.com> (where I meant\nto say \"series\") It's this specific change that will free enough things\nfor the tests to pass without these UNLEAK()s.\n\nAnd no, it can't come earlier beacuse just \"pruning\" or any other member\nisn't sufficient, this is just the last straw.\n\nIOW the API callers weren't calling UNLEAK() on specific members of\n\"rev\", but the whole struct. So we needed to fix a lot of leaks before\nwe colud remove these.\n"},{"id":"452987","messageId":"220403.865ynqmeun.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"14c17fa9-e9ff-ac5f-dbda-4a566ed09fd3@gmail.com","subject":"Re: [PATCH v4 13/27] revisions API users: use release_revisions() in builtin/log.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-03T14:07:44Z","receivedAt":"2022-04-03T14:16:55Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sat, Apr 02 2022, Phillip Wood wrote:\n\n[A comment on v4, but also applies to v5 I think]\n\n> On 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:\n>> In preparation for having the \"log\" family of functions make wider use\n>> of release_revisions() let's have them call it just before\n>> exiting. This changes the \"log\", \"whatchanged\", \"show\",\n>> \"format-patch\", etc. commands, all of which live in this file.\n>> The release_revisions() API still only frees the \"pending\" member,\n>> but\n>> will learn to release more members of \"struct rev_info\" in subsequent\n>> commits.\n>> In the case of \"format-patch\" revert the addition of UNLEAK() in\n>> dee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16),\n>> which will cause several tests that previously passed under\n>> \"TEST_PASSES_SANITIZE_LEAK=true\" to start failing.\n>> In subsequent commits we'll now be able to use those tests to check\n>> whether that part of the API is really leaking memory, and will fix\n>> all of those memory leaks. Removing the UNLEAK() allows us to make\n>> incremental progress in that direction. See [1] for further details\n>> about this approach.\n>\n> This breaks \"git bisect\" but only when running the test suite to\n> detect leaks so I guess that's not too bad. An alternative would be to\n> manually remove the UNLEAK() when you're testing rather than\n> committing the change.\n\nIt doesn't, for this series each individual commit passes with\n\n    make test\n    GIT_TEST_PASSING_SANITIZE_LEAK=true make test SANITIZE=leak \n\nAnd also in a stricter mode that I have locally (not in git yet):\n\n    make test\n    GIT_TEST_PASSING_SANITIZE_LEAK=check make test SANITIZE=leak \n\nWhich ensures not only that the tests we marked as leak free pass, but\nthat no other tests we *haven't* marked pass unexpectedly (requires prep\nchanges before this series to mark the still-not-marked-but-should-be\ntests).\n\nI think that should address/help explain things re your questions about\nsome of the UNLEAK() back-and-forth.\n\nI.e. there's a few changes that are in this series just so it can pass\nin that \"GIT_TEST_PASSING_SANITIZE_LEAK=check\" mode, but it would still\npass in \"GIT_TEST_PASSING_SANITIZE_LEAK=true\", i.e. because we'd make\nsome new test pass unexpectedly.\n\nBut I think maintaining the 1=1 correspondance really helps to follow\nalong with this, i.e. tests are tweaked as they become leak-free, and we\n(or well, mostly I) can be confident that I marked all the relevant\nnewlry passing ones, and that there are no regressions in-between.\n\n\n>>   /*\n>>    * This gives a rough estimate for how many commits we\n>>    * will print out in the list.\n>> @@ -558,7 +564,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)\n>>   \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n>>   \tif (!rev.diffopt.output_format)\n>>   \t\trev.diffopt.output_format = DIFF_FORMAT_RAW;\n>> -\treturn cmd_log_walk(&rev);\n>> +\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n>\n> This is a rather unusual pattern, at first I wondered if there were\n> going to be more added to the body of cmd_log_deinit() in later\n> commits but there isn't so why not just call release_revisions() here\n> to be consistent with the other release_revisions() call that are\n> added in other patches?\n\nIt's just a way to save every single call to this callsite a change on\ntop like this:\n\t\n\tdiff --git a/builtin/log.c b/builtin/log.c\n\tindex 5dad70aa47e..ece03536bed 100644\n\t--- a/builtin/log.c\n\t+++ b/builtin/log.c\n\t@@ -684,8 +684,11 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n\t \topt.tweak = show_setup_revisions_tweak;\n\t \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n\t \n\t-\tif (!rev.no_walk)\n\t-\t\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n\t+\tif (!rev.no_walk) {\n\t+\t\tret = cmd_log_walk(&rev);\n\t+\t\trelease_revisions(&rev);\n\t+\t\treturn ret;\n\t+\t}\n\t \n\t \tcount = rev.pending.nr;\n\t \tobjects = rev.pending.objects;\n\nWhich, given that there's 6 of them nicely cuts down on the resulting\nverbosity.\n"},{"id":"452988","messageId":"220403.861qyeme56.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"5d3345d9-3c7c-a761-9c7a-03468a653e48@gmail.com","subject":"Re: [PATCH v5 00/27] revision.[ch]: add and use release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-03T14:18:09Z","receivedAt":"2022-04-03T14:32:30Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Sun, Apr 03 2022, Phillip Wood wrote:\n\n> Hi Ævar\n>\n> I've left a few comments on this series for the patches that I had not\n> looked at in v4. There's nothing major, the changes mostly make sense\n> to me (though I'm not familiar with the revisions api)\n\nThanks a lot. I'll re-roll a v6 in response, but will wait a bit for any\nother comments.\n\n> On 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:\n>> This series reduces the memory leaks spotted by our tests by at least\n>> half, see v3[1] for a more general overview.\n>> Thanks a lot to Phillip Wood and Junio for the v4 review! I think\n>> this\n>> v5 should address all of the things brought up in v4 in one way or\n>> another, changes:\n>>   * The \"extra_headers\" or \"ref_message_ids\" freeing is the same,\n>> but\n>>     the commit message is altered to explain that commit.\n>\n> I'm still not that convinced that these are owned by the code in\n> builtin/log.c but lets leave that for now.\n\nThanks.\n\n> The rest of the changes\n> look good. Thanks for working on this, when I use LSAN on my patches\n> before submitting them I often see a lot of existing leaks coming from\n> struct rev_info.\n\nYeah it'll become a lot more useful now.\n"},{"id":"452994","messageId":"e71173fc-1b75-cc1e-df8e-efad512bf173@gmail.com","threadId":"57525","inReplyTo":"patch-v5-23.27-0e9745d9ee1-20220402T102002Z-avarab@gmail.com","subject":"Re: [PATCH v5 23/27] revisions API: release \"reflog_info\" in release revisions()","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2022-04-03T15:19:53Z","receivedAt":"2022-04-03T15:20:14Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Ævar\n\n[It appears I forgot to press send earlier]\n\nOn 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:\n> Add a missing reflog_walk_info_release() to \"reflog-walk.c\" and use it\n> in release_revisions().\n> \n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>   reflog-walk.c            | 24 +++++++++++++++++++++++-\n>   reflog-walk.h            |  1 +\n>   revision.c               |  1 +\n>   t/t0100-previous.sh      |  1 +\n>   t/t1401-symbolic-ref.sh  |  2 ++\n>   t/t1411-reflog-show.sh   |  1 +\n>   t/t1412-reflog-loop.sh   |  2 ++\n>   t/t1415-worktree-refs.sh |  1 +\n>   8 files changed, 32 insertions(+), 1 deletion(-)\n> \n> diff --git a/reflog-walk.c b/reflog-walk.c\n> index 8ac4b284b6b..7aa6595a51f 100644\n> --- a/reflog-walk.c\n> +++ b/reflog-walk.c\n> @@ -8,7 +8,7 @@\n>   \n>   struct complete_reflogs {\n>   \tchar *ref;\n> -\tconst char *short_ref;\n> +\tchar *short_ref;\n\nAs this struct is only used internally I think changing the type rather \nthan casting when we free it makes sense.\n\nBest Wishes\n\nPhillip\n\n>   \tstruct reflog_info {\n>   \t\tstruct object_id ooid, noid;\n>   \t\tchar *email;\n> @@ -51,9 +51,16 @@ static void free_complete_reflog(struct complete_reflogs *array)\n>   \t}\n>   \tfree(array->items);\n>   \tfree(array->ref);\n> +\tfree(array->short_ref);\n>   \tfree(array);\n>   }\n>   \n> +static void complete_reflogs_clear(void *util, const char *str)\n> +{\n> +\tstruct complete_reflogs *array = util;\n> +\tfree_complete_reflog(array);\n> +}\n> +\n>   static struct complete_reflogs *read_complete_reflog(const char *ref)\n>   {\n>   \tstruct complete_reflogs *reflogs =\n> @@ -116,6 +123,21 @@ void init_reflog_walk(struct reflog_walk_info **info)\n>   \t(*info)->complete_reflogs.strdup_strings = 1;\n>   }\n>   \n> +void reflog_walk_info_release(struct reflog_walk_info *info)\n> +{\n> +\tsize_t i;\n> +\n> +\tif (!info)\n> +\t\treturn;\n> +\n> +\tfor (i = 0; i < info->nr; i++)\n> +\t\tfree(info->logs[i]);\n> +\tstring_list_clear_func(&info->complete_reflogs,\n> +\t\t\t       complete_reflogs_clear);\n> +\tfree(info->logs);\n> +\tfree(info);\n> +}\n> +\n>   int add_reflog_for_walk(struct reflog_walk_info *info,\n>   \t\tstruct commit *commit, const char *name)\n>   {\n> diff --git a/reflog-walk.h b/reflog-walk.h\n> index e9e00ffd479..8076f10d9fb 100644\n> --- a/reflog-walk.h\n> +++ b/reflog-walk.h\n> @@ -8,6 +8,7 @@ struct reflog_walk_info;\n>   struct date_mode;\n>   \n>   void init_reflog_walk(struct reflog_walk_info **info);\n> +void reflog_walk_info_release(struct reflog_walk_info *info);\n>   int add_reflog_for_walk(struct reflog_walk_info *info,\n>   \t\t\tstruct commit *commit, const char *name);\n>   void show_reflog_message(struct reflog_walk_info *info, int,\n> diff --git a/revision.c b/revision.c\n> index 40f59c441f7..e972addd8fc 100644\n> --- a/revision.c\n> +++ b/revision.c\n> @@ -2955,6 +2955,7 @@ void release_revisions(struct rev_info *revs)\n>   \tclear_pathspec(&revs->prune_data);\n>   \trelease_revisions_mailmap(revs->mailmap);\n>   \tfree_grep_patterns(&revs->grep_filter);\n> +\treflog_walk_info_release(revs->reflog_info);\n>   }\n>   \n>   static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n> diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh\n> index 69beb59f627..a16cc3d2983 100755\n> --- a/t/t0100-previous.sh\n> +++ b/t/t0100-previous.sh\n> @@ -5,6 +5,7 @@ test_description='previous branch syntax @{-n}'\n>   GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n>   export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   \n>   test_expect_success 'branch -d @{-1}' '\n> diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh\n> index 132a1b885ac..9fb0b90f252 100755\n> --- a/t/t1401-symbolic-ref.sh\n> +++ b/t/t1401-symbolic-ref.sh\n> @@ -1,6 +1,8 @@\n>   #!/bin/sh\n>   \n>   test_description='basic symbolic-ref tests'\n> +\n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   \n>   # If the tests munging HEAD fail, they can break detection of\n> diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh\n> index 0bb319b944a..3770ceffafd 100755\n> --- a/t/t1411-reflog-show.sh\n> +++ b/t/t1411-reflog-show.sh\n> @@ -4,6 +4,7 @@ test_description='Test reflog display routines'\n>   GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n>   export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   \n>   test_expect_success 'setup' '\n> diff --git a/t/t1412-reflog-loop.sh b/t/t1412-reflog-loop.sh\n> index 977603f7f1c..ff30874f940 100755\n> --- a/t/t1412-reflog-loop.sh\n> +++ b/t/t1412-reflog-loop.sh\n> @@ -1,6 +1,8 @@\n>   #!/bin/sh\n>   \n>   test_description='reflog walk shows repeated commits again'\n> +\n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   \n>   test_expect_success 'setup commits' '\n> diff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh\n> index a3e6ea08088..3b531842dd4 100755\n> --- a/t/t1415-worktree-refs.sh\n> +++ b/t/t1415-worktree-refs.sh\n> @@ -2,6 +2,7 @@\n>   \n>   test_description='per-worktree refs'\n>   \n> +TEST_PASSES_SANITIZE_LEAK=true\n>   . ./test-lib.sh\n>   \n>   test_expect_success 'setup' '\n"},{"id":"453008","messageId":"xmqq5ynpvnv1.fsf@gitster.g","threadId":"57525","inReplyTo":"14c17fa9-e9ff-ac5f-dbda-4a566ed09fd3@gmail.com","subject":"Re: [PATCH v4 13/27] revisions API users: use release_revisions() in builtin/log.c","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2022-04-03T21:49:38Z","receivedAt":"2022-04-03T21:49:45Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Phillip Wood <phillip.wood123@gmail.com> writes:\n\n>>   +static int cmd_log_deinit(int ret, struct rev_info *rev)\n>> +{\n>> +\trelease_revisions(rev);\n>> +\treturn ret;\n>> +}\n>\n>\n>>   /*\n>>    * This gives a rough estimate for how many commits we\n>>    * will print out in the list.\n>> @@ -558,7 +564,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)\n>>   \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n>>   \tif (!rev.diffopt.output_format)\n>>   \t\trev.diffopt.output_format = DIFF_FORMAT_RAW;\n>> -\treturn cmd_log_walk(&rev);\n>> +\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n>\n> This is a rather unusual pattern, at first I wondered if there were\n> going to be more added to the body of cmd_log_deinit() in later\n> commits but there isn't so why not just call release_revisions() here\n> to be consistent with the other release_revisions() call that are\n> added in other patches?\n\nIt is being cute and clever by not requiring a temporary variable\nret, where you would normally say\n\n\tint ret = 0; /* assume success */\n\n\t... a lot of code ...\n\tret = cmd_log_walk(&rev);\n        release_revisions(&rev);\n\treturn ret;\n\nI agree that this looks confusing; if this pattern can become\nmajority locally in the file, I guess it would be OK---at that point\nwe can claim that it is the (new) usual pattern ;-).\n\n\n\n\n"},{"id":"453030","messageId":"ce2c72e5-f172-b017-e62d-2eebc5ba1631@gmail.com","threadId":"57525","inReplyTo":"220403.86ilrqmflb.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v5 17/27] revisions API: have release_revisions() release \"mailmap\"","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2022-04-04T09:19:57Z","receivedAt":"2022-04-04T09:20:03Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Ævar\n\nOn 03/04/2022 14:57, Ævar Arnfjörð Bjarmason wrote:\n> [...]\n>>>    \tav[++ac] = buf.buf;\n>>>    \tav[++ac] = NULL;\n>>>    \tsetup_revisions(ac, av, &revs, NULL);\n>>> -\trevs.mailmap = &mailmap;\n>>> +\trevs.mailmap = xmalloc(sizeof(struct string_list));\n>>> +\tstring_list_init_nodup(revs.mailmap);\n>>\n>> This is a common pattern in one of the previous patches, is it worth\n>> adding helpers to allocate and initialize a struct string_list? Maybe\n>> string_list_new_nodup() and string_list_new_dup().\n> \n> Maybe, but generally in the git codebase things malloc and then init(),\n> if we're going to add something like this *_new() that would be a change\n> for a lot more APIs than just mailmap.\n> \n> And if it's just for mailmap I don't see how the inconsistency with\n> other code would be worth it.\n\nIt's for struct string_list not for mailmap and could be used for all \nthe conversions in patch 3. In general the split between allocation and \ninitialization is useful because it allows us to allocate structures on \nthe stack where possible or embed them in other structures. However if \nthere is a structure that is often allocated on the heap then I don't \nthink there is anything wrong with having a combined \nallocate-and-initialize helper function which makes the code shorter and \neliminates the possibility of passing the wrong size to malloc().\n\n>>> diff --git a/revision.c b/revision.c\n>>> index 553f7de8250..622f0faecc4 100644\n>>> --- a/revision.c\n>>> +++ b/revision.c\n>>> @@ -2926,10 +2926,19 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n>>>    \treturn left;\n>>>    }\n>>>    +static void release_revisions_mailmap(struct string_list\n>>> *mailmap)\n>>> +{\n>>> +\tif (!mailmap)\n>>> +\t\treturn;\n>>> +\tclear_mailmap(mailmap);\n>>> +\tfree(mailmap);\n>>> +}\n>>\n>> It's not a big issue but if there are no other users of this then it\n>> could just go inside release_revisions, my impression is that this\n>> series builds a collection of very small functions whose only caller\n>> is release_revisions()\n> \n> Yes, these are just trivial static helpers so that each line in\n> release_revisions() corresponds to a member of the struct, without\n> loops, indentation for \"don't free this\" etc.\n\nFair enough\n\n> To the machine code it makes no difference at higher optimization\n> levels.\n\nIndeed, not that these functions are particularly performance sensitive \nin the first place.\n\nBest Wishes\n\nPhillip\n"},{"id":"453031","messageId":"9b577148-dc7d-cfdd-56ae-edd933cfd1fa@gmail.com","threadId":"57525","inReplyTo":"220403.865ynqmeun.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v4 13/27] revisions API users: use release_revisions() in builtin/log.c","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2022-04-04T09:27:02Z","receivedAt":"2022-04-04T09:27:19Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Ævar\n\nOn 03/04/2022 15:07, Ævar Arnfjörð Bjarmason wrote:\n> \n> On Sat, Apr 02 2022, Phillip Wood wrote:\n> \n> [A comment on v4, but also applies to v5 I think]\n> \n>> On 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:\n>>> In preparation for having the \"log\" family of functions make wider use\n>>> of release_revisions() let's have them call it just before\n>>> exiting. This changes the \"log\", \"whatchanged\", \"show\",\n>>> \"format-patch\", etc. commands, all of which live in this file.\n>>> The release_revisions() API still only frees the \"pending\" member,\n>>> but\n>>> will learn to release more members of \"struct rev_info\" in subsequent\n>>> commits.\n>>> In the case of \"format-patch\" revert the addition of UNLEAK() in\n>>> dee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16),\n>>> which will cause several tests that previously passed under\n>>> \"TEST_PASSES_SANITIZE_LEAK=true\" to start failing.\n>>> In subsequent commits we'll now be able to use those tests to check\n>>> whether that part of the API is really leaking memory, and will fix\n>>> all of those memory leaks. Removing the UNLEAK() allows us to make\n>>> incremental progress in that direction. See [1] for further details\n>>> about this approach.\n>>\n>> This breaks \"git bisect\" but only when running the test suite to\n>> detect leaks so I guess that's not too bad. An alternative would be to\n>> manually remove the UNLEAK() when you're testing rather than\n>> committing the change.\n> \n> It doesn't, for this series each individual commit passes with\n\nOh I'd missed that, thanks for explaining\n\n>      make test\n>      GIT_TEST_PASSING_SANITIZE_LEAK=true make test SANITIZE=leak\n> \n> And also in a stricter mode that I have locally (not in git yet):\n> \n>      make test\n>      GIT_TEST_PASSING_SANITIZE_LEAK=check make test SANITIZE=leak\n> \n> Which ensures not only that the tests we marked as leak free pass, but\n> that no other tests we *haven't* marked pass unexpectedly (requires prep\n> changes before this series to mark the still-not-marked-but-should-be\n> tests).\n> \n> I think that should address/help explain things re your questions about\n> some of the UNLEAK() back-and-forth.\n\nYes it does, the next patch makes sense to me now as well thanks\n\n> [...]\n>>> @@ -558,7 +564,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)\n>>>    \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n>>>    \tif (!rev.diffopt.output_format)\n>>>    \t\trev.diffopt.output_format = DIFF_FORMAT_RAW;\n>>> -\treturn cmd_log_walk(&rev);\n>>> +\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n>>\n>> This is a rather unusual pattern, at first I wondered if there were\n>> going to be more added to the body of cmd_log_deinit() in later\n>> commits but there isn't so why not just call release_revisions() here\n>> to be consistent with the other release_revisions() call that are\n>> added in other patches?\n> \n> It's just a way to save every single call to this callsite a change on\n> top like this:\n> \t\n> \tdiff --git a/builtin/log.c b/builtin/log.c\n> \tindex 5dad70aa47e..ece03536bed 100644\n> \t--- a/builtin/log.c\n> \t+++ b/builtin/log.c\n> \t@@ -684,8 +684,11 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n> \t \topt.tweak = show_setup_revisions_tweak;\n> \t \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n> \t\n> \t-\tif (!rev.no_walk)\n> \t-\t\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n> \t+\tif (!rev.no_walk) {\n> \t+\t\tret = cmd_log_walk(&rev);\n> \t+\t\trelease_revisions(&rev);\n> \t+\t\treturn ret;\n> \t+\t}\n> \t\n> \t \tcount = rev.pending.nr;\n> \t \tobjects = rev.pending.objects;\n> \n> Which, given that there's 6 of them nicely cuts down on the resulting\n> verbosity.\n\nIf you want to adopt this pattern more widely then if there was a helper \nfunction in revisions.c it could be used in patches 11 and 14 as well as \nthis one I think.\n\nBest Wishes\n\nPhillip\n"},{"id":"453564","messageId":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v5-00.27-00000000000-20220402T102002Z-avarab@gmail.com","subject":"[PATCH v6 00/27] revision.[ch]: add and use release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:29Z","receivedAt":"2022-04-13T20:02:08Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"This series reduces the memory leaks spotted by our tests by at least\nhalf, see v3[1] for a more general overview.\n\nThe only change in this v6 is a trivial adjustment of the freeing for\nthe \"cmdline\" to get rid of a boilerplate was-always-unnecessary NULL\ncheck for the \"cmdline\", which could never be NULL in that\ncase. Thanks to Phillip Wood for spotting it.\n\n1. https://lore.kernel.org/git/cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com/\n\nÆvar Arnfjörð Bjarmason (27):\n  t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"\n  blame: use \"goto cleanup\" for cleanup_scoreboard()\n  string_list API users: use string_list_init_{no,}dup\n  format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"\n  cocci: add and apply free_commit_list() rules\n  revision.[ch]: provide and start using a release_revisions()\n  revisions API users: add straightforward release_revisions()\n  revision.[ch]: document and move code declared around \"init\"\n  revisions API users: use release_revisions() needing REV_INFO_INIT\n  stash: always have the owner of \"stash_info\" free it\n  revisions API users: add \"goto cleanup\" for release_revisions()\n  revisions API users: use release_revisions() in http-push.c\n  revisions API users: use release_revisions() in builtin/log.c\n  revisions API users: use release_revisions() with UNLEAK()\n  revisions API users: use release_revisions() for \"prune_data\" users\n  revisions API: have release_revisions() release \"commits\"\n  revisions API: have release_revisions() release \"mailmap\"\n  revisions API: have release_revisions() release \"cmdline\"\n  revisions API: have release_revisions() release \"filter\"\n  revisions API: have release_revisions() release \"grep_filter\"\n  revisions API: have release_revisions() release \"prune_data\"\n  revisions API: clear \"boundary_commits\" in release_revisions()\n  revisions API: release \"reflog_info\" in release revisions()\n  revisions API: call diff_free(&revs->pruning) in revisions_release()\n  revisions API: have release_revisions() release \"date_mode\"\n  revisions API: have release_revisions() release \"topo_walk_info\"\n  revisions API: add a TODO for diff_free(&revs->diffopt)\n\n add-interactive.c                          |   3 +-\n bisect.c                                   |  20 ++--\n builtin/add.c                              |   3 +-\n builtin/am.c                               |   3 +\n builtin/bisect--helper.c                   |   2 +\n builtin/blame.c                            |   4 +-\n builtin/checkout.c                         |   3 +-\n builtin/commit.c                           |   6 +-\n builtin/describe.c                         |   2 +\n builtin/diff-files.c                       |   8 +-\n builtin/diff-index.c                       |   5 +-\n builtin/diff.c                             |   2 +-\n builtin/fast-export.c                      |   1 +\n builtin/log.c                              |  36 ++++---\n builtin/merge.c                            |   2 +\n builtin/pack-objects.c                     |   2 +\n builtin/prune.c                            |   1 +\n builtin/reflog.c                           |   1 +\n builtin/rev-list.c                         |  25 +++--\n builtin/shortlog.c                         |   8 +-\n builtin/stash.c                            | 115 +++++++++++----------\n builtin/submodule--helper.c                |  14 ++-\n bundle.c                                   |  12 ++-\n commit.c                                   |  19 ++--\n contrib/coccinelle/free.cocci              |  27 +++++\n diff-lib.c                                 |   8 +-\n fmt-merge-msg.c                            |   1 +\n http-push.c                                |   3 +-\n merge-ort.c                                |   1 +\n merge-recursive.c                          |   5 +-\n midx.c                                     |   1 +\n pack-bitmap-write.c                        |   1 +\n range-diff.c                               |   2 +-\n ref-filter.c                               |   1 +\n reflog-walk.c                              |  24 ++++-\n reflog-walk.h                              |   1 +\n remote.c                                   |   1 +\n revision.c                                 |  70 ++++++++++---\n revision.h                                 |  73 ++++++++-----\n sequencer.c                                |  26 +++--\n shallow.c                                  |   1 +\n submodule.c                                |  11 +-\n t/helper/test-fast-rebase.c                |  23 +++--\n t/helper/test-revision-walking.c           |   1 +\n t/lib-git-svn.sh                           |   4 +\n t/t0056-git-C.sh                           |   1 +\n t/t0062-revision-walking.sh                |   1 +\n t/t0100-previous.sh                        |   1 +\n t/t0101-at-syntax.sh                       |   2 +\n t/t1001-read-tree-m-2way.sh                |   1 +\n t/t1002-read-tree-m-u-2way.sh              |   1 +\n t/t1060-object-corruption.sh               |   2 +\n t/t1401-symbolic-ref.sh                    |   2 +\n t/t1411-reflog-show.sh                     |   1 +\n t/t1412-reflog-loop.sh                     |   2 +\n t/t1415-worktree-refs.sh                   |   1 +\n t/t2015-checkout-unborn.sh                 |   1 +\n t/t2200-add-update.sh                      |   1 +\n t/t3302-notes-index-expensive.sh           |   1 +\n t/t3303-notes-subtrees.sh                  |   1 +\n t/t3305-notes-fanout.sh                    |   1 +\n t/t3408-rebase-multi-line.sh               |   1 +\n t/t4021-format-patch-numbered.sh           |   1 +\n t/t4027-diff-submodule.sh                  |   1 +\n t/t4028-format-patch-mime-headers.sh       |   2 +\n t/t4036-format-patch-signer-mime.sh        |   1 +\n t/t4039-diff-assume-unchanged.sh           |   1 +\n t/t4055-diff-context.sh                    |   1 +\n t/t4066-diff-emit-delay.sh                 |   1 +\n t/t4122-apply-symlink-inside.sh            |   1 +\n t/t4126-apply-empty.sh                     |   1 -\n t/t4128-apply-root.sh                      |   1 +\n t/t4206-log-follow-harder-copies.sh        |   2 +\n t/t4207-log-decoration-colors.sh           |   1 +\n t/t4212-log-corrupt.sh                     |   1 +\n t/t5301-sliding-window.sh                  |   2 +\n t/t5313-pack-bounds-checks.sh              |   2 +\n t/t5316-pack-delta-depth.sh                |   2 +\n t/t5320-delta-islands.sh                   |   2 +\n t/t5322-pack-objects-sparse.sh             |   1 +\n t/t5506-remote-groups.sh                   |   1 +\n t/t5513-fetch-track.sh                     |   1 +\n t/t5515-fetch-merge-logic.sh               |   1 +\n t/t5518-fetch-exit-status.sh               |   1 +\n t/t5532-fetch-proxy.sh                     |   2 +\n t/t5600-clone-fail-cleanup.sh              |   1 +\n t/t5900-repo-selection.sh                  |   2 +\n t/t6002-rev-list-bisect.sh                 |   1 +\n t/t6003-rev-list-topo-order.sh             |   1 +\n t/t6005-rev-list-count.sh                  |   1 +\n t/t6018-rev-list-glob.sh                   |   1 +\n t/t6100-rev-list-in-order.sh               |   1 +\n t/t6101-rev-parse-parents.sh               |   1 +\n t/t6110-rev-list-sparse.sh                 |   1 +\n t/t6114-keep-packs.sh                      |   2 +\n t/t6131-pathspec-icase.sh                  |   2 +\n t/t7008-filter-branch-null-sha1.sh         |   1 +\n t/t7702-repack-cyclic-alternate.sh         |   2 +\n t/t9001-send-email.sh                      |   1 +\n t/t9100-git-svn-basic.sh                   |   1 +\n t/t9101-git-svn-props.sh                   |   2 +\n t/t9104-git-svn-follow-parent.sh           |   2 +\n t/t9106-git-svn-commit-diff-clobber.sh     |   2 +\n t/t9115-git-svn-dcommit-funky-renames.sh   |   1 +\n t/t9116-git-svn-log.sh                     |   1 +\n t/t9122-git-svn-author.sh                  |   2 +\n t/t9127-git-svn-partial-rebuild.sh         |   1 +\n t/t9129-git-svn-i18n-commitencoding.sh     |   1 +\n t/t9132-git-svn-broken-symlink.sh          |   1 +\n t/t9139-git-svn-non-utf8-commitencoding.sh |   1 +\n t/t9146-git-svn-empty-dirs.sh              |   2 +\n t/t9148-git-svn-propset.sh                 |   1 +\n t/t9160-git-svn-preserve-empty-dirs.sh     |   1 +\n t/t9162-git-svn-dcommit-interactive.sh     |   2 +\n t/t9164-git-svn-dcommit-concurrent.sh      |   2 +\n t/t9501-gitweb-standalone-http-status.sh   |   1 +\n wt-status.c                                |  15 +--\n 117 files changed, 500 insertions(+), 190 deletions(-)\n\nRange-diff against v5:\n 1:  f6b7b27109c <  -:  ----------- list-objects-filter: remove CL_ARG__FILTER\n 2:  692f9578614 <  -:  ----------- pack-objects: move revs out of get_object_list()\n 3:  56ee616b898 <  -:  ----------- pack-objects: parse --filter directly into revs.filter\n 4:  00f69fb967f <  -:  ----------- bundle: move capabilities to end of 'verify'\n 5:  e33162a7a4f <  -:  ----------- bundle: output hash information in 'verify'\n 6:  ba6177f59bb <  -:  ----------- pack-objects: lazily set up \"struct rev_info\", don't leak\n 7:  e20f951a64c =  1:  1e904ad33b4 t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"\n 8:  de2e08a14e6 =  2:  c2dcdfab458 blame: use \"goto cleanup\" for cleanup_scoreboard()\n 9:  364aa7d8990 =  3:  21fe41e4ad8 string_list API users: use string_list_init_{no,}dup\n10:  5d4ad92d47b =  4:  f7fb26752b9 format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"\n11:  c38db28f920 =  5:  9eada36dacf cocci: add and apply free_commit_list() rules\n12:  b99f96599bb =  6:  22de5dc3cc1 revision.[ch]: provide and start using a release_revisions()\n13:  521f6967b64 =  7:  31c8eccf68e revisions API users: add straightforward release_revisions()\n14:  003b507e0b7 =  8:  4a0e57acf17 revision.[ch]: document and move code declared around \"init\"\n15:  240307bc3c6 =  9:  2bfa8640b6b revisions API users: use release_revisions() needing REV_INFO_INIT\n16:  4c5404912e9 = 10:  f0c3d274fb5 stash: always have the owner of \"stash_info\" free it\n17:  4210232e3a1 = 11:  e93791b6242 revisions API users: add \"goto cleanup\" for release_revisions()\n18:  1918ca0a85d = 12:  2c744ccdbb1 revisions API users: use release_revisions() in http-push.c\n19:  6a02b7aae4e = 13:  fdb3a63e0a9 revisions API users: use release_revisions() in builtin/log.c\n20:  ddc7402b054 = 14:  31ce58da43d revisions API users: use release_revisions() with UNLEAK()\n21:  03e9bd73d4b = 15:  71d59de2fe1 revisions API users: use release_revisions() for \"prune_data\" users\n22:  285673e40bf = 16:  dcf63f64135 revisions API: have release_revisions() release \"commits\"\n23:  ca659940ee5 = 17:  3e226c5b3d4 revisions API: have release_revisions() release \"mailmap\"\n24:  7987c748ba4 ! 18:  ccae0f32ad5 revisions API: have release_revisions() release \"cmdline\"\n    @@ revision.c: int setup_revisions(int argc, const char **argv, struct rev_info *re\n     +{\n     +\tunsigned int i;\n     +\n    -+\tif (!cmdline)\n    -+\t\treturn;\n     +\tfor (i = 0; i < cmdline->nr; i++)\n     +\t\tfree((char *)cmdline->rev[i].name);\n     +\tfree(cmdline->rev);\n25:  e6e87ce3608 = 19:  2fc60559f1c revisions API: have release_revisions() release \"filter\"\n26:  ebdcdc88d79 = 20:  e7e0e46bbb2 revisions API: have release_revisions() release \"grep_filter\"\n27:  84c8714571a = 21:  54514a01096 revisions API: have release_revisions() release \"prune_data\"\n28:  d96642d3502 = 22:  69745303734 revisions API: clear \"boundary_commits\" in release_revisions()\n29:  0e9745d9ee1 = 23:  87ba9572b10 revisions API: release \"reflog_info\" in release revisions()\n30:  c7f3e92a8d1 = 24:  79279864c42 revisions API: call diff_free(&revs->pruning) in revisions_release()\n31:  d428d752462 = 25:  4aadeabaae7 revisions API: have release_revisions() release \"date_mode\"\n32:  fbf233ec768 = 26:  f0cedadbb94 revisions API: have release_revisions() release \"topo_walk_info\"\n33:  ec2c77ce869 = 27:  3be672a8150 revisions API: add a TODO for diff_free(&revs->diffopt)\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453565","messageId":"patch-v6-01.27-1e904ad33b4-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 01/27] t/helper/test-fast-rebase.c: don't leak \"struct strbuf\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:30Z","receivedAt":"2022-04-13T20:02:10Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix a memory leak that's been with us since f9500261e0a (fast-rebase:\nwrite conflict state to working tree, index, and HEAD, 2021-05-20)\nchanged this code to move these strbuf_release() into an if/else\nblock.\n\nWe'll also add to \"reflog_msg\" in the \"else\" arm of the \"if\" block\nbeing modified here, and we'll append to \"branch_msg\" in both\ncases. But after f9500261e0a only the \"if\" block would free these two\n\"struct strbuf\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n t/helper/test-fast-rebase.c | 5 +++--\n 1 file changed, 3 insertions(+), 2 deletions(-)\n\ndiff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c\nindex fc2d4609043..993b90eaedd 100644\n--- a/t/helper/test-fast-rebase.c\n+++ b/t/helper/test-fast-rebase.c\n@@ -201,8 +201,6 @@ int cmd__fast_rebase(int argc, const char **argv)\n \t\t}\n \t\tif (create_symref(\"HEAD\", branch_name.buf, reflog_msg.buf) < 0)\n \t\t\tdie(_(\"unable to update HEAD\"));\n-\t\tstrbuf_release(&reflog_msg);\n-\t\tstrbuf_release(&branch_name);\n \n \t\tprime_cache_tree(the_repository, the_repository->index,\n \t\t\t\t result.tree);\n@@ -221,5 +219,8 @@ int cmd__fast_rebase(int argc, const char **argv)\n \tif (write_locked_index(&the_index, &lock,\n \t\t\t       COMMIT_LOCK | SKIP_IF_UNCHANGED))\n \t\tdie(_(\"unable to write %s\"), get_index_file());\n+\n+\tstrbuf_release(&reflog_msg);\n+\tstrbuf_release(&branch_name);\n \treturn (result.clean == 0);\n }\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453566","messageId":"patch-v6-02.27-c2dcdfab458-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 02/27] blame: use \"goto cleanup\" for cleanup_scoreboard()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:31Z","receivedAt":"2022-04-13T20:02:11Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Amend a freeing pattern added in 0906ac2b54b (blame: use changed-path\nBloom filters, 2020-04-16) to use a \"goto cleanup\", so that we can be\nsure that we call cleanup_scoreboard().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/blame.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 8d15b68afc9..885b381ab83 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1167,7 +1167,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \tif (!incremental)\n \t\tsetup_pager();\n \telse\n-\t\treturn 0;\n+\t\tgoto cleanup;\n \n \tblame_sort_final(&sb);\n \n@@ -1201,6 +1201,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \t\tprintf(\"num commits: %d\\n\", sb.num_commits);\n \t}\n \n+cleanup:\n \tcleanup_scoreboard(&sb);\n \treturn 0;\n }\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453567","messageId":"patch-v6-03.27-21fe41e4ad8-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 03/27] string_list API users: use string_list_init_{no,}dup","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:32Z","receivedAt":"2022-04-13T20:02:13Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Follow-up on the introduction of string_list_init_nodup() and\nstring_list_init_dup() in the series merged in bd4232fac33 (Merge\nbranch 'ab/struct-init', 2021-07-16) and convert code that implicitly\nrelied on xcalloc() being equivalent to the initializer to use\nxmalloc() and string_list_init_{no,}dup() instead.\n\nIn the case of get_unmerged() in merge-recursive.c we used the\ncombination of xcalloc() and assigning \"1\" to \"strdup_strings\" to get\nwhat we'd get via string_list_init_dup(), let's use that instead.\n\nAdjacent code in cmd_format_patch() will be changed in a subsequent\ncommit, since we're changing that let's change the other in-tree\npatterns that do the same. Let's also convert a \"x == NULL\" to \"!x\"\nper our CodingGuidelines, as we need to change the \"if\" line anyway.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/log.c      | 9 ++++++---\n builtin/shortlog.c | 6 ++++--\n merge-recursive.c  | 4 ++--\n 3 files changed, 12 insertions(+), 7 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex c211d66d1d0..634dc782cce 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -231,7 +231,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,\n \t}\n \n \tif (mailmap) {\n-\t\trev->mailmap = xcalloc(1, sizeof(struct string_list));\n+\t\trev->mailmap = xmalloc(sizeof(struct string_list));\n+\t\tstring_list_init_nodup(rev->mailmap);\n \t\tread_mailmap(rev->mailmap);\n \t}\n \n@@ -2173,8 +2174,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tprepare_bases(&bases, base, list, nr);\n \t}\n \n-\tif (in_reply_to || thread || cover_letter)\n-\t\trev.ref_message_ids = xcalloc(1, sizeof(struct string_list));\n+\tif (in_reply_to || thread || cover_letter) {\n+\t\trev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));\n+\t\tstring_list_init_nodup(rev.ref_message_ids);\n+\t}\n \tif (in_reply_to) {\n \t\tconst char *msgid = clean_message_id(in_reply_to);\n \t\tstring_list_append(rev.ref_message_ids, msgid);\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex 26c5c0cf935..fcde07c9367 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -81,8 +81,10 @@ static void insert_one_record(struct shortlog *log,\n \t\tformat_subject(&subject, oneline, \" \");\n \t\tbuffer = strbuf_detach(&subject, NULL);\n \n-\t\tif (item->util == NULL)\n-\t\t\titem->util = xcalloc(1, sizeof(struct string_list));\n+\t\tif (!item->util) {\n+\t\t\titem->util = xmalloc(sizeof(struct string_list));\n+\t\t\tstring_list_init_nodup(item->util);\n+\t\t}\n \t\tstring_list_append(item->util, buffer);\n \t}\n }\ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 1ee6364e8b1..32bbba5fbb1 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -522,10 +522,10 @@ static struct stage_data *insert_stage_data(struct repository *r,\n  */\n static struct string_list *get_unmerged(struct index_state *istate)\n {\n-\tstruct string_list *unmerged = xcalloc(1, sizeof(struct string_list));\n+\tstruct string_list *unmerged = xmalloc(sizeof(struct string_list));\n \tint i;\n \n-\tunmerged->strdup_strings = 1;\n+\tstring_list_init_dup(unmerged);\n \n \t/* TODO: audit for interaction with sparse-index. */\n \tensure_full_index(istate);\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453568","messageId":"patch-v6-04.27-f7fb26752b9-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 04/27] format-patch: don't leak \"extra_headers\" or \"ref_message_ids\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:33Z","receivedAt":"2022-04-13T20:02:14Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Fix two memory leaks in \"struct rev_info\" by freeing that memory in\ncmd_format_patch(). These two are unusual special-cases in being in\nthe \"struct rev_info\", but not being \"owned\" by the code in\nrevision.c. I.e. they're members of the struct so that this code in\n\"builtin/log.c\" can conveniently pass information code in\n\"log-tree.c\".\n\nSee e.g. the make_cover_letter() caller of log_write_email_headers()\nhere in \"builtin/log.c\", and [1] for a demonstration of where the\n\"extra_headers\" and \"ref_message_ids\" struct members are used.\n\nSee 20ff06805c6 (format-patch: resurrect extra headers from config,\n2006-06-02) and d1566f7883f (git-format-patch: Make the second and\nsubsequent mails replies to the first, 2006-07-14) for the initial\nintroduction of \"extra_headers\" and \"ref_message_ids\".\n\nWe can count on repo_init_revisions() memset()-ing this data to 0\nhowever, so we can count on it being either NULL or something we\nallocated. In the case of \"extra_headers\" let's add a local \"char *\"\nvariable to hold it, to avoid the eventual cast from \"const char *\"\nwhen we free() it.\n\n1. https://lore.kernel.org/git/220401.868rsoogxf.gmgdl@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/log.c | 7 ++++++-\n 1 file changed, 6 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 634dc782cce..6f9928fabfe 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -1747,6 +1747,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tstruct commit *commit;\n \tstruct commit **list = NULL;\n \tstruct rev_info rev;\n+\tchar *to_free = NULL;\n \tstruct setup_revision_opt s_r_opt;\n \tint nr = 0, total, i;\n \tint use_stdout = 0;\n@@ -1947,7 +1948,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \t\tstrbuf_addch(&buf, '\\n');\n \t}\n \n-\trev.extra_headers = strbuf_detach(&buf, NULL);\n+\trev.extra_headers = to_free = strbuf_detach(&buf, NULL);\n \n \tif (from) {\n \t\tif (split_ident_line(&rev.from_ident, from, strlen(from)))\n@@ -2284,6 +2285,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tstrbuf_release(&rdiff1);\n \tstrbuf_release(&rdiff2);\n \tstrbuf_release(&rdiff_title);\n+\tfree(to_free);\n+\tif (rev.ref_message_ids)\n+\t\tstring_list_clear(rev.ref_message_ids, 0);\n+\tfree(rev.ref_message_ids);\n \tUNLEAK(rev);\n \treturn 0;\n }\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453569","messageId":"patch-v6-05.27-9eada36dacf-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 05/27] cocci: add and apply free_commit_list() rules","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:34Z","receivedAt":"2022-04-13T20:02:16Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add and apply coccinelle rules to remove \"if (E)\" before\n\"free_commit_list(E)\", the function can accept NULL, and further\nchange cases where \"E = NULL\" followed to also be unconditionally.\n\nThe code changes in this commit were entirely made by the coccinelle\nrule being added here, and applied with:\n\n    make contrib/coccinelle/free.cocci.patch\n    patch -p1 <contrib/coccinelle/free.cocci.patch\n\nThe only manual intervention here is that the the relevant code in\ncommit.c has been manually re-indented.\n\nSuggested-by: Phillip Wood <phillip.wood123@gmail.com>\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/rev-list.c            |  6 ++----\n commit.c                      | 19 ++++++++-----------\n contrib/coccinelle/free.cocci | 27 +++++++++++++++++++++++++++\n revision.c                    | 17 ++++++-----------\n submodule.c                   |  6 ++----\n 5 files changed, 45 insertions(+), 30 deletions(-)\n\ndiff --git a/builtin/rev-list.c b/builtin/rev-list.c\nindex 572da1472e5..07c0ad11d8d 100644\n--- a/builtin/rev-list.c\n+++ b/builtin/rev-list.c\n@@ -213,10 +213,8 @@ static void show_commit(struct commit *commit, void *data)\n \n static void finish_commit(struct commit *commit)\n {\n-\tif (commit->parents) {\n-\t\tfree_commit_list(commit->parents);\n-\t\tcommit->parents = NULL;\n-\t}\n+\tfree_commit_list(commit->parents);\n+\tcommit->parents = NULL;\n \tfree_commit_buffer(the_repository->parsed_objects,\n \t\t\t   commit);\n }\ndiff --git a/commit.c b/commit.c\nindex 59b6c3e4552..9628716bf2f 100644\n--- a/commit.c\n+++ b/commit.c\n@@ -407,17 +407,14 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b\n \n \tif (item->object.parsed)\n \t\treturn 0;\n-\n-\tif (item->parents) {\n-\t\t/*\n-\t\t * Presumably this is leftover from an earlier failed parse;\n-\t\t * clear it out in preparation for us re-parsing (we'll hit the\n-\t\t * same error, but that's good, since it lets our caller know\n-\t\t * the result cannot be trusted.\n-\t\t */\n-\t\tfree_commit_list(item->parents);\n-\t\titem->parents = NULL;\n-\t}\n+\t/*\n+\t * Presumably this is leftover from an earlier failed parse;\n+\t * clear it out in preparation for us re-parsing (we'll hit the\n+\t * same error, but that's good, since it lets our caller know\n+\t * the result cannot be trusted.\n+\t */\n+\tfree_commit_list(item->parents);\n+\titem->parents = NULL;\n \n \ttail += size;\n \tif (tail <= bufptr + tree_entry_len + 1 || memcmp(bufptr, \"tree \", 5) ||\ndiff --git a/contrib/coccinelle/free.cocci b/contrib/coccinelle/free.cocci\nindex 4490069df96..6fb9eb6e883 100644\n--- a/contrib/coccinelle/free.cocci\n+++ b/contrib/coccinelle/free.cocci\n@@ -2,13 +2,21 @@\n expression E;\n @@\n - if (E)\n+(\n   free(E);\n+|\n+  free_commit_list(E);\n+)\n \n @@\n expression E;\n @@\n - if (!E)\n+(\n   free(E);\n+|\n+  free_commit_list(E);\n+)\n \n @@\n expression E;\n@@ -16,3 +24,22 @@ expression E;\n - free(E);\n + FREE_AND_NULL(E);\n - E = NULL;\n+\n+@@\n+expression E;\n+@@\n+- if (E)\n+- {\n+  free_commit_list(E);\n+  E = NULL;\n+- }\n+\n+@@\n+expression E;\n+statement S;\n+@@\n+- if (E) {\n++ if (E)\n+  S\n+  free_commit_list(E);\n+- }\ndiff --git a/revision.c b/revision.c\nindex 7d435f80480..4963ba7def8 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -1456,10 +1456,9 @@ static int limit_list(struct rev_info *revs)\n \tif (revs->left_only || revs->right_only)\n \t\tlimit_left_right(newlist, revs);\n \n-\tif (bottom) {\n+\tif (bottom)\n \t\tlimit_to_ancestry(bottom, newlist);\n-\t\tfree_commit_list(bottom);\n-\t}\n+\tfree_commit_list(bottom);\n \n \t/*\n \t * Check if any commits have become TREESAME by some of their parents\n@@ -4080,10 +4079,8 @@ static void create_boundary_commit_list(struct rev_info *revs)\n \t * boundary commits anyway.  (This is what the code has always\n \t * done.)\n \t */\n-\tif (revs->commits) {\n-\t\tfree_commit_list(revs->commits);\n-\t\trevs->commits = NULL;\n-\t}\n+\tfree_commit_list(revs->commits);\n+\trevs->commits = NULL;\n \n \t/*\n \t * Put all of the actual boundary commits from revs->boundary_commits\n@@ -4220,10 +4217,8 @@ struct commit *get_revision(struct rev_info *revs)\n \t\tgraph_update(revs->graph, c);\n \tif (!c) {\n \t\tfree_saved_parents(revs);\n-\t\tif (revs->previous_parents) {\n-\t\t\tfree_commit_list(revs->previous_parents);\n-\t\t\trevs->previous_parents = NULL;\n-\t\t}\n+\t\tfree_commit_list(revs->previous_parents);\n+\t\trevs->previous_parents = NULL;\n \t}\n \treturn c;\n }\ndiff --git a/submodule.c b/submodule.c\nindex 86c8f0f89db..9b715b4a5cb 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -645,8 +645,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,\n \tprint_submodule_diff_summary(sub, &rev, o);\n \n out:\n-\tif (merge_bases)\n-\t\tfree_commit_list(merge_bases);\n+\tfree_commit_list(merge_bases);\n \tclear_commit_marks(left, ~0);\n \tclear_commit_marks(right, ~0);\n \tif (sub) {\n@@ -735,8 +734,7 @@ void show_submodule_inline_diff(struct diff_options *o, const char *path,\n \n done:\n \tstrbuf_release(&sb);\n-\tif (merge_bases)\n-\t\tfree_commit_list(merge_bases);\n+\tfree_commit_list(merge_bases);\n \tif (left)\n \t\tclear_commit_marks(left, ~0);\n \tif (right)\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453570","messageId":"patch-v6-06.27-22de5dc3cc1-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 06/27] revision.[ch]: provide and start using a release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:35Z","receivedAt":"2022-04-13T20:02:26Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"The users of the revision.[ch] API's \"struct rev_info\" are a major\nsource of memory leaks in the test suite under SANITIZE=leak, which in\nturn adds a lot of noise when trying to mark up tests with\n\"TEST_PASSES_SANITIZE_LEAK=true\".\n\nThe users of that API are largely one-shot, e.g. \"git rev-list\" or\n\"git log\", or the \"git checkout\" and \"git stash\" being modified here\n\nFor these callers freeing the memory is arguably a waste of time, but\nin many cases they've actually been trying to free the memory, and\njust doing that in a buggy manner.\n\nLet's provide a release_revisions() function for these users, and\nstart migrating them over per the plan outlined in [1]. Right now this\nonly handles the \"pending\" member of the struct, but more will be\nadded in subsequent commits.\n\nEven though we only clear the \"pending\" member now, let's not leave a\ntrap in code like the pre-image of index_differs_from(), where we'd\nstart doing the wrong thing as soon as the release_revisions() learned\nto clear its \"diffopt\". I.e. we need to call release_revisions() after\nwe've inspected any state in \"struct rev_info\".\n\nThis leaves in place e.g. clear_pathspec(&rev.prune_data) in\nstash_working_tree() in builtin/stash.c, subsequent commits will teach\nrelease_revisions() to free \"prune_data\" and other members that in\nsome cases are individually cleared by users of \"struct rev_info\" by\nreaching into its members. Those subsequent commits will remove the\nrelevant calls to e.g. clear_pathspec().\n\nWe avoid amending code in index_differs_from() in diff-lib.c as well\nas wt_status_collect_changes_index(), has_unstaged_changes() and\nhas_uncommitted_changes() in wt-status.c in a way that assumes that we\nare already clearing the \"diffopt\" member. That will be handled in a\nsubsequent commit.\n\n1. https://lore.kernel.org/git/87a6k8daeu.fsf@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/checkout.c | 2 +-\n builtin/stash.c    | 5 ++---\n diff-lib.c         | 2 +-\n range-diff.c       | 2 +-\n revision.c         | 5 +++++\n revision.h         | 6 ++++++\n wt-status.c        | 5 +++--\n 7 files changed, 19 insertions(+), 8 deletions(-)\n\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 797681481d1..4d9e0bd3ac1 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -629,7 +629,7 @@ static void show_local_changes(struct object *head,\n \tdiff_setup_done(&rev.diffopt);\n \tadd_pending_object(&rev, head, NULL);\n \trun_diff_index(&rev, 0);\n-\tobject_array_clear(&rev.pending);\n+\trelease_revisions(&rev);\n }\n \n static void describe_detached_head(const char *msg, struct commit *commit)\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 0c7b6a95882..74fa810cf8c 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1047,7 +1047,6 @@ static int check_changes_tracked_files(const struct pathspec *ps)\n \t\tgoto done;\n \t}\n \n-\tobject_array_clear(&rev.pending);\n \tresult = run_diff_files(&rev, 0);\n \tif (diff_result_code(&rev.diffopt, result)) {\n \t\tret = 1;\n@@ -1056,6 +1055,7 @@ static int check_changes_tracked_files(const struct pathspec *ps)\n \n done:\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n \treturn ret;\n }\n \n@@ -1266,9 +1266,8 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps\n \n done:\n \tdiscard_index(&istate);\n-\tUNLEAK(rev);\n-\tobject_array_clear(&rev.pending);\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n \tstrbuf_release(&diff_output);\n \tremove_path(stash_index_path.buf);\n \treturn ret;\ndiff --git a/diff-lib.c b/diff-lib.c\nindex ca085a03efc..d6800274bd5 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -662,7 +662,7 @@ int index_differs_from(struct repository *r,\n \t\tdiff_flags_or(&rev.diffopt.flags, flags);\n \trev.diffopt.ita_invisible_in_index = ita_invisible_in_index;\n \trun_diff_index(&rev, 1);\n-\tobject_array_clear(&rev.pending);\n+\trelease_revisions(&rev);\n \treturn (rev.diffopt.flags.has_changes != 0);\n }\n \ndiff --git a/range-diff.c b/range-diff.c\nindex b72eb9fdbee..39cc010c628 100644\n--- a/range-diff.c\n+++ b/range-diff.c\n@@ -596,6 +596,6 @@ int is_range_diff_range(const char *arg)\n \t}\n \n \tfree(copy);\n-\tobject_array_clear(&revs.pending);\n+\trelease_revisions(&revs);\n \treturn negative > 0 && positive > 0;\n }\ndiff --git a/revision.c b/revision.c\nindex 4963ba7def8..5dd4b2e910c 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2922,6 +2922,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \treturn left;\n }\n \n+void release_revisions(struct rev_info *revs)\n+{\n+\tobject_array_clear(&revs->pending);\n+}\n+\n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n {\n \tstruct commit_list *l = xcalloc(1, sizeof(*l));\ndiff --git a/revision.h b/revision.h\nindex 5bc59c7bfe1..61c780fc4cd 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -377,6 +377,12 @@ void repo_init_revisions(struct repository *r,\n int setup_revisions(int argc, const char **argv, struct rev_info *revs,\n \t\t    struct setup_revision_opt *);\n \n+/**\n+ * Free data allocated in a \"struct rev_info\" after it's been\n+ * initialized with repo_init_revisions().\n+ */\n+void release_revisions(struct rev_info *revs);\n+\n void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,\n \t\t\tconst struct option *options,\n \t\t\tconst char * const usagestr[]);\ndiff --git a/wt-status.c b/wt-status.c\nindex d33f9272b72..922cf787f95 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -662,7 +662,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)\n \n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_index(&rev, 1);\n-\tobject_array_clear(&rev.pending);\n+\trelease_revisions(&rev);\n \tclear_pathspec(&rev.prune_data);\n }\n \n@@ -2545,6 +2545,7 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)\n \trev_info.diffopt.flags.quick = 1;\n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_files(&rev_info, 0);\n+\trelease_revisions(&rev_info);\n \treturn diff_result_code(&rev_info.diffopt, result);\n }\n \n@@ -2577,7 +2578,7 @@ int has_uncommitted_changes(struct repository *r,\n \n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_index(&rev_info, 1);\n-\tobject_array_clear(&rev_info.pending);\n+\trelease_revisions(&rev_info);\n \treturn diff_result_code(&rev_info.diffopt, result);\n }\n \n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453571","messageId":"patch-v6-08.27-4a0e57acf17-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 08/27] revision.[ch]: document and move code declared around \"init\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:37Z","receivedAt":"2022-04-13T20:02:27Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"A subsequent commit will add \"REV_INFO_INIT\" macro adjacent to\nrepo_init_revisions(), unfortunately between the \"struct rev_info\"\nitself and that function we've added various miscellaneous code\nbetween the two over the years.\n\nLet's move that code either lower in revision.h, giving it API docs\nwhile we're at it, or in cases where it wasn't public API at all move\nit into revision.c No lines of code are changed here, only moved\naround. The only changes are the addition of new API comments.\n\nThe \"tree_difference\" variable could also be declared like this, which\nI think would be a lot clearer, but let's leave that for now to keep\nthis a move-only change:\n\n\tstatic enum {\n\t\tREV_TREE_SAME,\n\t\tREV_TREE_NEW, /* Only new files */\n\t\tREV_TREE_OLD, /* Only files removed */\n\t\tREV_TREE_DIFFERENT, /* Mixed changes */\n\t} tree_difference = REV_TREE_SAME;\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c |  4 ++++\n revision.h | 50 ++++++++++++++++++++++++--------------------------\n 2 files changed, 28 insertions(+), 26 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex 5dd4b2e910c..472fff1e0a7 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -606,6 +606,10 @@ static struct commit *one_relevant_parent(const struct rev_info *revs,\n  *\n  *   2. We saw anything except REV_TREE_NEW.\n  */\n+#define REV_TREE_SAME\t\t0\n+#define REV_TREE_NEW\t\t1\t/* Only new files */\n+#define REV_TREE_OLD\t\t2\t/* Only files removed */\n+#define REV_TREE_DIFFERENT\t3\t/* Mixed changes */\n static int tree_difference = REV_TREE_SAME;\n \n static void file_add_remove(struct diff_options *options,\ndiff --git a/revision.h b/revision.h\nindex 61c780fc4cd..b9070e43428 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -329,32 +329,6 @@ struct rev_info {\n \tstruct tmp_objdir *remerge_objdir;\n };\n \n-int ref_excluded(struct string_list *, const char *path);\n-void clear_ref_exclusion(struct string_list **);\n-void add_ref_exclusion(struct string_list **, const char *exclude);\n-\n-\n-#define REV_TREE_SAME\t\t0\n-#define REV_TREE_NEW\t\t1\t/* Only new files */\n-#define REV_TREE_OLD\t\t2\t/* Only files removed */\n-#define REV_TREE_DIFFERENT\t3\t/* Mixed changes */\n-\n-/* revision.c */\n-typedef void (*show_early_output_fn_t)(struct rev_info *, struct commit_list *);\n-extern volatile show_early_output_fn_t show_early_output;\n-\n-struct setup_revision_opt {\n-\tconst char *def;\n-\tvoid (*tweak)(struct rev_info *, struct setup_revision_opt *);\n-\tunsigned int\tassume_dashdash:1,\n-\t\t\tallow_exclude_promisor_objects:1;\n-\tunsigned revarg_opt;\n-};\n-\n-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS\n-#define init_revisions(revs, prefix) repo_init_revisions(the_repository, revs, prefix)\n-#endif\n-\n /**\n  * Initialize a rev_info structure with default values. The third parameter may\n  * be NULL or can be prefix path, and then the `.prefix` variable will be set\n@@ -366,6 +340,9 @@ struct setup_revision_opt {\n void repo_init_revisions(struct repository *r,\n \t\t\t struct rev_info *revs,\n \t\t\t const char *prefix);\n+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS\n+#define init_revisions(revs, prefix) repo_init_revisions(the_repository, revs, prefix)\n+#endif\n \n /**\n  * Parse revision information, filling in the `rev_info` structure, and\n@@ -374,6 +351,13 @@ void repo_init_revisions(struct repository *r,\n  * head of the argument list. The last parameter is used in case no\n  * parameter given by the first two arguments.\n  */\n+struct setup_revision_opt {\n+\tconst char *def;\n+\tvoid (*tweak)(struct rev_info *, struct setup_revision_opt *);\n+\tunsigned int\tassume_dashdash:1,\n+\t\t\tallow_exclude_promisor_objects:1;\n+\tunsigned revarg_opt;\n+};\n int setup_revisions(int argc, const char **argv, struct rev_info *revs,\n \t\t    struct setup_revision_opt *);\n \n@@ -423,6 +407,14 @@ void mark_trees_uninteresting_sparse(struct repository *r, struct oidset *trees)\n \n void show_object_with_name(FILE *, struct object *, const char *);\n \n+/**\n+ * Helpers to check if a \"struct string_list\" item matches with\n+ * wildmatch().\n+ */\n+int ref_excluded(struct string_list *, const char *path);\n+void clear_ref_exclusion(struct string_list **);\n+void add_ref_exclusion(struct string_list **, const char *exclude);\n+\n /**\n  * This function can be used if you want to add commit objects as revision\n  * information. You can use the `UNINTERESTING` object flag to indicate if\n@@ -478,4 +470,10 @@ int rewrite_parents(struct rev_info *revs,\n  */\n struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit);\n \n+/**\n+ * Global for the (undocumented) \"--early-output\" flag for \"git log\".\n+ */\n+typedef void (*show_early_output_fn_t)(struct rev_info *, struct commit_list *);\n+extern volatile show_early_output_fn_t show_early_output;\n+\n #endif\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453572","messageId":"patch-v6-10.27-f0c3d274fb5-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 10/27] stash: always have the owner of \"stash_info\" free it","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:39Z","receivedAt":"2022-04-13T20:02:28Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Change the initialization of the \"revision\" member of \"struct\nstash_info\" to be initialized vi a macro, and more importantly that\nthat initializing function be tasked to free it, usually via a \"goto\ncleanup\" pattern.\n\nDespite the \"revision\" name (and the topic of the series containing\nthis commit) the \"stash info\" has nothing to do with the \"struct\nrev_info\". I'm making this change because in the subsequent commit\nwhen we do want to free the \"struct rev_info\" via a \"goto cleanup\"\npattern we'd otherwise free() uninitialized memory in some cases, as\nwe only strbuf_init() the string in get_stash_info().\n\nSo while it's not the smallest possible change, let's convert all\nusers of this pattern in the file while we're at it.\n\nA good follow-up to this change would be to change all the \"ret = -1;\ngoto done;\" in this file to instead use a \"goto cleanup\", and\ninitialize \"int ret = -1\" at the start of the relevant functions. That\nwould allow us to drop a lot of needless brace verbosity on two-line\n\"if\" statements, but let's leave that alone for now.\n\nTo ensure that there's a 1=1 mapping between owners of the \"struct\nstash_info\" and free_stash_info() change the assert_stash_ref()\nfunction to be a trivial get_stash_info_assert() wrapper. The caller\nwill call free_stash_info(), and by returning -1 we'll eventually (via\n!!ret) exit with status 1 anyway.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/stash.c | 107 ++++++++++++++++++++++++++----------------------\n 1 file changed, 59 insertions(+), 48 deletions(-)\n\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex 74fa810cf8c..ba030238939 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -116,6 +116,10 @@ struct stash_info {\n \tint has_u;\n };\n \n+#define STASH_INFO_INIT { \\\n+\t.revision = STRBUF_INIT, \\\n+}\n+\n static void free_stash_info(struct stash_info *info)\n {\n \tstrbuf_release(&info->revision);\n@@ -157,10 +161,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)\n \tif (argc == 1)\n \t\tcommit = argv[0];\n \n-\tstrbuf_init(&info->revision, 0);\n \tif (!commit) {\n \t\tif (!ref_exists(ref_stash)) {\n-\t\t\tfree_stash_info(info);\n \t\t\tfprintf_ln(stderr, _(\"No stash entries found.\"));\n \t\t\treturn -1;\n \t\t}\n@@ -174,11 +176,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)\n \n \trevision = info->revision.buf;\n \n-\tif (get_oid(revision, &info->w_commit)) {\n-\t\terror(_(\"%s is not a valid reference\"), revision);\n-\t\tfree_stash_info(info);\n-\t\treturn -1;\n-\t}\n+\tif (get_oid(revision, &info->w_commit))\n+\t\treturn error(_(\"%s is not a valid reference\"), revision);\n \n \tassert_stash_like(info, revision);\n \n@@ -197,7 +196,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)\n \t\tinfo->is_stash_ref = !strcmp(expanded_ref, ref_stash);\n \t\tbreak;\n \tdefault: /* Invalid or ambiguous */\n-\t\tfree_stash_info(info);\n+\t\tbreak;\n \t}\n \n \tfree(expanded_ref);\n@@ -598,10 +597,10 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,\n \n static int apply_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret;\n+\tint ret = -1;\n \tint quiet = 0;\n \tint index = 0;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n \t\tOPT_BOOL(0, \"index\", &index,\n@@ -613,9 +612,10 @@ static int apply_stash(int argc, const char **argv, const char *prefix)\n \t\t\t     git_stash_apply_usage, 0);\n \n \tif (get_stash_info(&info, argc, argv))\n-\t\treturn -1;\n+\t\tgoto cleanup;\n \n \tret = do_apply_stash(prefix, &info, index, quiet);\n+cleanup:\n \tfree_stash_info(&info);\n \treturn ret;\n }\n@@ -651,20 +651,25 @@ static int do_drop_stash(struct stash_info *info, int quiet)\n \treturn 0;\n }\n \n-static void assert_stash_ref(struct stash_info *info)\n+static int get_stash_info_assert(struct stash_info *info, int argc,\n+\t\t\t\t const char **argv)\n {\n-\tif (!info->is_stash_ref) {\n-\t\terror(_(\"'%s' is not a stash reference\"), info->revision.buf);\n-\t\tfree_stash_info(info);\n-\t\texit(1);\n-\t}\n+\tint ret = get_stash_info(info, argc, argv);\n+\n+\tif (ret < 0)\n+\t\treturn ret;\n+\n+\tif (!info->is_stash_ref)\n+\t\treturn error(_(\"'%s' is not a stash reference\"), info->revision.buf);\n+\n+\treturn 0;\n }\n \n static int drop_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret;\n+\tint ret = -1;\n \tint quiet = 0;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n \t\tOPT_END()\n@@ -673,22 +678,21 @@ static int drop_stash(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, options,\n \t\t\t     git_stash_drop_usage, 0);\n \n-\tif (get_stash_info(&info, argc, argv))\n-\t\treturn -1;\n-\n-\tassert_stash_ref(&info);\n+\tif (get_stash_info_assert(&info, argc, argv))\n+\t\tgoto cleanup;\n \n \tret = do_drop_stash(&info, quiet);\n+cleanup:\n \tfree_stash_info(&info);\n \treturn ret;\n }\n \n static int pop_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret;\n+\tint ret = -1;\n \tint index = 0;\n \tint quiet = 0;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct option options[] = {\n \t\tOPT__QUIET(&quiet, N_(\"be quiet, only report errors\")),\n \t\tOPT_BOOL(0, \"index\", &index,\n@@ -699,25 +703,25 @@ static int pop_stash(int argc, const char **argv, const char *prefix)\n \targc = parse_options(argc, argv, prefix, options,\n \t\t\t     git_stash_pop_usage, 0);\n \n-\tif (get_stash_info(&info, argc, argv))\n-\t\treturn -1;\n+\tif (get_stash_info_assert(&info, argc, argv))\n+\t\tgoto cleanup;\n \n-\tassert_stash_ref(&info);\n \tif ((ret = do_apply_stash(prefix, &info, index, quiet)))\n \t\tprintf_ln(_(\"The stash entry is kept in case \"\n \t\t\t    \"you need it again.\"));\n \telse\n \t\tret = do_drop_stash(&info, quiet);\n \n+cleanup:\n \tfree_stash_info(&info);\n \treturn ret;\n }\n \n static int branch_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret;\n+\tint ret = -1;\n \tconst char *branch = NULL;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct child_process cp = CHILD_PROCESS_INIT;\n \tstruct option options[] = {\n \t\tOPT_END()\n@@ -734,7 +738,7 @@ static int branch_stash(int argc, const char **argv, const char *prefix)\n \tbranch = argv[0];\n \n \tif (get_stash_info(&info, argc - 1, argv + 1))\n-\t\treturn -1;\n+\t\tgoto cleanup;\n \n \tcp.git_cmd = 1;\n \tstrvec_pushl(&cp.args, \"checkout\", \"-b\", NULL);\n@@ -746,8 +750,8 @@ static int branch_stash(int argc, const char **argv, const char *prefix)\n \tif (!ret && info.is_stash_ref)\n \t\tret = do_drop_stash(&info, 0);\n \n+cleanup:\n \tfree_stash_info(&info);\n-\n \treturn ret;\n }\n \n@@ -825,8 +829,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op\n static int show_stash(int argc, const char **argv, const char *prefix)\n {\n \tint i;\n-\tint ret = 0;\n-\tstruct stash_info info;\n+\tint ret = -1;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct rev_info rev;\n \tstruct strvec stash_args = STRVEC_INIT;\n \tstruct strvec revision_args = STRVEC_INIT;\n@@ -844,6 +848,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \t\t\t      UNTRACKED_ONLY, PARSE_OPT_NONEG),\n \t\tOPT_END()\n \t};\n+\tint do_usage = 0;\n \n \tinit_diff_ui_defaults();\n \tgit_config(git_diff_ui_config, NULL);\n@@ -861,10 +866,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \t\t\tstrvec_push(&revision_args, argv[i]);\n \t}\n \n-\tret = get_stash_info(&info, stash_args.nr, stash_args.v);\n-\tstrvec_clear(&stash_args);\n-\tif (ret)\n-\t\treturn -1;\n+\tif (get_stash_info(&info, stash_args.nr, stash_args.v))\n+\t\tgoto cleanup;\n \n \t/*\n \t * The config settings are applied only if there are not passed\n@@ -878,16 +881,14 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \t\t\trev.diffopt.output_format |= DIFF_FORMAT_PATCH;\n \n \t\tif (!show_stat && !show_patch) {\n-\t\t\tfree_stash_info(&info);\n-\t\t\treturn 0;\n+\t\t\tret = 0;\n+\t\t\tgoto cleanup;\n \t\t}\n \t}\n \n \targc = setup_revisions(revision_args.nr, revision_args.v, &rev, NULL);\n-\tif (argc > 1) {\n-\t\tfree_stash_info(&info);\n-\t\tusage_with_options(git_stash_show_usage, options);\n-\t}\n+\tif (argc > 1)\n+\t\tgoto usage;\n \tif (!rev.diffopt.output_format) {\n \t\trev.diffopt.output_format = DIFF_FORMAT_PATCH;\n \t\tdiff_setup_done(&rev.diffopt);\n@@ -912,8 +913,16 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n \t}\n \tlog_tree_diff_flush(&rev);\n \n+\tret = diff_result_code(&rev.diffopt, 0);\n+cleanup:\n+\tstrvec_clear(&stash_args);\n \tfree_stash_info(&info);\n-\treturn diff_result_code(&rev.diffopt, 0);\n+\tif (do_usage)\n+\t\tusage_with_options(git_stash_show_usage, options);\n+\treturn ret;\n+usage:\n+\tdo_usage = 1;\n+\tgoto cleanup;\n }\n \n static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,\n@@ -1409,9 +1418,9 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b\n \n static int create_stash(int argc, const char **argv, const char *prefix)\n {\n-\tint ret = 0;\n+\tint ret;\n \tstruct strbuf stash_msg_buf = STRBUF_INIT;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct pathspec ps;\n \n \t/* Starting with argv[1], since argv[0] is \"create\" */\n@@ -1426,6 +1435,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)\n \tif (!ret)\n \t\tprintf_ln(\"%s\", oid_to_hex(&info.w_commit));\n \n+\tfree_stash_info(&info);\n \tstrbuf_release(&stash_msg_buf);\n \treturn ret;\n }\n@@ -1434,7 +1444,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q\n \t\t\t int keep_index, int patch_mode, int include_untracked, int only_staged)\n {\n \tint ret = 0;\n-\tstruct stash_info info;\n+\tstruct stash_info info = STASH_INFO_INIT;\n \tstruct strbuf patch = STRBUF_INIT;\n \tstruct strbuf stash_msg_buf = STRBUF_INIT;\n \tstruct strbuf untracked_files = STRBUF_INIT;\n@@ -1633,6 +1643,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q\n \t}\n \n done:\n+\tfree_stash_info(&info);\n \tstrbuf_release(&stash_msg_buf);\n \treturn ret;\n }\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453573","messageId":"patch-v6-09.27-2bfa8640b6b-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 09/27] revisions API users: use release_revisions() needing REV_INFO_INIT","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:38Z","receivedAt":"2022-04-13T20:02:31Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use release_revisions() to various users of \"struct rev_list\" which\nneed to have their \"struct rev_info\" zero-initialized before we can\nstart using it.\n\nFor the bundle.c code see the early exit case added in\n3bbbe467f29 (bundle verify: error out if called without an object\ndatabase, 2019-05-27).\n\nFor the relevant bisect.c code see 45b6370812c (bisect: libify\n`check_good_are_ancestors_of_bad` and its dependents, 2020-02-17).\n\nFor the submodule.c code see the \"goto\" on \"(!left || !right || !sub)\"\nadded in 8e6df65015f (submodule: refactor show_submodule_summary with\nhelper function, 2016-08-31).\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n bisect.c                    | 18 ++++++++++++------\n builtin/submodule--helper.c |  3 ++-\n bundle.c                    | 12 ++++++++----\n revision.h                  | 21 ++++++++++++++++++++-\n submodule.c                 |  3 ++-\n 5 files changed, 44 insertions(+), 13 deletions(-)\n\ndiff --git a/bisect.c b/bisect.c\nindex cc6b8b6230d..b63669cc9d7 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -1010,7 +1010,7 @@ void read_bisect_terms(const char **read_bad, const char **read_good)\n  */\n enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n {\n-\tstruct rev_info revs;\n+\tstruct rev_info revs = REV_INFO_INIT;\n \tstruct commit_list *tried;\n \tint reaches = 0, all = 0, nr, steps;\n \tenum bisect_error res = BISECT_OK;\n@@ -1035,7 +1035,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \n \tres = check_good_are_ancestors_of_bad(r, prefix, no_checkout);\n \tif (res)\n-\t\treturn res;\n+\t\tgoto cleanup;\n \n \tbisect_rev_setup(r, &revs, prefix, \"%s\", \"^%s\", 1);\n \n@@ -1060,14 +1060,16 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \t\t       term_good,\n \t\t       term_bad);\n \n-\t\treturn BISECT_FAILED;\n+\t\tres = BISECT_FAILED;\n+\t\tgoto cleanup;\n \t}\n \n \tif (!all) {\n \t\tfprintf(stderr, _(\"No testable commit found.\\n\"\n \t\t\t\"Maybe you started with bad path arguments?\\n\"));\n \n-\t\treturn BISECT_NO_TESTABLE_COMMIT;\n+\t\tres = BISECT_NO_TESTABLE_COMMIT;\n+\t\tgoto cleanup;\n \t}\n \n \tbisect_rev = &revs.commits->item->object.oid;\n@@ -1087,7 +1089,8 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \t\t * for negative return values for early returns up\n \t\t * until the cmd_bisect__helper() caller.\n \t\t */\n-\t\treturn BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;\n+\t\tres = BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;\n+\t\tgoto cleanup;\n \t}\n \n \tnr = all - reaches - 1;\n@@ -1106,7 +1109,10 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)\n \t/* Clean up objects used, as they will be reused. */\n \trepo_clear_commit_marks(r, ALL_REV_FLAGS);\n \n-\treturn bisect_checkout(bisect_rev, no_checkout);\n+\tres = bisect_checkout(bisect_rev, no_checkout);\n+cleanup:\n+\trelease_revisions(&revs);\n+\treturn res;\n }\n \n static inline int log2i(int n)\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 5d6b43fe3c0..9a65985b418 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -766,7 +766,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,\n {\n \tchar *displaypath;\n \tstruct strvec diff_files_args = STRVEC_INIT;\n-\tstruct rev_info rev;\n+\tstruct rev_info rev = REV_INFO_INIT;\n \tint diff_files_result;\n \tstruct strbuf buf = STRBUF_INIT;\n \tconst char *git_dir;\n@@ -853,6 +853,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,\n cleanup:\n \tstrvec_clear(&diff_files_args);\n \tfree(displaypath);\n+\trelease_revisions(&rev);\n }\n \n static void status_submodule_cb(const struct cache_entry *list_item,\ndiff --git a/bundle.c b/bundle.c\nindex d50cfb5aa7e..6a870a6edb7 100644\n--- a/bundle.c\n+++ b/bundle.c\n@@ -196,14 +196,16 @@ int verify_bundle(struct repository *r,\n \t * to be verbose about the errors\n \t */\n \tstruct string_list *p = &header->prerequisites;\n-\tstruct rev_info revs;\n+\tstruct rev_info revs = REV_INFO_INIT;\n \tconst char *argv[] = {NULL, \"--all\", NULL};\n \tstruct commit *commit;\n \tint i, ret = 0, req_nr;\n \tconst char *message = _(\"Repository lacks these prerequisite commits:\");\n \n-\tif (!r || !r->objects || !r->objects->odb)\n-\t\treturn error(_(\"need a repository to verify a bundle\"));\n+\tif (!r || !r->objects || !r->objects->odb) {\n+\t\tret = error(_(\"need a repository to verify a bundle\"));\n+\t\tgoto cleanup;\n+\t}\n \n \trepo_init_revisions(r, &revs, NULL);\n \tfor (i = 0; i < p->nr; i++) {\n@@ -221,7 +223,7 @@ int verify_bundle(struct repository *r,\n \t\terror(\"%s %s\", oid_to_hex(oid), name);\n \t}\n \tif (revs.pending.nr != p->nr)\n-\t\treturn ret;\n+\t\tgoto cleanup;\n \treq_nr = revs.pending.nr;\n \tsetup_revisions(2, argv, &revs, NULL);\n \n@@ -284,6 +286,8 @@ int verify_bundle(struct repository *r,\n \t\t\tprintf_ln(\"The bundle uses this filter: %s\",\n \t\t\t\t  list_objects_filter_spec(&header->filter));\n \t}\n+cleanup:\n+\trelease_revisions(&revs);\n \treturn ret;\n }\n \ndiff --git a/revision.h b/revision.h\nindex b9070e43428..2621eb6d65a 100644\n--- a/revision.h\n+++ b/revision.h\n@@ -329,6 +329,25 @@ struct rev_info {\n \tstruct tmp_objdir *remerge_objdir;\n };\n \n+/**\n+ * Initialize the \"struct rev_info\" structure with a macro.\n+ *\n+ * This will not fully initialize a \"struct rev_info\", the\n+ * repo_init_revisions() function needs to be called before\n+ * setup_revisions() and any revision walking takes place.\n+ *\n+ * Use REV_INFO_INIT to make the \"struct rev_info\" safe for passing to\n+ * release_revisions() when it's inconvenient (e.g. due to a \"goto\n+ * cleanup\" pattern) to arrange for repo_init_revisions() to be called\n+ * before release_revisions() is called.\n+ *\n+ * Initializing with this REV_INFO_INIT is redundant to invoking\n+ * repo_init_revisions(). If repo_init_revisions() is guaranteed to be\n+ * called before release_revisions() the \"struct rev_info\" can be left\n+ * uninitialized.\n+ */\n+#define REV_INFO_INIT { 0 }\n+\n /**\n  * Initialize a rev_info structure with default values. The third parameter may\n  * be NULL or can be prefix path, and then the `.prefix` variable will be set\n@@ -363,7 +382,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs,\n \n /**\n  * Free data allocated in a \"struct rev_info\" after it's been\n- * initialized with repo_init_revisions().\n+ * initialized with repo_init_revisions() or REV_INFO_INIT.\n  */\n void release_revisions(struct rev_info *revs);\n \ndiff --git a/submodule.c b/submodule.c\nindex 7b5d7753930..7923e951e1c 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -619,7 +619,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,\n \t\tstruct object_id *one, struct object_id *two,\n \t\tunsigned dirty_submodule)\n {\n-\tstruct rev_info rev;\n+\tstruct rev_info rev = REV_INFO_INIT;\n \tstruct commit *left = NULL, *right = NULL;\n \tstruct commit_list *merge_bases = NULL;\n \tstruct repository *sub;\n@@ -646,6 +646,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,\n \n out:\n \tfree_commit_list(merge_bases);\n+\trelease_revisions(&rev);\n \tclear_commit_marks(left, ~0);\n \tclear_commit_marks(right, ~0);\n \tif (sub) {\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453574","messageId":"patch-v6-07.27-31c8eccf68e-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 07/27] revisions API users: add straightforward release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:36Z","receivedAt":"2022-04-13T20:02:34Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a release_revisions() to various users of \"struct rev_list\" in\nthose straightforward cases where we only need to add the\nrelease_revisions() call to the end of a block, and don't need to\ne.g. refactor anything to use a \"goto cleanup\" pattern.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n add-interactive.c                | 1 +\n bisect.c                         | 2 ++\n builtin/add.c                    | 1 +\n builtin/am.c                     | 3 +++\n builtin/bisect--helper.c         | 2 ++\n builtin/blame.c                  | 1 +\n builtin/checkout.c               | 1 +\n builtin/commit.c                 | 1 +\n builtin/describe.c               | 2 ++\n builtin/fast-export.c            | 1 +\n builtin/merge.c                  | 2 ++\n builtin/pack-objects.c           | 2 ++\n builtin/prune.c                  | 1 +\n builtin/reflog.c                 | 1 +\n builtin/shortlog.c               | 2 ++\n builtin/submodule--helper.c      | 1 +\n fmt-merge-msg.c                  | 1 +\n merge-ort.c                      | 1 +\n merge-recursive.c                | 1 +\n midx.c                           | 1 +\n pack-bitmap-write.c              | 1 +\n ref-filter.c                     | 1 +\n remote.c                         | 1 +\n sequencer.c                      | 3 +++\n shallow.c                        | 1 +\n submodule.c                      | 2 ++\n t/helper/test-revision-walking.c | 1 +\n wt-status.c                      | 1 +\n 28 files changed, 39 insertions(+)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex 72472103017..54cdfc82017 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -570,6 +570,7 @@ static int get_modified_files(struct repository *r,\n \n \t\tif (ps)\n \t\t\tclear_pathspec(&rev.prune_data);\n+\t\trelease_revisions(&rev);\n \t}\n \thashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);\n \tif (unmerged_count)\ndiff --git a/bisect.c b/bisect.c\nindex 9e6a2b7f201..cc6b8b6230d 100644\n--- a/bisect.c\n+++ b/bisect.c\n@@ -884,6 +884,7 @@ static int check_ancestors(struct repository *r, int rev_nr,\n \t/* Clean up objects used, as they will be reused. */\n \tclear_commit_marks_many(rev_nr, rev, ALL_REV_FLAGS);\n \n+\trelease_revisions(&revs);\n \treturn res;\n }\n \n@@ -964,6 +965,7 @@ static void show_diff_tree(struct repository *r,\n \n \tsetup_revisions(ARRAY_SIZE(argv) - 1, argv, &opt, NULL);\n \tlog_tree_commit(&opt, commit);\n+\trelease_revisions(&opt);\n }\n \n /*\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 3ffb86a4338..f507d2191cd 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -340,6 +340,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)\n \n \tunlink(file);\n \tfree(file);\n+\trelease_revisions(&rev);\n \treturn 0;\n }\n \ndiff --git a/builtin/am.c b/builtin/am.c\nindex 0f4111bafa0..93bec62afa9 100644\n--- a/builtin/am.c\n+++ b/builtin/am.c\n@@ -1397,6 +1397,7 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm\n \tadd_pending_object(&rev_info, &commit->object, \"\");\n \tdiff_setup_done(&rev_info.diffopt);\n \tlog_tree_commit(&rev_info, commit);\n+\trelease_revisions(&rev_info);\n }\n \n /**\n@@ -1429,6 +1430,7 @@ static void write_index_patch(const struct am_state *state)\n \tadd_pending_object(&rev_info, &tree->object, \"\");\n \tdiff_setup_done(&rev_info.diffopt);\n \trun_diff_index(&rev_info, 1);\n+\trelease_revisions(&rev_info);\n }\n \n /**\n@@ -1582,6 +1584,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa\n \t\tadd_pending_oid(&rev_info, \"HEAD\", &our_tree, 0);\n \t\tdiff_setup_done(&rev_info.diffopt);\n \t\trun_diff_index(&rev_info, 1);\n+\t\trelease_revisions(&rev_info);\n \t}\n \n \tif (run_apply(state, index_path))\ndiff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c\nindex 8b2b259ff0d..e4d7b6779ae 100644\n--- a/builtin/bisect--helper.c\n+++ b/builtin/bisect--helper.c\n@@ -555,6 +555,7 @@ static int bisect_skipped_commits(struct bisect_terms *terms)\n \treset_revision_walk();\n \n \tstrbuf_release(&commit_name);\n+\trelease_revisions(&revs);\n \tfclose(fp);\n \treturn 0;\n }\n@@ -1041,6 +1042,7 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar\n \t\t\t\t\t\toid_to_hex(&commit->object.oid));\n \n \t\t\treset_revision_walk();\n+\t\t\trelease_revisions(&revs);\n \t\t} else {\n \t\t\tstrvec_push(&argv_state, argv[i]);\n \t\t}\ndiff --git a/builtin/blame.c b/builtin/blame.c\nindex 885b381ab83..24bac822c56 100644\n--- a/builtin/blame.c\n+++ b/builtin/blame.c\n@@ -1203,5 +1203,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)\n \n cleanup:\n \tcleanup_scoreboard(&sb);\n+\trelease_revisions(&revs);\n \treturn 0;\n }\ndiff --git a/builtin/checkout.c b/builtin/checkout.c\nindex 4d9e0bd3ac1..7ad4a7113c9 100644\n--- a/builtin/checkout.c\n+++ b/builtin/checkout.c\n@@ -1082,6 +1082,7 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne\n \n \t/* Clean up objects used, as they will be reused. */\n \trepo_clear_commit_marks(the_repository, ALL_REV_FLAGS);\n+\trelease_revisions(&revs);\n }\n \n static int switch_branches(const struct checkout_opts *opts,\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex 009a1de0a3d..c7eda9bbb72 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1123,6 +1123,7 @@ static const char *find_author_by_nickname(const char *name)\n \t\tstrbuf_release(&buf);\n \t\tformat_commit_message(commit, \"%aN <%aE>\", &buf, &ctx);\n \t\tclear_mailmap(&mailmap);\n+\t\trelease_revisions(&revs);\n \t\treturn strbuf_detach(&buf, NULL);\n \t}\n \tdie(_(\"--author '%s' is not 'Name <email>' and matches no existing author\"), name);\ndiff --git a/builtin/describe.c b/builtin/describe.c\nindex 42159cd26bd..a76f1a1a7a7 100644\n--- a/builtin/describe.c\n+++ b/builtin/describe.c\n@@ -517,6 +517,7 @@ static void describe_blob(struct object_id oid, struct strbuf *dst)\n \n \ttraverse_commit_list(&revs, process_commit, process_object, &pcd);\n \treset_revision_walk();\n+\trelease_revisions(&revs);\n }\n \n static void describe(const char *arg, int last_one)\n@@ -667,6 +668,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)\n \t\t\t\tsuffix = NULL;\n \t\t\telse\n \t\t\t\tsuffix = dirty;\n+\t\t\trelease_revisions(&revs);\n \t\t}\n \t\tdescribe(\"HEAD\", 1);\n \t} else if (dirty) {\ndiff --git a/builtin/fast-export.c b/builtin/fast-export.c\nindex a7d72697fba..f34ae451ee3 100644\n--- a/builtin/fast-export.c\n+++ b/builtin/fast-export.c\n@@ -1275,6 +1275,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)\n \t\tprintf(\"done\\n\");\n \n \trefspec_clear(&refspecs);\n+\trelease_revisions(&revs);\n \n \treturn 0;\n }\ndiff --git a/builtin/merge.c b/builtin/merge.c\nindex f178f5a3ee1..d9784d4891c 100644\n--- a/builtin/merge.c\n+++ b/builtin/merge.c\n@@ -443,6 +443,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead\n \t}\n \twrite_file_buf(git_path_squash_msg(the_repository), out.buf, out.len);\n \tstrbuf_release(&out);\n+\trelease_revisions(&rev);\n }\n \n static void finish(struct commit *head_commit,\n@@ -998,6 +999,7 @@ static int evaluate_result(void)\n \t */\n \tcnt += count_unmerged_entries();\n \n+\trelease_revisions(&rev);\n \treturn cnt;\n }\n \ndiff --git a/builtin/pack-objects.c b/builtin/pack-objects.c\nindex 014dcd4bc98..180fff03569 100644\n--- a/builtin/pack-objects.c\n+++ b/builtin/pack-objects.c\n@@ -4177,11 +4177,13 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)\n \t\tread_object_list_from_stdin();\n \t} else if (pfd.have_revs) {\n \t\tget_object_list(&pfd.revs, rp.nr, rp.v);\n+\t\trelease_revisions(&pfd.revs);\n \t} else {\n \t\tstruct rev_info revs;\n \n \t\trepo_init_revisions(the_repository, &revs, NULL);\n \t\tget_object_list(&revs, rp.nr, rp.v);\n+\t\trelease_revisions(&revs);\n \t}\n \tcleanup_preferred_base();\n \tif (include_tag && nr_result)\ndiff --git a/builtin/prune.c b/builtin/prune.c\nindex c2bcdc07db4..df376b2ed1e 100644\n--- a/builtin/prune.c\n+++ b/builtin/prune.c\n@@ -196,5 +196,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix)\n \t\tprune_shallow(show_only ? PRUNE_SHOW_ONLY : 0);\n \t}\n \n+\trelease_revisions(&revs);\n \treturn 0;\n }\ndiff --git a/builtin/reflog.c b/builtin/reflog.c\nindex c943c2aabe6..4dd297dce86 100644\n--- a/builtin/reflog.c\n+++ b/builtin/reflog.c\n@@ -293,6 +293,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)\n \t\tif (verbose)\n \t\t\tprintf(_(\"Marking reachable objects...\"));\n \t\tmark_reachable_objects(&revs, 0, 0, NULL);\n+\t\trelease_revisions(&revs);\n \t\tif (verbose)\n \t\t\tputchar('\\n');\n \t}\ndiff --git a/builtin/shortlog.c b/builtin/shortlog.c\nindex fcde07c9367..35825f075e3 100644\n--- a/builtin/shortlog.c\n+++ b/builtin/shortlog.c\n@@ -422,6 +422,8 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)\n \telse\n \t\tget_from_rev(&rev, &log);\n \n+\trelease_revisions(&rev);\n+\n \tshortlog_output(&log);\n \tif (log.file != stdout)\n \t\tfclose(log.file);\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 2c87ef9364f..5d6b43fe3c0 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1269,6 +1269,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \t\trun_diff_files(&rev, 0);\n \tprepare_submodule_summary(info, &list);\n \tstrvec_clear(&diff_args);\n+\trelease_revisions(&rev);\n \treturn 0;\n }\n \ndiff --git a/fmt-merge-msg.c b/fmt-merge-msg.c\nindex baca57d5b64..f48f44f9cd1 100644\n--- a/fmt-merge-msg.c\n+++ b/fmt-merge-msg.c\n@@ -699,6 +699,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,\n \t\t\tshortlog(origins.items[i].string,\n \t\t\t\t origins.items[i].util,\n \t\t\t\t head, &rev, opts, out);\n+\t\trelease_revisions(&rev);\n \t}\n \n \tstrbuf_complete_line(out);\ndiff --git a/merge-ort.c b/merge-ort.c\nindex 8545354dafd..4c0d4eba19b 100644\n--- a/merge-ort.c\n+++ b/merge-ort.c\n@@ -1594,6 +1594,7 @@ static int find_first_merges(struct repository *repo,\n \t}\n \n \tobject_array_clear(&merges);\n+\trelease_revisions(&revs);\n \treturn result->nr;\n }\n \ndiff --git a/merge-recursive.c b/merge-recursive.c\nindex 32bbba5fbb1..acd13b2b069 100644\n--- a/merge-recursive.c\n+++ b/merge-recursive.c\n@@ -1160,6 +1160,7 @@ static int find_first_merges(struct repository *repo,\n \t}\n \n \tobject_array_clear(&merges);\n+\trelease_revisions(&revs);\n \treturn result->nr;\n }\n \ndiff --git a/midx.c b/midx.c\nindex 107365d2114..d5724df7d60 100644\n--- a/midx.c\n+++ b/midx.c\n@@ -1061,6 +1061,7 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr\n \tif (indexed_commits_nr_p)\n \t\t*indexed_commits_nr_p = cb.commits_nr;\n \n+\trelease_revisions(&revs);\n \treturn cb.commits;\n }\n \ndiff --git a/pack-bitmap-write.c b/pack-bitmap-write.c\nindex cf681547f2e..c43375bd344 100644\n--- a/pack-bitmap-write.c\n+++ b/pack-bitmap-write.c\n@@ -326,6 +326,7 @@ static void bitmap_builder_init(struct bitmap_builder *bb,\n \ttrace2_data_intmax(\"pack-bitmap-write\", the_repository,\n \t\t\t   \"num_maximal_commits\", num_maximal);\n \n+\trelease_revisions(&revs);\n \tfree_commit_list(reusable);\n }\n \ndiff --git a/ref-filter.c b/ref-filter.c\nindex 7838bd22b8d..a91688bbf17 100644\n--- a/ref-filter.c\n+++ b/ref-filter.c\n@@ -2392,6 +2392,7 @@ static void reach_filter(struct ref_array *array,\n \t\tclear_commit_marks(merge_commit, ALL_REV_FLAGS);\n \t}\n \n+\trelease_revisions(&revs);\n \tfree(to_clear);\n }\n \ndiff --git a/remote.c b/remote.c\nindex 42a4e7106e1..fa3152a5d52 100644\n--- a/remote.c\n+++ b/remote.c\n@@ -2172,6 +2172,7 @@ static int stat_branch_pair(const char *branch_name, const char *base,\n \tclear_commit_marks(theirs, ALL_REV_FLAGS);\n \n \tstrvec_clear(&argv);\n+\trelease_revisions(&revs);\n \treturn 1;\n }\n \ndiff --git a/sequencer.c b/sequencer.c\nindex a1bb39383db..f9d7acd1065 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -1347,6 +1347,7 @@ void print_commit_summary(struct repository *r,\n \t\tlog_tree_commit(&rev, commit);\n \t}\n \n+\trelease_revisions(&rev);\n \tstrbuf_release(&format);\n }\n \n@@ -3415,6 +3416,7 @@ static int make_patch(struct repository *r,\n \t\tunuse_commit_buffer(commit, commit_buffer);\n \t}\n \tstrbuf_release(&buf);\n+\trelease_revisions(&log_tree_opt);\n \n \treturn res;\n }\n@@ -4525,6 +4527,7 @@ static int pick_commits(struct repository *r,\n \t\t\t\t\t      &log_tree_opt.diffopt);\n \t\t\t\tlog_tree_diff_flush(&log_tree_opt);\n \t\t\t}\n+\t\t\trelease_revisions(&log_tree_opt);\n \t\t}\n \t\tflush_rewritten_pending();\n \t\tif (!stat(rebase_path_rewritten_list(), &st) &&\ndiff --git a/shallow.c b/shallow.c\nindex e158be58b05..797a593633f 100644\n--- a/shallow.c\n+++ b/shallow.c\n@@ -262,6 +262,7 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,\n \t\tif ((o->flags & both_flags) == both_flags)\n \t\t\to->flags &= ~not_shallow_flag;\n \t}\n+\trelease_revisions(&revs);\n \treturn result;\n }\n \ndiff --git a/submodule.c b/submodule.c\nindex 9b715b4a5cb..7b5d7753930 100644\n--- a/submodule.c\n+++ b/submodule.c\n@@ -923,9 +923,11 @@ static void collect_changed_submodules(struct repository *r,\n \t\tdiff_rev.diffopt.format_callback_data = &data;\n \t\tdiff_rev.dense_combined_merges = 1;\n \t\tdiff_tree_combined_merge(commit, &diff_rev);\n+\t\trelease_revisions(&diff_rev);\n \t}\n \n \treset_revision_walk();\n+\trelease_revisions(&rev);\n }\n \n static void free_submodules_data(struct string_list *submodules)\ndiff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c\nindex 625b2dbf822..4a45d5bac2a 100644\n--- a/t/helper/test-revision-walking.c\n+++ b/t/helper/test-revision-walking.c\n@@ -43,6 +43,7 @@ static int run_revision_walk(void)\n \t}\n \n \treset_revision_walk();\n+\trelease_revisions(&rev);\n \treturn got_revision;\n }\n \ndiff --git a/wt-status.c b/wt-status.c\nindex 922cf787f95..f9100621375 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -1152,6 +1152,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)\n \t\trev.diffopt.b_prefix = \"w/\";\n \t\trun_diff_files(&rev, 0);\n \t}\n+\trelease_revisions(&rev);\n }\n \n static void wt_longstatus_print_tracking(struct wt_status *s)\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453575","messageId":"patch-v6-13.27-fdb3a63e0a9-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 13/27] revisions API users: use release_revisions() in builtin/log.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:42Z","receivedAt":"2022-04-13T20:02:36Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In preparation for having the \"log\" family of functions make wider use\nof release_revisions() let's have them call it just before\nexiting. This changes the \"log\", \"whatchanged\", \"show\",\n\"format-patch\", etc. commands, all of which live in this file.\n\nThe release_revisions() API still only frees the \"pending\" member, but\nwill learn to release more members of \"struct rev_info\" in subsequent\ncommits.\n\nIn the case of \"format-patch\" revert the addition of UNLEAK() in\ndee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16),\nwhich will cause several tests that previously passed under\n\"TEST_PASSES_SANITIZE_LEAK=true\" to start failing.\n\nIn subsequent commits we'll now be able to use those tests to check\nwhether that part of the API is really leaking memory, and will fix\nall of those memory leaks. Removing the UNLEAK() allows us to make\nincremental progress in that direction. See [1] for further details\nabout this approach.\n\nNote that the release_revisions() will not be sufficient to deal with\nthe code in cmd_show() added in 5d7eeee2ac6 (git-show: grok blobs,\ntrees and tags, too, 2006-12-14) which clobbers the \"pending\" array in\nthe case of \"OBJ_COMMIT\". That will need to be dealt with by some\nfuture follow-up work.\n\n1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/log.c          | 20 ++++++++++++--------\n t/t4126-apply-empty.sh |  2 --\n 2 files changed, 12 insertions(+), 10 deletions(-)\n\ndiff --git a/builtin/log.c b/builtin/log.c\nindex 6f9928fabfe..c40ebe1c3f4 100644\n--- a/builtin/log.c\n+++ b/builtin/log.c\n@@ -295,6 +295,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,\n \tcmd_log_init_finish(argc, argv, prefix, rev, opt);\n }\n \n+static int cmd_log_deinit(int ret, struct rev_info *rev)\n+{\n+\trelease_revisions(rev);\n+\treturn ret;\n+}\n+\n /*\n  * This gives a rough estimate for how many commits we\n  * will print out in the list.\n@@ -558,7 +564,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)\n \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n \tif (!rev.diffopt.output_format)\n \t\trev.diffopt.output_format = DIFF_FORMAT_RAW;\n-\treturn cmd_log_walk(&rev);\n+\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n }\n \n static void show_tagger(const char *buf, struct rev_info *rev)\n@@ -677,7 +683,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n \n \tif (!rev.no_walk)\n-\t\treturn cmd_log_walk(&rev);\n+\t\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n \n \tcount = rev.pending.nr;\n \tobjects = rev.pending.objects;\n@@ -732,8 +738,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)\n \t\t\tret = error(_(\"unknown type: %d\"), o->type);\n \t\t}\n \t}\n-\tfree(objects);\n-\treturn ret;\n+\treturn cmd_log_deinit(ret, &rev);\n }\n \n /*\n@@ -761,7 +766,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)\n \trev.always_show_header = 1;\n \tcmd_log_init_finish(argc, argv, prefix, &rev, &opt);\n \n-\treturn cmd_log_walk(&rev);\n+\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n }\n \n static void log_setup_revisions_tweak(struct rev_info *rev,\n@@ -792,7 +797,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)\n \topt.revarg_opt = REVARG_COMMITTISH;\n \topt.tweak = log_setup_revisions_tweak;\n \tcmd_log_init(argc, argv, prefix, &rev, &opt);\n-\treturn cmd_log_walk(&rev);\n+\treturn cmd_log_deinit(cmd_log_walk(&rev), &rev);\n }\n \n /* format-patch */\n@@ -2289,8 +2294,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)\n \tif (rev.ref_message_ids)\n \t\tstring_list_clear(rev.ref_message_ids, 0);\n \tfree(rev.ref_message_ids);\n-\tUNLEAK(rev);\n-\treturn 0;\n+\treturn cmd_log_deinit(0, &rev);\n }\n \n static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)\ndiff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh\nindex 33860d38290..66a7ba8ab8f 100755\n--- a/t/t4126-apply-empty.sh\n+++ b/t/t4126-apply-empty.sh\n@@ -2,8 +2,6 @@\n \n test_description='apply empty'\n \n-\n-TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453576","messageId":"patch-v6-11.27-e93791b6242-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 11/27] revisions API users: add \"goto cleanup\" for release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:40Z","receivedAt":"2022-04-13T20:02:38Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a release_revisions() to various users of \"struct rev_info\" which\nrequires a minor refactoring to a \"goto cleanup\" pattern to use that\nfunction.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/diff-files.c        |  8 ++++++--\n builtin/rev-list.c          | 19 ++++++++++++-------\n builtin/stash.c             |  1 +\n builtin/submodule--helper.c | 10 +++++++---\n sequencer.c                 | 23 ++++++++++++++++-------\n t/helper/test-fast-rebase.c | 18 +++++++++++++-----\n 6 files changed, 55 insertions(+), 24 deletions(-)\n\ndiff --git a/builtin/diff-files.c b/builtin/diff-files.c\nindex 70103c40952..2bfaf9ba7ae 100644\n--- a/builtin/diff-files.c\n+++ b/builtin/diff-files.c\n@@ -77,8 +77,12 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)\n \n \tif (read_cache_preload(&rev.diffopt.pathspec) < 0) {\n \t\tperror(\"read_cache_preload\");\n-\t\treturn -1;\n+\t\tresult = -1;\n+\t\tgoto cleanup;\n \t}\n+cleanup:\n \tresult = run_diff_files(&rev, options);\n-\treturn diff_result_code(&rev.diffopt, result);\n+\tresult = diff_result_code(&rev.diffopt, result);\n+\trelease_revisions(&rev);\n+\treturn result;\n }\ndiff --git a/builtin/rev-list.c b/builtin/rev-list.c\nindex 07c0ad11d8d..30fd8e83eaf 100644\n--- a/builtin/rev-list.c\n+++ b/builtin/rev-list.c\n@@ -500,6 +500,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \tint use_bitmap_index = 0;\n \tint filter_provided_objects = 0;\n \tconst char *show_progress = NULL;\n+\tint ret = 0;\n \n \tif (argc == 2 && !strcmp(argv[1], \"-h\"))\n \t\tusage(rev_list_usage);\n@@ -583,7 +584,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \t\t}\n \t\tif (!strcmp(arg, \"--test-bitmap\")) {\n \t\t\ttest_bitmap_walk(&revs);\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t\t}\n \t\tif (skip_prefix(arg, \"--progress=\", &arg)) {\n \t\t\tshow_progress = arg;\n@@ -672,11 +673,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \n \tif (use_bitmap_index) {\n \t\tif (!try_bitmap_count(&revs, filter_provided_objects))\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t\tif (!try_bitmap_disk_usage(&revs, filter_provided_objects))\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t\tif (!try_bitmap_traversal(&revs, filter_provided_objects))\n-\t\t\treturn 0;\n+\t\t\tgoto cleanup;\n \t}\n \n \tif (prepare_revision_walk(&revs))\n@@ -696,8 +697,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \n \t\tfind_bisection(&revs.commits, &reaches, &all, bisect_flags);\n \n-\t\tif (bisect_show_vars)\n-\t\t\treturn show_bisect_vars(&info, reaches, all);\n+\t\tif (bisect_show_vars) {\n+\t\t\tret = show_bisect_vars(&info, reaches, all);\n+\t\t\tgoto cleanup;\n+\t\t}\n \t}\n \n \tif (filter_provided_objects) {\n@@ -752,5 +755,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)\n \tif (show_disk_usage)\n \t\tprintf(\"%\"PRIuMAX\"\\n\", (uintmax_t)total_disk_usage);\n \n-\treturn 0;\n+cleanup:\n+\trelease_revisions(&revs);\n+\treturn ret;\n }\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex ba030238939..fce4393d123 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -917,6 +917,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)\n cleanup:\n \tstrvec_clear(&stash_args);\n \tfree_stash_info(&info);\n+\trelease_revisions(&rev);\n \tif (do_usage)\n \t\tusage_with_options(git_stash_show_usage, options);\n \treturn ret;\ndiff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c\nindex 9a65985b418..f83c75c7768 100644\n--- a/builtin/submodule--helper.c\n+++ b/builtin/submodule--helper.c\n@@ -1232,6 +1232,7 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \tstruct strvec diff_args = STRVEC_INIT;\n \tstruct rev_info rev;\n \tstruct module_cb_list list = MODULE_CB_LIST_INIT;\n+\tint ret = 0;\n \n \tstrvec_push(&diff_args, get_diff_cmd(diff_cmd));\n \tif (info->cached)\n@@ -1257,11 +1258,13 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \t\t\tsetup_work_tree();\n \t\tif (read_cache_preload(&rev.diffopt.pathspec) < 0) {\n \t\t\tperror(\"read_cache_preload\");\n-\t\t\treturn -1;\n+\t\t\tret = -1;\n+\t\t\tgoto cleanup;\n \t\t}\n \t} else if (read_cache() < 0) {\n \t\tperror(\"read_cache\");\n-\t\treturn -1;\n+\t\tret = -1;\n+\t\tgoto cleanup;\n \t}\n \n \tif (diff_cmd == DIFF_INDEX)\n@@ -1269,9 +1272,10 @@ static int compute_summary_module_list(struct object_id *head_oid,\n \telse\n \t\trun_diff_files(&rev, 0);\n \tprepare_submodule_summary(info, &list);\n+cleanup:\n \tstrvec_clear(&diff_args);\n \trelease_revisions(&rev);\n-\treturn 0;\n+\treturn ret;\n }\n \n static int module_summary(int argc, const char **argv, const char *prefix)\ndiff --git a/sequencer.c b/sequencer.c\nindex f9d7acd1065..41ae5e25278 100644\n--- a/sequencer.c\n+++ b/sequencer.c\n@@ -5354,6 +5354,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \tint rebase_merges = flags & TODO_LIST_REBASE_MERGES;\n \tint reapply_cherry_picks = flags & TODO_LIST_REAPPLY_CHERRY_PICKS;\n \tint skipped_commit = 0;\n+\tint ret = 0;\n \n \trepo_init_revisions(r, &revs, NULL);\n \trevs.verbose_header = 1;\n@@ -5377,14 +5378,20 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \tpp.fmt = revs.commit_format;\n \tpp.output_encoding = get_log_output_encoding();\n \n-\tif (setup_revisions(argc, argv, &revs, NULL) > 1)\n-\t\treturn error(_(\"make_script: unhandled options\"));\n+\tif (setup_revisions(argc, argv, &revs, NULL) > 1) {\n+\t\tret = error(_(\"make_script: unhandled options\"));\n+\t\tgoto cleanup;\n+\t}\n \n-\tif (prepare_revision_walk(&revs) < 0)\n-\t\treturn error(_(\"make_script: error preparing revisions\"));\n+\tif (prepare_revision_walk(&revs) < 0) {\n+\t\tret = error(_(\"make_script: error preparing revisions\"));\n+\t\tgoto cleanup;\n+\t}\n \n-\tif (rebase_merges)\n-\t\treturn make_script_with_merges(&pp, &revs, out, flags);\n+\tif (rebase_merges) {\n+\t\tret = make_script_with_merges(&pp, &revs, out, flags);\n+\t\tgoto cleanup;\n+\t}\n \n \twhile ((commit = get_revision(&revs))) {\n \t\tint is_empty = is_original_commit_empty(commit);\n@@ -5408,7 +5415,9 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,\n \tif (skipped_commit)\n \t\tadvise_if_enabled(ADVICE_SKIPPED_CHERRY_PICKS,\n \t\t\t\t  _(\"use --reapply-cherry-picks to include skipped commits\"));\n-\treturn 0;\n+cleanup:\n+\trelease_revisions(&revs);\n+\treturn ret;\n }\n \n /*\ndiff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c\nindex 993b90eaedd..4e5553e2024 100644\n--- a/t/helper/test-fast-rebase.c\n+++ b/t/helper/test-fast-rebase.c\n@@ -99,6 +99,7 @@ int cmd__fast_rebase(int argc, const char **argv)\n \tstruct merge_result result;\n \tstruct strbuf reflog_msg = STRBUF_INIT;\n \tstruct strbuf branch_name = STRBUF_INIT;\n+\tint ret = 0;\n \n \t/*\n \t * test-tool stuff doesn't set up the git directory by default; need to\n@@ -137,13 +138,17 @@ int cmd__fast_rebase(int argc, const char **argv)\n \trevs.topo_order = 1;\n \tstrvec_pushl(&rev_walk_args, \"\", argv[4], \"--not\", argv[3], NULL);\n \n-\tif (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1)\n-\t\treturn error(_(\"unhandled options\"));\n+\tif (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) {\n+\t\tret = error(_(\"unhandled options\"));\n+\t\tgoto cleanup;\n+\t}\n \n \tstrvec_clear(&rev_walk_args);\n \n-\tif (prepare_revision_walk(&revs) < 0)\n-\t\treturn error(_(\"error preparing revisions\"));\n+\tif (prepare_revision_walk(&revs) < 0) {\n+\t\tret = error(_(\"error preparing revisions\"));\n+\t\tgoto cleanup;\n+\t}\n \n \tinit_merge_options(&merge_opt, the_repository);\n \tmemset(&result, 0, sizeof(result));\n@@ -220,7 +225,10 @@ int cmd__fast_rebase(int argc, const char **argv)\n \t\t\t       COMMIT_LOCK | SKIP_IF_UNCHANGED))\n \t\tdie(_(\"unable to write %s\"), get_index_file());\n \n+\tret = (result.clean == 0);\n+cleanup:\n \tstrbuf_release(&reflog_msg);\n \tstrbuf_release(&branch_name);\n-\treturn (result.clean == 0);\n+\trelease_revisions(&revs);\n+\treturn ret;\n }\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453577","messageId":"patch-v6-12.27-2c744ccdbb1-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 12/27] revisions API users: use release_revisions() in http-push.c","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:41Z","receivedAt":"2022-04-13T20:02:41Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"In the case of cmd_main() in http-push.c we need to move the\ndeceleration of the \"struct rev-list\" into the loop over the\n\"remote_refs\" when adding a release_revisions().\n\nWe'd previously set up the \"revs\" for each remote, but would\npotentially leak memory on each one.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n http-push.c | 3 ++-\n 1 file changed, 2 insertions(+), 1 deletion(-)\n\ndiff --git a/http-push.c b/http-push.c\nindex f0c044dcf76..01e7c2ac5c8 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -1689,7 +1689,6 @@ int cmd_main(int argc, const char **argv)\n \tstruct refspec rs = REFSPEC_INIT_PUSH;\n \tstruct remote_lock *ref_lock = NULL;\n \tstruct remote_lock *info_ref_lock = NULL;\n-\tstruct rev_info revs;\n \tint delete_branch = 0;\n \tint force_delete = 0;\n \tint objects_to_send;\n@@ -1825,6 +1824,7 @@ int cmd_main(int argc, const char **argv)\n \n \tnew_refs = 0;\n \tfor (ref = remote_refs; ref; ref = ref->next) {\n+\t\tstruct rev_info revs;\n \t\tstruct strvec commit_argv = STRVEC_INIT;\n \n \t\tif (!ref->peer_ref)\n@@ -1941,6 +1941,7 @@ int cmd_main(int argc, const char **argv)\n \t\tunlock_remote(ref_lock);\n \t\tcheck_locks();\n \t\tstrvec_clear(&commit_argv);\n+\t\trelease_revisions(&revs);\n \t}\n \n \t/* Update remote server info if appropriate */\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453578","messageId":"patch-v6-15.27-71d59de2fe1-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 15/27] revisions API users: use release_revisions() for \"prune_data\" users","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:44Z","receivedAt":"2022-04-13T20:02:43Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use release_revisions() for users of \"struct rev_list\" that reach into\nthe \"struct rev_info\" and clear the \"prune_data\" already.\n\nIn a subsequent commit we'll teach release_revisions() to clear this\nitself, but in the meantime let's invoke release_revisions() here to\nclear anything else we may have missed, and for reasons of having\nconsistent boilerplate.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/add.c | 1 +\n diff-lib.c    | 1 +\n wt-status.c   | 1 +\n 3 files changed, 3 insertions(+)\n\ndiff --git a/builtin/add.c b/builtin/add.c\nindex f507d2191cd..115a26ea633 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -143,6 +143,7 @@ int add_files_to_cache(const char *prefix,\n \trev.max_count = 0; /* do not compare unmerged paths with stage #2 */\n \trun_diff_files(&rev, DIFF_RACY_IS_MODIFIED);\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n \treturn !!data.add_errors;\n }\n \ndiff --git a/diff-lib.c b/diff-lib.c\nindex d6800274bd5..0f16281253f 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -642,6 +642,7 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)\n \tif (diff_cache(&revs, tree_oid, NULL, 1))\n \t\texit(128);\n \tclear_pathspec(&revs.prune_data);\n+\trelease_revisions(&revs);\n \treturn 0;\n }\n \ndiff --git a/wt-status.c b/wt-status.c\nindex f9100621375..a14fad1e03a 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -617,6 +617,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)\n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_files(&rev, 0);\n \tclear_pathspec(&rev.prune_data);\n+\trelease_revisions(&rev);\n }\n \n static void wt_status_collect_changes_index(struct wt_status *s)\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453579","messageId":"patch-v6-16.27-dcf63f64135-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 16/27] revisions API: have release_revisions() release \"commits\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:45Z","receivedAt":"2022-04-13T20:02:44Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"commits\" in the \"struct rev_info\".\n\nWe don't expect to use this \"struct rev_info\" again, so there's no\nreason to NULL out revs->commits, as e.g. simplify_merges() and\ncreate_boundary_commit_list() do.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/revision.c b/revision.c\nindex 472fff1e0a7..553f7de8250 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2928,6 +2928,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \n void release_revisions(struct rev_info *revs)\n {\n+\tfree_commit_list(revs->commits);\n \tobject_array_clear(&revs->pending);\n }\n \n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453580","messageId":"patch-v6-14.27-31ce58da43d-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 14/27] revisions API users: use release_revisions() with UNLEAK()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:43Z","receivedAt":"2022-04-13T20:02:46Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Use a release_revisions() with those \"struct rev_list\" users which\nalready \"UNLEAK\" the struct. It may seem odd to simultaneously attempt\nto free() memory, but also to explicitly ignore whether we have memory\nleaks in the same.\n\nAs explained in preceding commits this is being done to use the\nbuilt-in commands as a guinea pig for whether the release_revisions()\nfunction works as expected, we'd like to test e.g. whether we segfault\nas we change it. In subsequent commits we'll then remove these\nUNLEAK() as the function is made to free the memory that caused us to\nadd them in the first place.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/diff-index.c | 4 +++-\n builtin/diff.c       | 1 +\n 2 files changed, 4 insertions(+), 1 deletion(-)\n\ndiff --git a/builtin/diff-index.c b/builtin/diff-index.c\nindex 5fd23ab5b6c..3a83183c312 100644\n--- a/builtin/diff-index.c\n+++ b/builtin/diff-index.c\n@@ -71,5 +71,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)\n \t}\n \tresult = run_diff_index(&rev, option);\n \tUNLEAK(rev);\n-\treturn diff_result_code(&rev.diffopt, result);\n+\tresult = diff_result_code(&rev.diffopt, result);\n+\trelease_revisions(&rev);\n+\treturn result;\n }\ndiff --git a/builtin/diff.c b/builtin/diff.c\nindex bb7fafca618..dd48336da56 100644\n--- a/builtin/diff.c\n+++ b/builtin/diff.c\n@@ -595,6 +595,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \tif (1 < rev.diffopt.skip_stat_unmatch)\n \t\trefresh_index_quietly();\n \tUNLEAK(rev);\n+\trelease_revisions(&rev);\n \tUNLEAK(ent);\n \tUNLEAK(blob);\n \treturn result;\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453581","messageId":"patch-v6-17.27-3e226c5b3d4-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 17/27] revisions API: have release_revisions() release \"mailmap\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:46Z","receivedAt":"2022-04-13T20:02:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"mailmap\" in the \"struct rev_info\".\n\nThe log family of functions now calls the clear_mailmap() function\nadded in fa8afd18e5a (revisions API: provide and use a\nrelease_revisions(), 2021-09-19), allowing us to whitelist some tests\nwith \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nUnfortunately having a pointer to a mailmap in \"struct rev_info\"\ninstead of an embedded member that we \"own\" get a bit messy, as can be\nseen in the change to builtin/commit.c.\n\nWhen we free() this data we won't be able to tell apart a pointer to a\n\"mailmap\" on the heap from one on the stack. As seen in\nea57bc0d41b (log: add --use-mailmap option, 2013-01-05) the \"log\"\nfamily allocates it on the heap, but in the find_author_by_nickname()\ncode added in ea16794e430 (commit: search author pattern against\nmailmap, 2013-08-23) we allocated it on the stack instead.\n\nIdeally we'd simply change that member to a \"struct string_list\nmailmap\" and never free() the \"mailmap\" itself, but that would be a\nmuch larger change to the revisions API.\n\nWe have code that needs to hand an existing \"mailmap\" to a \"struct\nrev_info\", while we could change all of that, let's not go there\nnow.\n\nThe complexity isn't in the ownership of the \"mailmap\" per-se, but\nthat various things assume a \"rev_info.mailmap == NULL\" means \"doesn't\nwant mailmap\", if we changed that to an init'd \"struct string_list\nwe'd need to carefully refactor things to change those assumptions.\n\nLet's instead always free() it, and simply declare that if you add\nsuch a \"mailmap\" it must be allocated on the heap. Any modern libc\nwill correctly panic if we free() a stack variable, so this should be\nsafe going forward.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/commit.c                   | 5 ++---\n revision.c                         | 9 +++++++++\n t/t0056-git-C.sh                   | 1 +\n t/t3302-notes-index-expensive.sh   | 1 +\n t/t4055-diff-context.sh            | 1 +\n t/t4066-diff-emit-delay.sh         | 1 +\n t/t7008-filter-branch-null-sha1.sh | 1 +\n 7 files changed, 16 insertions(+), 3 deletions(-)\n\ndiff --git a/builtin/commit.c b/builtin/commit.c\nindex c7eda9bbb72..cd6cebcf8c8 100644\n--- a/builtin/commit.c\n+++ b/builtin/commit.c\n@@ -1100,7 +1100,6 @@ static const char *find_author_by_nickname(const char *name)\n \tstruct rev_info revs;\n \tstruct commit *commit;\n \tstruct strbuf buf = STRBUF_INIT;\n-\tstruct string_list mailmap = STRING_LIST_INIT_NODUP;\n \tconst char *av[20];\n \tint ac = 0;\n \n@@ -1111,7 +1110,8 @@ static const char *find_author_by_nickname(const char *name)\n \tav[++ac] = buf.buf;\n \tav[++ac] = NULL;\n \tsetup_revisions(ac, av, &revs, NULL);\n-\trevs.mailmap = &mailmap;\n+\trevs.mailmap = xmalloc(sizeof(struct string_list));\n+\tstring_list_init_nodup(revs.mailmap);\n \tread_mailmap(revs.mailmap);\n \n \tif (prepare_revision_walk(&revs))\n@@ -1122,7 +1122,6 @@ static const char *find_author_by_nickname(const char *name)\n \t\tctx.date_mode.type = DATE_NORMAL;\n \t\tstrbuf_release(&buf);\n \t\tformat_commit_message(commit, \"%aN <%aE>\", &buf, &ctx);\n-\t\tclear_mailmap(&mailmap);\n \t\trelease_revisions(&revs);\n \t\treturn strbuf_detach(&buf, NULL);\n \t}\ndiff --git a/revision.c b/revision.c\nindex 553f7de8250..622f0faecc4 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2926,10 +2926,19 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \treturn left;\n }\n \n+static void release_revisions_mailmap(struct string_list *mailmap)\n+{\n+\tif (!mailmap)\n+\t\treturn;\n+\tclear_mailmap(mailmap);\n+\tfree(mailmap);\n+}\n+\n void release_revisions(struct rev_info *revs)\n {\n \tfree_commit_list(revs->commits);\n \tobject_array_clear(&revs->pending);\n+\trelease_revisions_mailmap(revs->mailmap);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\ndiff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh\nindex 2630e756dab..752aa8c9454 100755\n--- a/t/t0056-git-C.sh\n+++ b/t/t0056-git-C.sh\n@@ -2,6 +2,7 @@\n \n test_description='\"-C <path>\" option and its effects on other path-related options'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success '\"git -C <path>\" runs git from the directory <path>' '\ndiff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh\nindex bb5fea02a03..d0c4d38b4d4 100755\n--- a/t/t3302-notes-index-expensive.sh\n+++ b/t/t3302-notes-index-expensive.sh\n@@ -8,6 +8,7 @@ test_description='Test commit notes index (expensive!)'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n create_repo () {\ndiff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh\nindex 741e0803c1a..73048d0a526 100755\n--- a/t/t4055-diff-context.sh\n+++ b/t/t4055-diff-context.sh\n@@ -5,6 +5,7 @@\n \n test_description='diff.context configuration'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t4066-diff-emit-delay.sh b/t/t4066-diff-emit-delay.sh\nindex a1de63b77f8..0ecb3915412 100755\n--- a/t/t4066-diff-emit-delay.sh\n+++ b/t/t4066-diff-emit-delay.sh\n@@ -4,6 +4,7 @@ test_description='test combined/stat/moved interaction'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # This test covers a weird 3-way interaction between \"--cc -p\", which will run\ndiff --git a/t/t7008-filter-branch-null-sha1.sh b/t/t7008-filter-branch-null-sha1.sh\nindex 9ba9f24ad2f..93fbc92b8db 100755\n--- a/t/t7008-filter-branch-null-sha1.sh\n+++ b/t/t7008-filter-branch-null-sha1.sh\n@@ -1,6 +1,7 @@\n #!/bin/sh\n \n test_description='filter-branch removal of trees with null sha1'\n+\n . ./test-lib.sh\n \n test_expect_success 'setup: base commits' '\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453582","messageId":"patch-v6-19.27-2fc60559f1c-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 19/27] revisions API: have release_revisions() release \"filter\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:48Z","receivedAt":"2022-04-13T20:02:51Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"filter\" in the \"struct rev_info\". This in combination with a\npreceding change to free \"cmdline\" means that we can mark another set\nof tests as passing under \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nThe \"filter\" member was added recently in ffaa137f646 (revision: put\nobject filter into struct rev_info, 2022-03-09), and this fixes leaks\nintruded in the subsequent leak 7940941de1f (pack-objects: use\nrev.filter when possible, 2022-03-09) and 105c6f14ad3 (bundle: parse\nfilter capability, 2022-03-09).\n\nThe \"builtin/pack-objects.c\" leak in 7940941de1f was effectively with\nus already, but the variable was referred to by a \"static\" file-scoped\nvariable. The \"bundle.c \" leak in 105c6f14ad3 was newly introduced\nwith the new \"filter\" feature for bundles.\n\nThe \"t5600-clone-fail-cleanup.sh\" change here to add\n\"TEST_PASSES_SANITIZE_LEAK=true\" is one of the cases where\nrun-command.c in not carrying the abort() exit code upwards would have\nhad that test passing before, but now it *actually* passes[1]. We\nshould fix the lack of 1=1 mapping of SANITIZE=leak testing to actual\nleaks some other time, but it's an existing edge case, let's just mark\nthe really-passing test as passing for now.\n\n1. https://lore.kernel.org/git/220303.86fsnz5o9w.gmgdl@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c                         | 1 +\n t/t1060-object-corruption.sh       | 1 +\n t/t2015-checkout-unborn.sh         | 1 +\n t/t4207-log-decoration-colors.sh   | 1 +\n t/t5301-sliding-window.sh          | 2 ++\n t/t5313-pack-bounds-checks.sh      | 2 ++\n t/t5316-pack-delta-depth.sh        | 2 ++\n t/t5320-delta-islands.sh           | 2 ++\n t/t5322-pack-objects-sparse.sh     | 1 +\n t/t5506-remote-groups.sh           | 1 +\n t/t5513-fetch-track.sh             | 1 +\n t/t5532-fetch-proxy.sh             | 2 ++\n t/t5600-clone-fail-cleanup.sh      | 1 +\n t/t5900-repo-selection.sh          | 2 ++\n t/t6101-rev-parse-parents.sh       | 1 +\n t/t6114-keep-packs.sh              | 2 ++\n t/t7702-repack-cyclic-alternate.sh | 2 ++\n t/t9127-git-svn-partial-rebuild.sh | 1 -\n 18 files changed, 25 insertions(+), 1 deletion(-)\n\ndiff --git a/revision.c b/revision.c\nindex de4076e77de..a9d6d3a8dca 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2948,6 +2948,7 @@ void release_revisions(struct rev_info *revs)\n \tfree_commit_list(revs->commits);\n \tobject_array_clear(&revs->pending);\n \trelease_revisions_cmdline(&revs->cmdline);\n+\tlist_objects_filter_release(&revs->filter);\n \trelease_revisions_mailmap(revs->mailmap);\n }\n \ndiff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh\nindex e8a58b15897..5b8e47e346c 100755\n--- a/t/t1060-object-corruption.sh\n+++ b/t/t1060-object-corruption.sh\n@@ -2,6 +2,7 @@\n \n test_description='see how we handle various forms of corruption'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # convert \"1234abcd\" to \".git/objects/12/34abcd\"\ndiff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh\nindex a9721215fab..9425aae6395 100755\n--- a/t/t2015-checkout-unborn.sh\n+++ b/t/t2015-checkout-unborn.sh\n@@ -4,6 +4,7 @@ test_description='checkout from unborn branch'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh\nindex b8709424981..36ac6aff1e4 100755\n--- a/t/t4207-log-decoration-colors.sh\n+++ b/t/t4207-log-decoration-colors.sh\n@@ -8,6 +8,7 @@ test_description='Test for \"git log --decorate\" colors'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t5301-sliding-window.sh b/t/t5301-sliding-window.sh\nindex 76f9798ab95..3ccaaeb3977 100755\n--- a/t/t5301-sliding-window.sh\n+++ b/t/t5301-sliding-window.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='mmap sliding window tests'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success \\\ndiff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh\nindex 535313e4dc8..cc4cfaa9d37 100755\n--- a/t/t5313-pack-bounds-checks.sh\n+++ b/t/t5313-pack-bounds-checks.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='bounds-checking of access to mmapped on-disk file formats'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n clear_base () {\ndiff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh\nindex e9045009a11..eb4ef3dda4d 100755\n--- a/t/t5316-pack-delta-depth.sh\n+++ b/t/t5316-pack-delta-depth.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='pack-objects breaks long cross-pack delta chains'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # This mirrors a repeated push setup:\ndiff --git a/t/t5320-delta-islands.sh b/t/t5320-delta-islands.sh\nindex fea92a5777f..124d47603df 100755\n--- a/t/t5320-delta-islands.sh\n+++ b/t/t5320-delta-islands.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='exercise delta islands'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # returns true iff $1 is a delta based on $2\ndiff --git a/t/t5322-pack-objects-sparse.sh b/t/t5322-pack-objects-sparse.sh\nindex d39958c066d..770695c9278 100755\n--- a/t/t5322-pack-objects-sparse.sh\n+++ b/t/t5322-pack-objects-sparse.sh\n@@ -4,6 +4,7 @@ test_description='pack-objects object selection using sparse algorithm'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup repo' '\ndiff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh\nindex 8f150c0793e..5bac03ede81 100755\n--- a/t/t5506-remote-groups.sh\n+++ b/t/t5506-remote-groups.sh\n@@ -4,6 +4,7 @@ test_description='git remote group handling'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n mark() {\ndiff --git a/t/t5513-fetch-track.sh b/t/t5513-fetch-track.sh\nindex 65d1e05bd62..c46c4dbaefc 100755\n--- a/t/t5513-fetch-track.sh\n+++ b/t/t5513-fetch-track.sh\n@@ -2,6 +2,7 @@\n \n test_description='fetch follows remote-tracking branches correctly'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh\nindex 9c2798603b4..d664912799b 100755\n--- a/t/t5532-fetch-proxy.sh\n+++ b/t/t5532-fetch-proxy.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='fetching via git:// using core.gitproxy'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup remote repo' '\ndiff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh\nindex 34b3df40275..c814afa5656 100755\n--- a/t/t5600-clone-fail-cleanup.sh\n+++ b/t/t5600-clone-fail-cleanup.sh\n@@ -13,6 +13,7 @@ Unless the directory already exists, in which case we clean up only what we\n wrote.\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n corrupt_repo () {\ndiff --git a/t/t5900-repo-selection.sh b/t/t5900-repo-selection.sh\nindex 14e59c5b3e4..a84faac242d 100755\n--- a/t/t5900-repo-selection.sh\n+++ b/t/t5900-repo-selection.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='selecting remote repo in ambiguous cases'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n reset() {\ndiff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh\nindex c571fa51797..a3a41c7a3e4 100755\n--- a/t/t6101-rev-parse-parents.sh\n+++ b/t/t6101-rev-parse-parents.sh\n@@ -8,6 +8,7 @@ test_description='Test git rev-parse with different parent options'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_cmp_rev_output () {\ndiff --git a/t/t6114-keep-packs.sh b/t/t6114-keep-packs.sh\nindex 9239d8aa46d..44246f8a63e 100755\n--- a/t/t6114-keep-packs.sh\n+++ b/t/t6114-keep-packs.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='rev-list with .keep packs'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t7702-repack-cyclic-alternate.sh b/t/t7702-repack-cyclic-alternate.sh\nindex 93b74867ac8..f3cdb98eec2 100755\n--- a/t/t7702-repack-cyclic-alternate.sh\n+++ b/t/t7702-repack-cyclic-alternate.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='repack involving cyclic alternate'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh\nindex 90b1b30dde5..97f495bd49b 100755\n--- a/t/t9127-git-svn-partial-rebuild.sh\n+++ b/t/t9127-git-svn-partial-rebuild.sh\n@@ -5,7 +5,6 @@\n \n test_description='git svn partial-rebuild tests'\n \n-TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize svnrepo' '\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453583","messageId":"patch-v6-20.27-e7e0e46bbb2-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 20/27] revisions API: have release_revisions() release \"grep_filter\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:49Z","receivedAt":"2022-04-13T20:02:54Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"grep_filter\" in the \"struct rev_info\".This allows us to mark a test\nas passing under \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c               | 1 +\n t/t9151-svn-mergeinfo.sh | 1 -\n 2 files changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/revision.c b/revision.c\nindex a9d6d3a8dca..1db58c3e4db 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2950,6 +2950,7 @@ void release_revisions(struct rev_info *revs)\n \trelease_revisions_cmdline(&revs->cmdline);\n \tlist_objects_filter_release(&revs->filter);\n \trelease_revisions_mailmap(revs->mailmap);\n+\tfree_grep_patterns(&revs->grep_filter);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\ndiff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh\nindex 85221d439bd..c93a5beab25 100755\n--- a/t/t9151-svn-mergeinfo.sh\n+++ b/t/t9151-svn-mergeinfo.sh\n@@ -5,7 +5,6 @@\n \n test_description='git-svn svn mergeinfo properties'\n \n-TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'load svn dump' \"\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453584","messageId":"patch-v6-18.27-ccae0f32ad5-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 18/27] revisions API: have release_revisions() release \"cmdline\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:47Z","receivedAt":"2022-04-13T20:02:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"cmdline\" in the \"struct rev_info\". This in combination with a\npreceding change to free \"commits\" and \"mailmap\" means that we can\nwhitelist another test under \"TEST_PASSES_SANITIZE_LEAK=true\".\n\nThere was a proposal in [1] to do away with xstrdup()-ing this\nadd_rev_cmdline(), perhaps that would be worthwhile, but for now let's\njust free() it.\n\nWe could also make that a \"char *\" in \"struct rev_cmdline_entry\"\nitself, but since we own it let's expose it as a constant to outside\ncallers. I proposed that in [2] but have since changed my mind. See\n14d30cdfc04 (ref-filter: fix memory leak in `free_array_item()`,\n2019-07-10), c514c62a4fd (checkout: fix leak of non-existent branch\nnames, 2020-08-14) and other log history hits for \"free((char *)\" for\nprior art.\n\nThis includes the tests we had false-positive passes on before my\n6798b08e848 (perl Git.pm: don't ignore signalled failure in\n_cmd_close(), 2022-02-01), now they pass for real.\n\nSince there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to\nlist those that don't pass than to touch most of those 66. So let's\nintroduce a \"TEST_FAILS_SANITIZE_LEAK=true\", which if set in the tests\nwon't cause lib-git-svn.sh to set \"TEST_PASSES_SANITIZE_LEAK=true.\n\nThis change also marks all the tests that we removed\n\"TEST_FAILS_SANITIZE_LEAK=true\" from in an earlier commit due to\nremoving the UNLEAK() from cmd_format_patch(), we can now assert that\nits API use doesn't leak any \"struct rev_info\" memory.\n\nThis change also made commit \"t5503-tagfollow.sh\" pass on current\nmaster, but that would regress when combined with\nps/fetch-atomic-fixup's de004e848a9 (t5503: simplify setup of test\nwhich exercises failure of backfill, 2022-03-03) (through no fault of\nthat topic, that change started using \"git clone\" in the test, which\nhas an outstanding leak). Let's leave that test out for now to avoid\nin-flight semantic conflicts.\n\n1. https://lore.kernel.org/git/YUj%2FgFRh6pwrZalY@carlos-mbp.lan/\n2. https://lore.kernel.org/git/87o88obkb1.fsf@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c                                 | 10 ++++++++++\n t/lib-git-svn.sh                           |  4 ++++\n t/t0062-revision-walking.sh                |  1 +\n t/t0101-at-syntax.sh                       |  2 ++\n t/t1060-object-corruption.sh               |  1 +\n t/t3303-notes-subtrees.sh                  |  1 +\n t/t3305-notes-fanout.sh                    |  1 +\n t/t3408-rebase-multi-line.sh               |  1 +\n t/t4027-diff-submodule.sh                  |  1 +\n t/t4128-apply-root.sh                      |  1 +\n t/t4212-log-corrupt.sh                     |  1 +\n t/t5515-fetch-merge-logic.sh               |  1 +\n t/t5518-fetch-exit-status.sh               |  1 +\n t/t6002-rev-list-bisect.sh                 |  1 +\n t/t6003-rev-list-topo-order.sh             |  1 +\n t/t6005-rev-list-count.sh                  |  1 +\n t/t6018-rev-list-glob.sh                   |  1 +\n t/t6100-rev-list-in-order.sh               |  1 +\n t/t9100-git-svn-basic.sh                   |  1 +\n t/t9101-git-svn-props.sh                   |  2 ++\n t/t9104-git-svn-follow-parent.sh           |  2 ++\n t/t9106-git-svn-commit-diff-clobber.sh     |  2 ++\n t/t9115-git-svn-dcommit-funky-renames.sh   |  1 +\n t/t9116-git-svn-log.sh                     |  2 ++\n t/t9122-git-svn-author.sh                  |  2 ++\n t/t9127-git-svn-partial-rebuild.sh         |  2 ++\n t/t9129-git-svn-i18n-commitencoding.sh     |  1 +\n t/t9132-git-svn-broken-symlink.sh          |  1 +\n t/t9139-git-svn-non-utf8-commitencoding.sh |  1 +\n t/t9146-git-svn-empty-dirs.sh              |  2 ++\n t/t9148-git-svn-propset.sh                 |  1 +\n t/t9151-svn-mergeinfo.sh                   |  1 +\n t/t9160-git-svn-preserve-empty-dirs.sh     |  1 +\n t/t9162-git-svn-dcommit-interactive.sh     |  2 ++\n t/t9164-git-svn-dcommit-concurrent.sh      |  2 ++\n t/t9501-gitweb-standalone-http-status.sh   |  1 +\n 36 files changed, 58 insertions(+)\n\ndiff --git a/revision.c b/revision.c\nindex 622f0faecc4..de4076e77de 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2926,6 +2926,15 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s\n \treturn left;\n }\n \n+static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)\n+{\n+\tunsigned int i;\n+\n+\tfor (i = 0; i < cmdline->nr; i++)\n+\t\tfree((char *)cmdline->rev[i].name);\n+\tfree(cmdline->rev);\n+}\n+\n static void release_revisions_mailmap(struct string_list *mailmap)\n {\n \tif (!mailmap)\n@@ -2938,6 +2947,7 @@ void release_revisions(struct rev_info *revs)\n {\n \tfree_commit_list(revs->commits);\n \tobject_array_clear(&revs->pending);\n+\trelease_revisions_cmdline(&revs->cmdline);\n \trelease_revisions_mailmap(revs->mailmap);\n }\n \ndiff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh\nindex 2fde2353fd3..ea28971e8ee 100644\n--- a/t/lib-git-svn.sh\n+++ b/t/lib-git-svn.sh\n@@ -1,3 +1,7 @@\n+if test -z \"$TEST_FAILS_SANITIZE_LEAK\"\n+then\n+\tTEST_PASSES_SANITIZE_LEAK=true\n+fi\n . ./test-lib.sh\n \n if test -n \"$NO_SVN_TESTS\"\ndiff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh\nindex 8e215867b8c..b9480c81781 100755\n--- a/t/t0062-revision-walking.sh\n+++ b/t/t0062-revision-walking.sh\n@@ -5,6 +5,7 @@\n \n test_description='Test revision walking api'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n cat >run_twice_expected <<-EOF\ndiff --git a/t/t0101-at-syntax.sh b/t/t0101-at-syntax.sh\nindex a1998b558f9..878aadd64c9 100755\n--- a/t/t0101-at-syntax.sh\n+++ b/t/t0101-at-syntax.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='various @{whatever} syntax tests'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh\nindex bc89371f534..e8a58b15897 100755\n--- a/t/t1060-object-corruption.sh\n+++ b/t/t1060-object-corruption.sh\n@@ -1,6 +1,7 @@\n #!/bin/sh\n \n test_description='see how we handle various forms of corruption'\n+\n . ./test-lib.sh\n \n # convert \"1234abcd\" to \".git/objects/12/34abcd\"\ndiff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh\nindex eac193757bf..bc9b791d1b9 100755\n--- a/t/t3303-notes-subtrees.sh\n+++ b/t/t3303-notes-subtrees.sh\n@@ -5,6 +5,7 @@ test_description='Test commit notes organized in subtrees'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n number_of_commits=100\ndiff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh\nindex 9976d787f47..64a9915761a 100755\n--- a/t/t3305-notes-fanout.sh\n+++ b/t/t3305-notes-fanout.sh\n@@ -2,6 +2,7 @@\n \n test_description='Test that adding/removing many notes triggers automatic fanout restructuring'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n path_has_fanout() {\ndiff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh\nindex cde3562e3a6..7b4607d72f2 100755\n--- a/t/t3408-rebase-multi-line.sh\n+++ b/t/t3408-rebase-multi-line.sh\n@@ -5,6 +5,7 @@ test_description='rebasing a commit with multi-line first paragraph.'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh\nindex 295da987cce..40164ae07d2 100755\n--- a/t/t4027-diff-submodule.sh\n+++ b/t/t4027-diff-submodule.sh\n@@ -2,6 +2,7 @@\n \n test_description='difference in submodules'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-diff.sh\n \ndiff --git a/t/t4128-apply-root.sh b/t/t4128-apply-root.sh\nindex f6db5a79dd9..ed94c90204e 100755\n--- a/t/t4128-apply-root.sh\n+++ b/t/t4128-apply-root.sh\n@@ -2,6 +2,7 @@\n \n test_description='apply same filename'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh\nindex 0244888a5a7..30a219894bb 100755\n--- a/t/t4212-log-corrupt.sh\n+++ b/t/t4212-log-corrupt.sh\n@@ -2,6 +2,7 @@\n \n test_description='git log with invalid commit headers'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh\nindex 320d26796d2..c100a809c5e 100755\n--- a/t/t5515-fetch-merge-logic.sh\n+++ b/t/t5515-fetch-merge-logic.sh\n@@ -14,6 +14,7 @@ export GIT_TEST_PROTOCOL_VERSION\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n build_script () {\ndiff --git a/t/t5518-fetch-exit-status.sh b/t/t5518-fetch-exit-status.sh\nindex 5c4ac2556e7..c13120088fa 100755\n--- a/t/t5518-fetch-exit-status.sh\n+++ b/t/t5518-fetch-exit-status.sh\n@@ -8,6 +8,7 @@ test_description='fetch exit status test'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh\nindex b95a0212adf..162cf50778d 100755\n--- a/t/t6002-rev-list-bisect.sh\n+++ b/t/t6002-rev-list-bisect.sh\n@@ -4,6 +4,7 @@\n #\n test_description='Tests git rev-list --bisect functionality'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-t6000.sh # t6xxx specific functions\n \ndiff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh\nindex 24d1836f417..1f7d7dd20c1 100755\n--- a/t/t6003-rev-list-topo-order.sh\n+++ b/t/t6003-rev-list-topo-order.sh\n@@ -5,6 +5,7 @@\n \n test_description='Tests git rev-list --topo-order functionality'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-t6000.sh # t6xxx specific functions\n \ndiff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh\nindex e960049f647..0729f800c3c 100755\n--- a/t/t6005-rev-list-count.sh\n+++ b/t/t6005-rev-list-count.sh\n@@ -2,6 +2,7 @@\n \n test_description='git rev-list --max-count and --skip test'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh\nindex 24b34add833..e1abc5c2b32 100755\n--- a/t/t6018-rev-list-glob.sh\n+++ b/t/t6018-rev-list-glob.sh\n@@ -5,6 +5,7 @@ test_description='rev-list/rev-parse --glob'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n commit () {\ndiff --git a/t/t6100-rev-list-in-order.sh b/t/t6100-rev-list-in-order.sh\nindex e934bc239c5..88ed7bd75a7 100755\n--- a/t/t6100-rev-list-in-order.sh\n+++ b/t/t6100-rev-list-in-order.sh\n@@ -2,6 +2,7 @@\n \n test_description='rev-list testing in-commit-order'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup a commit history with trees, blobs' '\ndiff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh\nindex fea41b3c360..7c5b847f584 100755\n--- a/t/t9100-git-svn-basic.sh\n+++ b/t/t9100-git-svn-basic.sh\n@@ -8,6 +8,7 @@ test_description='git svn basic tests'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n prepare_utf8_locale\ndiff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh\nindex 8b5681dd68a..d043e80fc34 100755\n--- a/t/t9101-git-svn-props.sh\n+++ b/t/t9101-git-svn-props.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn property tests'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n mkdir import\ndiff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh\nindex c7d8e0bf00f..5cf2ef4b8b0 100755\n--- a/t/t9104-git-svn-follow-parent.sh\n+++ b/t/t9104-git-svn-follow-parent.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn fetching'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh\nindex aec45bca3b7..3cab0b9720a 100755\n--- a/t/t9106-git-svn-commit-diff-clobber.sh\n+++ b/t/t9106-git-svn-commit-diff-clobber.sh\n@@ -2,6 +2,8 @@\n #\n # Copyright (c) 2006 Eric Wong\n test_description='git svn commit-diff clobber'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh\nindex 743fbe1fe46..419f055721d 100755\n--- a/t/t9115-git-svn-dcommit-funky-renames.sh\n+++ b/t/t9115-git-svn-dcommit-funky-renames.sh\n@@ -5,6 +5,7 @@\n \n test_description='git svn dcommit can commit renames of files with ugly names'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'load repository with strange names' '\ndiff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh\nindex 0a9f1ef366d..34f6c80dea3 100755\n--- a/t/t9116-git-svn-log.sh\n+++ b/t/t9116-git-svn-log.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn log tests'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup repository and import' '\ndiff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh\nindex 9e8fe38e7ef..527ba3d2932 100755\n--- a/t/t9122-git-svn-author.sh\n+++ b/t/t9122-git-svn-author.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='git svn authorship'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup svn repository' '\ndiff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh\nindex 2e4789d061f..90b1b30dde5 100755\n--- a/t/t9127-git-svn-partial-rebuild.sh\n+++ b/t/t9127-git-svn-partial-rebuild.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='git svn partial-rebuild tests'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize svnrepo' '\ndiff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh\nindex 01e1e8a8f76..185248a4cd7 100755\n--- a/t/t9129-git-svn-i18n-commitencoding.sh\n+++ b/t/t9129-git-svn-i18n-commitencoding.sh\n@@ -4,6 +4,7 @@\n \n test_description='git svn honors i18n.commitEncoding in config'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n compare_git_head_with () {\ndiff --git a/t/t9132-git-svn-broken-symlink.sh b/t/t9132-git-svn-broken-symlink.sh\nindex aeceffaf7b0..4d8d0584b79 100755\n--- a/t/t9132-git-svn-broken-symlink.sh\n+++ b/t/t9132-git-svn-broken-symlink.sh\n@@ -2,6 +2,7 @@\n \n test_description='test that git handles an svn repository with empty symlinks'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n test_expect_success 'load svn dumpfile' '\n \tsvnadmin load \"$rawsvnrepo\" <<EOF\ndiff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh\nindex 22d80b0be2b..b7f756b2b7f 100755\n--- a/t/t9139-git-svn-non-utf8-commitencoding.sh\n+++ b/t/t9139-git-svn-non-utf8-commitencoding.sh\n@@ -4,6 +4,7 @@\n \n test_description='git svn refuses to dcommit non-UTF8 messages'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n # ISO-2022-JP can pass for valid UTF-8, so skipping that in this test\ndiff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh\nindex 80cb55fee70..79c26ed69c1 100755\n--- a/t/t9146-git-svn-empty-dirs.sh\n+++ b/t/t9146-git-svn-empty-dirs.sh\n@@ -3,6 +3,8 @@\n # Copyright (c) 2009 Eric Wong\n \n test_description='git svn creates empty directories'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9148-git-svn-propset.sh b/t/t9148-git-svn-propset.sh\nindex aebb28995e5..6cc76a07b39 100755\n--- a/t/t9148-git-svn-propset.sh\n+++ b/t/t9148-git-svn-propset.sh\n@@ -5,6 +5,7 @@\n \n test_description='git svn propset tests'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup propset via import' '\ndiff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh\nindex c93a5beab25..85221d439bd 100755\n--- a/t/t9151-svn-mergeinfo.sh\n+++ b/t/t9151-svn-mergeinfo.sh\n@@ -5,6 +5,7 @@\n \n test_description='git-svn svn mergeinfo properties'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'load svn dump' \"\ndiff --git a/t/t9160-git-svn-preserve-empty-dirs.sh b/t/t9160-git-svn-preserve-empty-dirs.sh\nindex 36c6b1a12ff..9cf7a1427ab 100755\n--- a/t/t9160-git-svn-preserve-empty-dirs.sh\n+++ b/t/t9160-git-svn-preserve-empty-dirs.sh\n@@ -9,6 +9,7 @@ This test uses git to clone a Subversion repository that contains empty\n directories, and checks that corresponding directories are created in the\n local Git repository with placeholder files.'\n \n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n GIT_REPO=git-svn-repo\ndiff --git a/t/t9162-git-svn-dcommit-interactive.sh b/t/t9162-git-svn-dcommit-interactive.sh\nindex e38d9fa37b5..e2aa8ed88a9 100755\n--- a/t/t9162-git-svn-dcommit-interactive.sh\n+++ b/t/t9162-git-svn-dcommit-interactive.sh\n@@ -3,6 +3,8 @@\n # Copyright (c) 2011 Frédéric Heitzmann\n \n test_description='git svn dcommit --interactive series'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'initialize repo' '\ndiff --git a/t/t9164-git-svn-dcommit-concurrent.sh b/t/t9164-git-svn-dcommit-concurrent.sh\nindex 8466269bf50..1465156072e 100755\n--- a/t/t9164-git-svn-dcommit-concurrent.sh\n+++ b/t/t9164-git-svn-dcommit-concurrent.sh\n@@ -4,6 +4,8 @@\n #\n \n test_description='concurrent git svn dcommit'\n+\n+TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n \ndiff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh\nindex 32814e75df5..c900231079c 100755\n--- a/t/t9501-gitweb-standalone-http-status.sh\n+++ b/t/t9501-gitweb-standalone-http-status.sh\n@@ -13,6 +13,7 @@ code and message.'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./lib-gitweb.sh\n \n #\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453585","messageId":"patch-v6-21.27-54514a01096-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 21/27] revisions API: have release_revisions() release \"prune_data\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:50Z","receivedAt":"2022-04-13T20:02:57Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"prune_data\" in the \"struct rev_info\". This means that any code that\ncalls \"release_revisions()\" already can get rid of adjacent calls to\nclear_pathspec().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n add-interactive.c | 2 --\n builtin/add.c     | 1 -\n builtin/stash.c   | 2 --\n diff-lib.c        | 1 -\n revision.c        | 1 +\n wt-status.c       | 2 --\n 6 files changed, 1 insertion(+), 8 deletions(-)\n\ndiff --git a/add-interactive.c b/add-interactive.c\nindex 54cdfc82017..6047e8f6489 100644\n--- a/add-interactive.c\n+++ b/add-interactive.c\n@@ -568,8 +568,6 @@ static int get_modified_files(struct repository *r,\n \t\t\trun_diff_files(&rev, 0);\n \t\t}\n \n-\t\tif (ps)\n-\t\t\tclear_pathspec(&rev.prune_data);\n \t\trelease_revisions(&rev);\n \t}\n \thashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);\ndiff --git a/builtin/add.c b/builtin/add.c\nindex 115a26ea633..fc729e14c17 100644\n--- a/builtin/add.c\n+++ b/builtin/add.c\n@@ -142,7 +142,6 @@ int add_files_to_cache(const char *prefix,\n \trev.diffopt.flags.override_submodule_config = 1;\n \trev.max_count = 0; /* do not compare unmerged paths with stage #2 */\n \trun_diff_files(&rev, DIFF_RACY_IS_MODIFIED);\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n \treturn !!data.add_errors;\n }\ndiff --git a/builtin/stash.c b/builtin/stash.c\nindex fce4393d123..16aad4b4d35 100644\n--- a/builtin/stash.c\n+++ b/builtin/stash.c\n@@ -1064,7 +1064,6 @@ static int check_changes_tracked_files(const struct pathspec *ps)\n \t}\n \n done:\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n \treturn ret;\n }\n@@ -1276,7 +1275,6 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps\n \n done:\n \tdiscard_index(&istate);\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n \tstrbuf_release(&diff_output);\n \tremove_path(stash_index_path.buf);\ndiff --git a/diff-lib.c b/diff-lib.c\nindex 0f16281253f..298265e5b54 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -641,7 +641,6 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)\n \n \tif (diff_cache(&revs, tree_oid, NULL, 1))\n \t\texit(128);\n-\tclear_pathspec(&revs.prune_data);\n \trelease_revisions(&revs);\n \treturn 0;\n }\ndiff --git a/revision.c b/revision.c\nindex 1db58c3e4db..6f444eaaeba 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2949,6 +2949,7 @@ void release_revisions(struct rev_info *revs)\n \tobject_array_clear(&revs->pending);\n \trelease_revisions_cmdline(&revs->cmdline);\n \tlist_objects_filter_release(&revs->filter);\n+\tclear_pathspec(&revs->prune_data);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n }\ndiff --git a/wt-status.c b/wt-status.c\nindex a14fad1e03a..61e0c1022f5 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -616,7 +616,6 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)\n \trev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;\n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_files(&rev, 0);\n-\tclear_pathspec(&rev.prune_data);\n \trelease_revisions(&rev);\n }\n \n@@ -664,7 +663,6 @@ static void wt_status_collect_changes_index(struct wt_status *s)\n \tcopy_pathspec(&rev.prune_data, &s->pathspec);\n \trun_diff_index(&rev, 1);\n \trelease_revisions(&rev);\n-\tclear_pathspec(&rev.prune_data);\n }\n \n static int add_file_to_list(const struct object_id *oid,\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453586","messageId":"patch-v6-23.27-87ba9572b10-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 23/27] revisions API: release \"reflog_info\" in release revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:52Z","receivedAt":"2022-04-13T20:03:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a missing reflog_walk_info_release() to \"reflog-walk.c\" and use it\nin release_revisions().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n reflog-walk.c            | 24 +++++++++++++++++++++++-\n reflog-walk.h            |  1 +\n revision.c               |  1 +\n t/t0100-previous.sh      |  1 +\n t/t1401-symbolic-ref.sh  |  2 ++\n t/t1411-reflog-show.sh   |  1 +\n t/t1412-reflog-loop.sh   |  2 ++\n t/t1415-worktree-refs.sh |  1 +\n 8 files changed, 32 insertions(+), 1 deletion(-)\n\ndiff --git a/reflog-walk.c b/reflog-walk.c\nindex 8ac4b284b6b..7aa6595a51f 100644\n--- a/reflog-walk.c\n+++ b/reflog-walk.c\n@@ -8,7 +8,7 @@\n \n struct complete_reflogs {\n \tchar *ref;\n-\tconst char *short_ref;\n+\tchar *short_ref;\n \tstruct reflog_info {\n \t\tstruct object_id ooid, noid;\n \t\tchar *email;\n@@ -51,9 +51,16 @@ static void free_complete_reflog(struct complete_reflogs *array)\n \t}\n \tfree(array->items);\n \tfree(array->ref);\n+\tfree(array->short_ref);\n \tfree(array);\n }\n \n+static void complete_reflogs_clear(void *util, const char *str)\n+{\n+\tstruct complete_reflogs *array = util;\n+\tfree_complete_reflog(array);\n+}\n+\n static struct complete_reflogs *read_complete_reflog(const char *ref)\n {\n \tstruct complete_reflogs *reflogs =\n@@ -116,6 +123,21 @@ void init_reflog_walk(struct reflog_walk_info **info)\n \t(*info)->complete_reflogs.strdup_strings = 1;\n }\n \n+void reflog_walk_info_release(struct reflog_walk_info *info)\n+{\n+\tsize_t i;\n+\n+\tif (!info)\n+\t\treturn;\n+\n+\tfor (i = 0; i < info->nr; i++)\n+\t\tfree(info->logs[i]);\n+\tstring_list_clear_func(&info->complete_reflogs,\n+\t\t\t       complete_reflogs_clear);\n+\tfree(info->logs);\n+\tfree(info);\n+}\n+\n int add_reflog_for_walk(struct reflog_walk_info *info,\n \t\tstruct commit *commit, const char *name)\n {\ndiff --git a/reflog-walk.h b/reflog-walk.h\nindex e9e00ffd479..8076f10d9fb 100644\n--- a/reflog-walk.h\n+++ b/reflog-walk.h\n@@ -8,6 +8,7 @@ struct reflog_walk_info;\n struct date_mode;\n \n void init_reflog_walk(struct reflog_walk_info **info);\n+void reflog_walk_info_release(struct reflog_walk_info *info);\n int add_reflog_for_walk(struct reflog_walk_info *info,\n \t\t\tstruct commit *commit, const char *name);\n void show_reflog_message(struct reflog_walk_info *info, int,\ndiff --git a/revision.c b/revision.c\nindex 5aa6dec4532..8cd849aa2b9 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2953,6 +2953,7 @@ void release_revisions(struct rev_info *revs)\n \tclear_pathspec(&revs->prune_data);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n+\treflog_walk_info_release(revs->reflog_info);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\ndiff --git a/t/t0100-previous.sh b/t/t0100-previous.sh\nindex 69beb59f627..a16cc3d2983 100755\n--- a/t/t0100-previous.sh\n+++ b/t/t0100-previous.sh\n@@ -5,6 +5,7 @@ test_description='previous branch syntax @{-n}'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'branch -d @{-1}' '\ndiff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh\nindex 132a1b885ac..9fb0b90f252 100755\n--- a/t/t1401-symbolic-ref.sh\n+++ b/t/t1401-symbolic-ref.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='basic symbolic-ref tests'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # If the tests munging HEAD fail, they can break detection of\ndiff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh\nindex 975c4ea83a8..da581ec19ac 100755\n--- a/t/t1411-reflog-show.sh\n+++ b/t/t1411-reflog-show.sh\n@@ -4,6 +4,7 @@ test_description='Test reflog display routines'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\ndiff --git a/t/t1412-reflog-loop.sh b/t/t1412-reflog-loop.sh\nindex 977603f7f1c..ff30874f940 100755\n--- a/t/t1412-reflog-loop.sh\n+++ b/t/t1412-reflog-loop.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='reflog walk shows repeated commits again'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup commits' '\ndiff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh\nindex a3e6ea08088..3b531842dd4 100755\n--- a/t/t1415-worktree-refs.sh\n+++ b/t/t1415-worktree-refs.sh\n@@ -2,6 +2,7 @@\n \n test_description='per-worktree refs'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'setup' '\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453587","messageId":"patch-v6-22.27-69745303734-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 22/27] revisions API: clear \"boundary_commits\" in release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:51Z","receivedAt":"2022-04-13T20:03:03Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Clear the \"boundary_commits\" object_array in release_revisions(). This\nmakes a few more tests pass under SANITIZE=leak, including\n\"t/t4126-apply-empty.sh\" which started failed as an UNLEAK() in\ncmd_format_patch() was removed in a preceding commit.\n\nThis also re-marks the various tests relying on \"git format-patch\" as\npassing under \"SANITIZE=leak\", in the preceding \"revisions API users:\nuse release_revisions() in builtin/log.c\" commit those were marked as\nfailing as we removed the UNLEAK(rev) from cmd_format_patch() in\n\"builtin/log.c\".\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c                           | 1 +\n t/t4021-format-patch-numbered.sh     | 1 +\n t/t4028-format-patch-mime-headers.sh | 2 ++\n t/t4036-format-patch-signer-mime.sh  | 1 +\n t/t4122-apply-symlink-inside.sh      | 1 +\n t/t4126-apply-empty.sh               | 1 +\n t/t6110-rev-list-sparse.sh           | 1 +\n t/t9001-send-email.sh                | 1 +\n t/t9116-git-svn-log.sh               | 1 -\n 9 files changed, 9 insertions(+), 1 deletion(-)\n\ndiff --git a/revision.c b/revision.c\nindex 6f444eaaeba..5aa6dec4532 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2947,6 +2947,7 @@ void release_revisions(struct rev_info *revs)\n {\n \tfree_commit_list(revs->commits);\n \tobject_array_clear(&revs->pending);\n+\tobject_array_clear(&revs->boundary_commits);\n \trelease_revisions_cmdline(&revs->cmdline);\n \tlist_objects_filter_release(&revs->filter);\n \tclear_pathspec(&revs->prune_data);\ndiff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh\nindex 9be65fd4440..1219aa226dc 100755\n--- a/t/t4021-format-patch-numbered.sh\n+++ b/t/t4021-format-patch-numbered.sh\n@@ -5,6 +5,7 @@\n \n test_description='Format-patch numbering options'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4028-format-patch-mime-headers.sh b/t/t4028-format-patch-mime-headers.sh\nindex 204ba673cb5..60cb819c42e 100755\n--- a/t/t4028-format-patch-mime-headers.sh\n+++ b/t/t4028-format-patch-mime-headers.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='format-patch mime headers and extra headers do not conflict'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success 'create commit with utf-8 body' '\ndiff --git a/t/t4036-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh\nindex 98d9713d8b2..48655bcc789 100755\n--- a/t/t4036-format-patch-signer-mime.sh\n+++ b/t/t4036-format-patch-signer-mime.sh\n@@ -2,6 +2,7 @@\n \n test_description='format-patch -s should force MIME encoding as needed'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh\nindex aa52de401b9..96965373036 100755\n--- a/t/t4122-apply-symlink-inside.sh\n+++ b/t/t4122-apply-symlink-inside.sh\n@@ -4,6 +4,7 @@ test_description='apply to deeper directory without getting fooled with symlink'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh\nindex 66a7ba8ab8f..ece9fae207d 100755\n--- a/t/t4126-apply-empty.sh\n+++ b/t/t4126-apply-empty.sh\n@@ -2,6 +2,7 @@\n \n test_description='apply empty'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t6110-rev-list-sparse.sh b/t/t6110-rev-list-sparse.sh\nindex 13c1da53528..ddefc7f24ee 100755\n--- a/t/t6110-rev-list-sparse.sh\n+++ b/t/t6110-rev-list-sparse.sh\n@@ -4,6 +4,7 @@ test_description='operations that cull histories in unusual ways'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh\nindex 42694fe5841..01c74b8b075 100755\n--- a/t/t9001-send-email.sh\n+++ b/t/t9001-send-email.sh\n@@ -4,6 +4,7 @@ test_description='git send-email'\n GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main\n export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # May be altered later in the test\ndiff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh\nindex 34f6c80dea3..d74d7b2de68 100755\n--- a/t/t9116-git-svn-log.sh\n+++ b/t/t9116-git-svn-log.sh\n@@ -5,7 +5,6 @@\n \n test_description='git svn log tests'\n \n-TEST_FAILS_SANITIZE_LEAK=true\n . ./lib-git-svn.sh\n \n test_expect_success 'setup repository and import' '\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453588","messageId":"patch-v6-24.27-79279864c42-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-13T20:01:53Z","receivedAt":"2022-04-13T20:03:04Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Call diff_free() on the \"pruning\" member of \"struct rev_info\".  Doing\nso makes several tests pass under SANITIZE=leak.\n\nThis was also the last missing piece that allows us to remove the\nUNLEAK() in \"cmd_diff\" and \"cmd_diff_index\", which allows us to use\nthose commands as a canary for general leaks in the revisions API. See\n[1] for further rationale, and 886e1084d78 (builtin/: add UNLEAKs,\n2017-10-01) for the commit that added the UNLEAK() there.\n\n1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n builtin/diff-index.c                | 1 -\n builtin/diff.c                      | 1 -\n revision.c                          | 1 +\n t/t1001-read-tree-m-2way.sh         | 1 +\n t/t1002-read-tree-m-u-2way.sh       | 1 +\n t/t2200-add-update.sh               | 1 +\n t/t4039-diff-assume-unchanged.sh    | 1 +\n t/t4206-log-follow-harder-copies.sh | 2 ++\n t/t6131-pathspec-icase.sh           | 2 ++\n 9 files changed, 9 insertions(+), 2 deletions(-)\n\ndiff --git a/builtin/diff-index.c b/builtin/diff-index.c\nindex 3a83183c312..7d158af6b6d 100644\n--- a/builtin/diff-index.c\n+++ b/builtin/diff-index.c\n@@ -70,7 +70,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)\n \t\treturn -1;\n \t}\n \tresult = run_diff_index(&rev, option);\n-\tUNLEAK(rev);\n \tresult = diff_result_code(&rev.diffopt, result);\n \trelease_revisions(&rev);\n \treturn result;\ndiff --git a/builtin/diff.c b/builtin/diff.c\nindex dd48336da56..f539132ac68 100644\n--- a/builtin/diff.c\n+++ b/builtin/diff.c\n@@ -594,7 +594,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n \tresult = diff_result_code(&rev.diffopt, result);\n \tif (1 < rev.diffopt.skip_stat_unmatch)\n \t\trefresh_index_quietly();\n-\tUNLEAK(rev);\n \trelease_revisions(&rev);\n \tUNLEAK(ent);\n \tUNLEAK(blob);\ndiff --git a/revision.c b/revision.c\nindex 8cd849aa2b9..63f17c085c9 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2953,6 +2953,7 @@ void release_revisions(struct rev_info *revs)\n \tclear_pathspec(&revs->prune_data);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n+\tdiff_free(&revs->pruning);\n \treflog_walk_info_release(revs->reflog_info);\n }\n \ndiff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh\nindex 0710b1fb1e9..516a6112fdc 100755\n--- a/t/t1001-read-tree-m-2way.sh\n+++ b/t/t1001-read-tree-m-2way.sh\n@@ -21,6 +21,7 @@ In the test, these paths are used:\n \tyomin   - not in H or M\n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-read-tree.sh\n \ndiff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh\nindex 46cbd5514a6..bd5313caec9 100755\n--- a/t/t1002-read-tree-m-u-2way.sh\n+++ b/t/t1002-read-tree-m-u-2way.sh\n@@ -9,6 +9,7 @@ This is identical to t1001, but uses -u to update the work tree as well.\n \n '\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-read-tree.sh\n \ndiff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh\nindex 0c38f8e3569..be394f1131a 100755\n--- a/t/t2200-add-update.sh\n+++ b/t/t2200-add-update.sh\n@@ -14,6 +14,7 @@ only the updates to dir/sub.\n Also tested are \"git add -u\" without limiting, and \"git add -u\"\n without contents changes, and other conditions'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n test_expect_success setup '\ndiff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh\nindex 0eb0314a8b3..78090e6852d 100755\n--- a/t/t4039-diff-assume-unchanged.sh\n+++ b/t/t4039-diff-assume-unchanged.sh\n@@ -2,6 +2,7 @@\n \n test_description='diff with assume-unchanged entries'\n \n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n # external diff has been tested in t4020-diff-external.sh\ndiff --git a/t/t4206-log-follow-harder-copies.sh b/t/t4206-log-follow-harder-copies.sh\nindex 4871a5dc92f..33ecf82c7f9 100755\n--- a/t/t4206-log-follow-harder-copies.sh\n+++ b/t/t4206-log-follow-harder-copies.sh\n@@ -6,6 +6,8 @@\n test_description='Test --follow should always find copies hard in git log.\n \n '\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n . \"$TEST_DIRECTORY\"/lib-diff.sh\n \ndiff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh\nindex 39fc3f6769b..770cce026cb 100755\n--- a/t/t6131-pathspec-icase.sh\n+++ b/t/t6131-pathspec-icase.sh\n@@ -1,6 +1,8 @@\n #!/bin/sh\n \n test_description='test case insensitive pathspec limiting'\n+\n+TEST_PASSES_SANITIZE_LEAK=true\n . ./test-lib.sh\n \n if test_have_prereq CASE_INSENSITIVE_FS\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453627","messageId":"patch-v6-25.27-4aadeabaae7-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 25/27] revisions API: have release_revisions() release \"date_mode\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-14T05:56:38Z","receivedAt":"2022-04-14T05:56:56Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Extend the the release_revisions() function so that it frees the\n\"date_mode\" in the \"struct ref_info\".\n\nThis uses the date_mode_release() function added in 974c919d36d (date\nAPI: add and use a date_mode_release(), 2022-02-16). As that commit\nnotes \"t7004-tag.sh\" tests for the leaks that are being fixed\nhere. That test now fails \"only\" 44 tests, instead of the 46 it failed\nbefore this change.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 1 +\n 1 file changed, 1 insertion(+)\n\ndiff --git a/revision.c b/revision.c\nindex 63f17c085c9..307f41e8891 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2951,6 +2951,7 @@ void release_revisions(struct rev_info *revs)\n \trelease_revisions_cmdline(&revs->cmdline);\n \tlist_objects_filter_release(&revs->filter);\n \tclear_pathspec(&revs->prune_data);\n+\tdate_mode_release(&revs->date_mode);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n \tdiff_free(&revs->pruning);\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453628","messageId":"patch-v6-26.27-f0cedadbb94-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 26/27] revisions API: have release_revisions() release \"topo_walk_info\"","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-14T05:56:39Z","receivedAt":"2022-04-14T05:57:01Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Refactor the existing reset_topo_walk() into a thin wrapper for a\nrelease_revisions_topo_walk_info() + resetting the member to \"NULL\",\nand call release_revisions_topo_walk_info() from release_revisions().\n\nThis fixes memory leaks that have been with us ever since\n\"topo_walk_info\" was added to revision.[ch] in\nf0d9cc4196a (revision.c: begin refactoring --topo-order logic,\n2018-11-01).\n\nDue to various other leaks this makes no tests pass in their entirety,\nbut e.g. before this running this on git.git:\n\n    ./git -P log --pretty=tformat:\"%P   %H | %s\" --parents --full-history --topo-order -3 -- README.md\n\nWould report under SANITIZE=leak:\n\n    SUMMARY: LeakSanitizer: 531064 byte(s) leaked in 6 allocation(s).\n\nNow we'll free all of that memory.\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n revision.c | 16 ++++++++++++----\n 1 file changed, 12 insertions(+), 4 deletions(-)\n\ndiff --git a/revision.c b/revision.c\nindex 307f41e8891..0107ac1077a 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2943,6 +2943,8 @@ static void release_revisions_mailmap(struct string_list *mailmap)\n \tfree(mailmap);\n }\n \n+static void release_revisions_topo_walk_info(struct topo_walk_info *info);\n+\n void release_revisions(struct rev_info *revs)\n {\n \tfree_commit_list(revs->commits);\n@@ -2956,6 +2958,7 @@ void release_revisions(struct rev_info *revs)\n \tfree_grep_patterns(&revs->grep_filter);\n \tdiff_free(&revs->pruning);\n \treflog_walk_info_release(revs->reflog_info);\n+\trelease_revisions_topo_walk_info(revs->topo_walk_info);\n }\n \n static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)\n@@ -3468,17 +3471,22 @@ static void compute_indegrees_to_depth(struct rev_info *revs,\n \t\tindegree_walk_step(revs);\n }\n \n-static void reset_topo_walk(struct rev_info *revs)\n+static void release_revisions_topo_walk_info(struct topo_walk_info *info)\n {\n-\tstruct topo_walk_info *info = revs->topo_walk_info;\n-\n+\tif (!info)\n+\t\treturn;\n \tclear_prio_queue(&info->explore_queue);\n \tclear_prio_queue(&info->indegree_queue);\n \tclear_prio_queue(&info->topo_queue);\n \tclear_indegree_slab(&info->indegree);\n \tclear_author_date_slab(&info->author_date);\n+\tfree(info);\n+}\n \n-\tFREE_AND_NULL(revs->topo_walk_info);\n+static void reset_topo_walk(struct rev_info *revs)\n+{\n+\trelease_revisions_topo_walk_info(revs->topo_walk_info);\n+\trevs->topo_walk_info = NULL;\n }\n \n static void init_topo_walk(struct rev_info *revs)\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453629","messageId":"patch-v6-27.27-3be672a8150-20220413T195935Z-avarab@gmail.com","threadId":"57525","inReplyTo":"cover-v6-00.27-00000000000-20220413T195935Z-avarab@gmail.com","subject":"[PATCH v6 27/27] revisions API: add a TODO for diff_free(&revs->diffopt)","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-04-14T05:56:40Z","receivedAt":"2022-04-14T05:57:06Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"Add a TODO comment indicating that we should release \"diffopt\" in\nrelease_revisions(). In a preceding commit we started releasing the\n\"pruning\" member of the same type, but handling \"diffopt\" will require\nus to untangle the \"no_free\" conditions I added in e900d494dcf (diff:\nadd an API for deferred freeing, 2021-02-11).\n\nLet's leave a TODO comment to that effect, and so that we don't forget\nrefactor code that was changed to use release_revisions() in earlier\ncommits to stop using the \"diffopt\" member after a call to\nrelease_revisions(). This works currently, but would become a logic\nerror as soon as we started freeing \"diffopt\". Doing that change now\ndoesn't harm anything, and future-proofs us against a later change to\nrelease_revisions().\n\nSigned-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n---\n diff-lib.c  | 4 +++-\n revision.c  | 1 +\n wt-status.c | 6 ++++--\n 3 files changed, 8 insertions(+), 3 deletions(-)\n\ndiff --git a/diff-lib.c b/diff-lib.c\nindex 298265e5b54..7eb66a417aa 100644\n--- a/diff-lib.c\n+++ b/diff-lib.c\n@@ -651,6 +651,7 @@ int index_differs_from(struct repository *r,\n {\n \tstruct rev_info rev;\n \tstruct setup_revision_opt opt;\n+\tunsigned has_changes;\n \n \trepo_init_revisions(r, &rev, NULL);\n \tmemset(&opt, 0, sizeof(opt));\n@@ -662,8 +663,9 @@ int index_differs_from(struct repository *r,\n \t\tdiff_flags_or(&rev.diffopt.flags, flags);\n \trev.diffopt.ita_invisible_in_index = ita_invisible_in_index;\n \trun_diff_index(&rev, 1);\n+\thas_changes = rev.diffopt.flags.has_changes;\n \trelease_revisions(&rev);\n-\treturn (rev.diffopt.flags.has_changes != 0);\n+\treturn (has_changes != 0);\n }\n \n static struct strbuf *idiff_prefix_cb(struct diff_options *opt, void *data)\ndiff --git a/revision.c b/revision.c\nindex 0107ac1077a..58d6212221b 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -2956,6 +2956,7 @@ void release_revisions(struct rev_info *revs)\n \tdate_mode_release(&revs->date_mode);\n \trelease_revisions_mailmap(revs->mailmap);\n \tfree_grep_patterns(&revs->grep_filter);\n+\t/* TODO (need to handle \"no_free\"): diff_free(&revs->diffopt) */\n \tdiff_free(&revs->pruning);\n \treflog_walk_info_release(revs->reflog_info);\n \trelease_revisions_topo_walk_info(revs->topo_walk_info);\ndiff --git a/wt-status.c b/wt-status.c\nindex 61e0c1022f5..102d904adcb 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -2545,8 +2545,9 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)\n \trev_info.diffopt.flags.quick = 1;\n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_files(&rev_info, 0);\n+\tresult = diff_result_code(&rev_info.diffopt, result);\n \trelease_revisions(&rev_info);\n-\treturn diff_result_code(&rev_info.diffopt, result);\n+\treturn result;\n }\n \n /**\n@@ -2578,8 +2579,9 @@ int has_uncommitted_changes(struct repository *r,\n \n \tdiff_setup_done(&rev_info.diffopt);\n \tresult = run_diff_index(&rev_info, 1);\n+\tresult = diff_result_code(&rev_info.diffopt, result);\n \trelease_revisions(&rev_info);\n-\treturn diff_result_code(&rev_info.diffopt, result);\n+\treturn result;\n }\n \n /**\n-- \n2.36.0.rc2.843.g193535c2aa7\n\n"},{"id":"453706","messageId":"2cc2d026-1496-1ed9-838b-6fdf8cfba285@gmail.com","threadId":"57525","inReplyTo":"patch-v6-14.27-31ce58da43d-20220413T195935Z-avarab@gmail.com","subject":"Re: [PATCH v6 14/27] revisions API users: use release_revisions() with UNLEAK()","fromName":"Phillip Wood","fromEmail":"phillip.wood123@gmail.com","sentAt":"2022-04-15T13:58:37Z","receivedAt":"2022-04-15T13:58:46Z","isPatch":true,"sender":{"key":"phillip.wood@dunelm.org.uk","avatar":null},"body":"Hi Ævar\n\nOn 13/04/2022 21:01, Ævar Arnfjörð Bjarmason wrote:\n> Use a release_revisions() with those \"struct rev_list\" users which\n> already \"UNLEAK\" the struct. It may seem odd to simultaneously attempt\n> to free() memory, but also to explicitly ignore whether we have memory\n> leaks in the same.\n> \n> As explained in preceding commits this is being done to use the\n> built-in commands as a guinea pig for whether the release_revisions()\n> function works as expected, we'd like to test e.g. whether we segfault\n> as we change it. In subsequent commits we'll then remove these\n> UNLEAK() as the function is made to free the memory that caused us to\n> add them in the first place.\n\nIt would have been nice to reword this to avoid the confusion that \nprompted my comments on the previous version. Saying \"As explained in \nthe preceding commits\" is confusing when those commits remove UNLEAK() \nand this commit leaves it in. I now understand why you are leaving \nUNLEAK() here thanks to your explanation on the previous series and I \nthink it makes sense to do so but I don't think this message does a good \njob of explaining it.\n\nThe rest of the series looks good\n\nBest Wishes\n\nPhillip\n\n> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>\n> ---\n>   builtin/diff-index.c | 4 +++-\n>   builtin/diff.c       | 1 +\n>   2 files changed, 4 insertions(+), 1 deletion(-)\n> \n> diff --git a/builtin/diff-index.c b/builtin/diff-index.c\n> index 5fd23ab5b6c..3a83183c312 100644\n> --- a/builtin/diff-index.c\n> +++ b/builtin/diff-index.c\n> @@ -71,5 +71,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)\n>   \t}\n>   \tresult = run_diff_index(&rev, option);\n>   \tUNLEAK(rev);\n> -\treturn diff_result_code(&rev.diffopt, result);\n> +\tresult = diff_result_code(&rev.diffopt, result);\n> +\trelease_revisions(&rev);\n> +\treturn result;\n>   }\n> diff --git a/builtin/diff.c b/builtin/diff.c\n> index bb7fafca618..dd48336da56 100644\n> --- a/builtin/diff.c\n> +++ b/builtin/diff.c\n> @@ -595,6 +595,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)\n>   \tif (1 < rev.diffopt.skip_stat_unmatch)\n>   \t\trefresh_index_quietly();\n>   \tUNLEAK(rev);\n> +\trelease_revisions(&rev);\n>   \tUNLEAK(ent);\n>   \tUNLEAK(blob);\n>   \treturn result;\n"},{"id":"458808","messageId":"YsxmrRCSpze1csHz@coredump.intra.peff.net","threadId":"57525","inReplyTo":"patch-v6-11.27-e93791b6242-20220413T195935Z-avarab@gmail.com","subject":"Re: [PATCH v6 11/27] revisions API users: add \"goto cleanup\" for release_revisions()","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2022-07-11T18:06:37Z","receivedAt":"2022-07-11T18:06:42Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Wed, Apr 13, 2022 at 10:01:40PM +0200, Ævar Arnfjörð Bjarmason wrote:\n\n> diff --git a/builtin/diff-files.c b/builtin/diff-files.c\n> index 70103c40952..2bfaf9ba7ae 100644\n> --- a/builtin/diff-files.c\n> +++ b/builtin/diff-files.c\n> @@ -77,8 +77,12 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)\n>  \n>  \tif (read_cache_preload(&rev.diffopt.pathspec) < 0) {\n>  \t\tperror(\"read_cache_preload\");\n> -\t\treturn -1;\n> +\t\tresult = -1;\n> +\t\tgoto cleanup;\n>  \t}\n> +cleanup:\n>  \tresult = run_diff_files(&rev, options);\n> -\treturn diff_result_code(&rev.diffopt, result);\n> +\tresult = diff_result_code(&rev.diffopt, result);\n> +\trelease_revisions(&rev);\n> +\treturn result;\n>  }\n\nA bit late, but I happened to notice Coverity complaining about this\ncode. And indeed, this patch seems pretty broken. If\nread_cache_preload() fails, we assign \"-1\" to result and jump to\ncleanup.\n\nBut then the first thing we do in cleanup is overwrite result! That\nhides the error (depending on how run_diff_files behaves if the cache\nload failed, but one can imagine it thinks there are no files to diff).\n\nShould the cleanup label come after the call to run_diff_files()?\n\nI was also somewhat confused by the double-assignment of \"result\" in the\ncleanup label. But I think that is because diff_result_code() is\nmassaging the current value of \"result\" into the right thing. But in\nthat case, should the \"-1\" from earlier be passed to diff_result_code()?\nI think probably not (and certainly it was not before your patch). Which\nwould imply that the label should go after that, like:\n\ndiff --git a/builtin/diff-files.c b/builtin/diff-files.c\nindex 2bfaf9ba7a..92cf6e1e92 100644\n--- a/builtin/diff-files.c\n+++ b/builtin/diff-files.c\n@@ -80,9 +80,9 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)\n \t\tresult = -1;\n \t\tgoto cleanup;\n \t}\n-cleanup:\n \tresult = run_diff_files(&rev, options);\n \tresult = diff_result_code(&rev.diffopt, result);\n+cleanup:\n \trelease_revisions(&rev);\n \treturn result;\n }\n\n-Peff\n"},{"id":"458816","messageId":"220711.86tu7n5ryf.gmgdl@evledraar.gmail.com","threadId":"57525","inReplyTo":"YsxmrRCSpze1csHz@coredump.intra.peff.net","subject":"Re: [PATCH v6 11/27] revisions API users: add \"goto cleanup\" for release_revisions()","fromName":"Ævar Arnfjörð Bjarmason","fromEmail":"avarab@gmail.com","sentAt":"2022-07-11T20:12:18Z","receivedAt":"2022-07-11T20:12:48Z","isPatch":true,"sender":{"key":"avarab@gmail.com","avatar":"https://avatars.githubusercontent.com/u/45301?v=4"},"body":"\nOn Mon, Jul 11 2022, Jeff King wrote:\n\n> On Wed, Apr 13, 2022 at 10:01:40PM +0200, Ævar Arnfjörð Bjarmason wrote:\n>\n>> diff --git a/builtin/diff-files.c b/builtin/diff-files.c\n>> index 70103c40952..2bfaf9ba7ae 100644\n>> --- a/builtin/diff-files.c\n>> +++ b/builtin/diff-files.c\n>> @@ -77,8 +77,12 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)\n>>  \n>>  \tif (read_cache_preload(&rev.diffopt.pathspec) < 0) {\n>>  \t\tperror(\"read_cache_preload\");\n>> -\t\treturn -1;\n>> +\t\tresult = -1;\n>> +\t\tgoto cleanup;\n>>  \t}\n>> +cleanup:\n>>  \tresult = run_diff_files(&rev, options);\n>> -\treturn diff_result_code(&rev.diffopt, result);\n>> +\tresult = diff_result_code(&rev.diffopt, result);\n>> +\trelease_revisions(&rev);\n>> +\treturn result;\n>>  }\n>\n> A bit late, but I happened to notice Coverity complaining about this\n> code. And indeed, this patch seems pretty broken. If\n> read_cache_preload() fails, we assign \"-1\" to result and jump to\n> cleanup.\n>\n> But then the first thing we do in cleanup is overwrite result! That\n> hides the error (depending on how run_diff_files behaves if the cache\n> load failed, but one can imagine it thinks there are no files to diff).\n>\n> Should the cleanup label come after the call to run_diff_files()?\n>\n> I was also somewhat confused by the double-assignment of \"result\" in the\n> cleanup label. But I think that is because diff_result_code() is\n> massaging the current value of \"result\" into the right thing. But in\n> that case, should the \"-1\" from earlier be passed to diff_result_code()?\n> I think probably not (and certainly it was not before your patch). Which\n> would imply that the label should go after that, like:\n>\n> diff --git a/builtin/diff-files.c b/builtin/diff-files.c\n> index 2bfaf9ba7a..92cf6e1e92 100644\n> --- a/builtin/diff-files.c\n> +++ b/builtin/diff-files.c\n> @@ -80,9 +80,9 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)\n>  \t\tresult = -1;\n>  \t\tgoto cleanup;\n>  \t}\n> -cleanup:\n>  \tresult = run_diff_files(&rev, options);\n>  \tresult = diff_result_code(&rev.diffopt, result);\n> +cleanup:\n>  \trelease_revisions(&rev);\n>  \treturn result;\n>  }\n>\n> -Peff\n\nUrgh, yes that's the obviously correct fix to bring it back to the\nprevious behavior, it's indeed just a misplaced \"cleanup\" label, sorry\nabout that.\n"},{"id":"458851","messageId":"Ys0at7pMPUrrmEaB@coredump.intra.peff.net","threadId":"57525","inReplyTo":"220711.86tu7n5ryf.gmgdl@evledraar.gmail.com","subject":"Re: [PATCH v6 11/27] revisions API users: add \"goto cleanup\" for release_revisions()","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2022-07-12T06:54:47Z","receivedAt":"2022-07-12T06:54:55Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Jul 11, 2022 at 10:12:18PM +0200, Ævar Arnfjörð Bjarmason wrote:\n\n> > I was also somewhat confused by the double-assignment of \"result\" in the\n> > cleanup label. But I think that is because diff_result_code() is\n> > massaging the current value of \"result\" into the right thing. But in\n> > that case, should the \"-1\" from earlier be passed to diff_result_code()?\n> > I think probably not (and certainly it was not before your patch). Which\n> > would imply that the label should go after that, like:\n> [...]\n> \n> Urgh, yes that's the obviously correct fix to bring it back to the\n> previous behavior, it's indeed just a misplaced \"cleanup\" label, sorry\n> about that.\n\nThanks, I'll work it up into a real patch. Which I should have just done\nin the first place; I just hadn't convinced myself I understood\neverything until I finished writing the other email. :)\n\n-Peff\n"},{"id":"458856","messageId":"Ys0c0ePxPOqZ/5ck@coredump.intra.peff.net","threadId":"57525","inReplyTo":"Ys0at7pMPUrrmEaB@coredump.intra.peff.net","subject":"[PATCH] diff-files: move misplaced cleanup label","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2022-07-12T07:03:45Z","receivedAt":"2022-07-12T07:04:14Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"\nCommit 0139c58ab9 (revisions API users: add \"goto cleanup\" for\nrelease_revisions(), 2022-04-13) converted an early return in\ncmd_diff_files() into a goto. But it put the cleanup label too early: if\nread_cache_preload() returns an error, we'll set result to \"-1\", but\nthen jump to calling run_diff_files(), overwriting our result.\n\nWe should jump past the call to run_diff_files(). Likewise, we should go\npast diff_result_code(), which is expecting to see a code from an actual\ndiff, not a negative error code.\n\nIn practice, I suspect this bug cannot actually be triggered, because\nread_cache_preload() does not seem to ever return an error. Its return\nvalue (eventually) comes from do_read_index(), which gives the number of\ncache entries found, and calls die() on error. Still, it makes sense to\nfix the inadvertent change from 0139c58ab9 first, and we can look into\nthe overall error handling of read_cache() separately (which is present\nin many other callsites).\n\nSigned-off-by: Jeff King <peff@peff.net>\n---\nThis is on top of ab/plug-leak-in-revisions, though it's long since\ngraduated to master.\n\nThe read_cache() thing is weird. The error checking was introduced back\nin 6304c29d51 (diff-files: do not play --no-index games, 2008-05-23).\nAnd even back then, I don't see it ever returning -1. Yet then, as well\nas now, running \"git grep 'read_cache.*< 0'\" turns up many hits. So I'm\nnot sure if I'm missing a case, or if it's cargo-culting gone wild.\n\nStill, I don't plan to dig further into it anytime soon. Removing the\nerror handling from those sites is relatively low reward, with high risk\nfor introducing a bug.\n\n builtin/diff-files.c | 2 +-\n 1 file changed, 1 insertion(+), 1 deletion(-)\n\ndiff --git a/builtin/diff-files.c b/builtin/diff-files.c\nindex 2bfaf9ba7a..92cf6e1e92 100644\n--- a/builtin/diff-files.c\n+++ b/builtin/diff-files.c\n@@ -80,9 +80,9 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)\n \t\tresult = -1;\n \t\tgoto cleanup;\n \t}\n-cleanup:\n \tresult = run_diff_files(&rev, options);\n \tresult = diff_result_code(&rev.diffopt, result);\n+cleanup:\n \trelease_revisions(&rev);\n \treturn result;\n }\n-- \n2.37.0.497.g676cb53800\n\n"}]}