git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [PATCH 00/24] revision.[ch]: add and use release_revisions()
@ 2022-03-09 13:16 Ævar Arnfjörð Bjarmason
  2022-03-09 13:16 ` [PATCH 01/24] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
                   ` (25 more replies)
  0 siblings, 26 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

== For Junio ==

This has a trivially resolved conflict with Derrick Stolee's
aaf251cdc5c (revision: put object filter into struct rev_info,
2022-03-08) currently in "seen" in builtin/rev-list.c.

The resolution is to just keep the "goto cleanup" in place of "return
0" in the conflicting lines, but to otherwise keep Derrick's version.

It will pass with/without SANITIZE=leak when applied to both "master"
and "seen". I omitted one test change (described in a relevant commit
message) due to the latter not being true (no fault of "seen", just a
new leaking command being added to a test).

== Summary ==

Ever since we started running regular leak testing in CI with
956d2e4639b (tests: add a test mode for SANITIZE=leak, run it in CI,
2021-09-23) a major roadblock for making headway in marking tests as
leak-free has been the leaks in the revision.[ch] API.

Since we use it for an revision walking, including "log", "rev-list"
etc. we haven't been able to run common test setup in many cases
without dying when compiled with SANITIZE=leak, and cases where we
genuinely leak memory are often obscured by several concurrent leaks
in the revisions API.

This series adds a release_revisions() and works towards fixing those
memory leaks one at a time. As the diffstat to t/* shows we can mark a
*lot* of tests leak-free as a result, and those are only the the tests
that 100% pass with this series.

An approximate count of the total number of memory leaks in the test
suite (approximate because some are "hidden" due to SANITIZE=leak
causing "git" to die) is, before this series (running with "--tee" and
"--verbose-log"):

    $ grep -a "SUMMARY: LeakSanitizer.*leaked in .*allocation" test-results/*.out|wc -l
    12032

After:

    $ grep -a "SUMMARY: LeakSanitizer.*leaked in .*allocation" test-results/*.out|wc -l
    6652

I.e. this recudes the number of times "git" dies due to having any of
N number of leaks by around half, and that's severely undercounting
the amount of individual memory leaks.

This is a rather large series, but it should be fairly easy to
review. I've leaned towards splitting commits up for ease of review,
such that when there's repetitive changes (e.g. additions of
release_revisions()) that they're split such that each commit has
hunks that make the same sort of change, whenever possible.

All of the changes to "t/*" are then just adding
"TEST_PASSES_SANITIZE_LEAK=true" lines as we go along.

This doesn't fix all of the leaks in the revision.[ch] API. In
particular there's harder edge cases where the ownership of a struct
member isn't very straightforward (sometimes revision.c will allocate
it, sometimes not). This does fix all of the common cases though, and
what's left is best suited for a follow-up series.

Ævar Arnfjörð Bjarmason (24):
  t/helper/test-fast-rebase.c: don't leak "struct strbuf"
  blame: use "goto cleanup" for cleanup_scoreboard()
  format-patch: don't leak "extra_headers" or "ref_message_ids"
  revision.[ch]: split freeing of revs->commit into a function
  revision.[ch]: provide and start using a release_revisions()
  revisions API users: add straightforward release_revisions()
  revisions API users: use release_revisions() needing "{ 0 }" init
  revisions API users: add "goto cleanup" for release_revisions()
  revisions API users: use release_revisions() in http-push.c
  revisions API users: use release_revisions() in builtin/log.c
  revisions API users: use release_revisions() with UNLEAK()
  revisions API users: use release_revisions() in submodule.c edge case
  revisions API users: use release_revisions() in wt-status.c edge case
  revisions API users: use release_revisions() for "prune_data" users
  revisions API: have release_revisions() release "commits"
  revisions API: have release_revisions() release "mailmap"
  revisions API: have release_revisions() release "cmdline"
  revisions API: have release_revisions() release "grep_filter"
  revisions API: have release_revisions() release "prune_data"
  revisions API: clear "boundary_commits" in release_revisions()
  revisions API: release "reflog_info" in release revisions()
  revisions API: call diff_free(&revs->pruning) in revisions_release()
  revisions API: have release_revisions() release "date_mode"
  revisions API: have release_revisions() release "topo_walk_info"

 add-interactive.c                             |  3 +-
 bisect.c                                      | 20 ++++--
 builtin/add.c                                 |  3 +-
 builtin/am.c                                  |  3 +
 builtin/bisect--helper.c                      |  2 +
 builtin/blame.c                               |  4 +-
 builtin/checkout.c                            |  3 +-
 builtin/commit.c                              |  7 +-
 builtin/describe.c                            |  2 +
 builtin/diff-files.c                          |  8 ++-
 builtin/diff-index.c                          |  5 +-
 builtin/diff.c                                |  2 +-
 builtin/fast-export.c                         |  1 +
 builtin/log.c                                 | 35 +++++++---
 builtin/merge.c                               |  2 +
 builtin/pack-objects.c                        |  2 +
 builtin/prune.c                               |  1 +
 builtin/reflog.c                              |  1 +
 builtin/rev-list.c                            | 19 +++--
 builtin/shortlog.c                            |  2 +
 builtin/stash.c                               | 20 +++---
 builtin/submodule--helper.c                   | 15 ++--
 bundle.c                                      | 12 ++--
 diff-lib.c                                    |  8 ++-
 fmt-merge-msg.c                               |  1 +
 http-push.c                                   |  3 +-
 merge-ort.c                                   |  1 +
 merge-recursive.c                             |  1 +
 midx.c                                        |  1 +
 pack-bitmap-write.c                           |  1 +
 range-diff.c                                  |  2 +-
 ref-filter.c                                  |  1 +
 reflog-walk.c                                 | 26 ++++++-
 reflog-walk.h                                 |  1 +
 remote.c                                      |  1 +
 revision.c                                    | 69 ++++++++++++++++---
 revision.h                                    |  6 ++
 sequencer.c                                   | 26 +++++--
 shallow.c                                     |  1 +
 submodule.c                                   |  5 +-
 t/helper/test-fast-rebase.c                   | 22 ++++--
 t/helper/test-revision-walking.c              |  1 +
 t/lib-git-svn.sh                              |  4 ++
 t/t0056-git-C.sh                              |  1 +
 t/t0062-revision-walking.sh                   |  1 +
 t/t0100-previous.sh                           |  1 +
 t/t0101-at-syntax.sh                          |  2 +
 t/t1060-object-corruption.sh                  |  2 +
 t/t1401-symbolic-ref.sh                       |  2 +
 t/t1411-reflog-show.sh                        |  1 +
 t/t1412-reflog-loop.sh                        |  2 +
 t/t1415-worktree-refs.sh                      |  1 +
 t/t2015-checkout-unborn.sh                    |  1 +
 t/t2016-checkout-patch.sh                     |  1 +
 t/t3408-rebase-multi-line.sh                  |  1 +
 t/t4021-format-patch-numbered.sh              |  1 +
 t/t4028-format-patch-mime-headers.sh          |  2 +
 t/t4036-format-patch-signer-mime.sh           |  1 +
 t/t4039-diff-assume-unchanged.sh              |  1 +
 t/t4055-diff-context.sh                       |  1 +
 t/t4066-diff-emit-delay.sh                    |  1 +
 t/t4122-apply-symlink-inside.sh               |  1 +
 t/t4126-apply-empty.sh                        |  1 -
 t/t4206-log-follow-harder-copies.sh           |  2 +
 t/t4207-log-decoration-colors.sh              |  1 +
 t/t4212-log-corrupt.sh                        |  1 +
 t/t5313-pack-bounds-checks.sh                 |  2 +
 t/t5322-pack-objects-sparse.sh                |  1 +
 t/t5506-remote-groups.sh                      |  1 +
 t/t5513-fetch-track.sh                        |  1 +
 t/t5515-fetch-merge-logic.sh                  |  1 +
 t/t5518-fetch-exit-status.sh                  |  1 +
 t/t5532-fetch-proxy.sh                        |  2 +
 t/t5900-repo-selection.sh                     |  2 +
 t/t6002-rev-list-bisect.sh                    |  1 +
 t/t6003-rev-list-topo-order.sh                |  1 +
 t/t6018-rev-list-glob.sh                      |  1 +
 t/t6019-rev-list-ancestry-path.sh             |  1 +
 t/t6100-rev-list-in-order.sh                  |  1 +
 t/t6101-rev-parse-parents.sh                  |  1 +
 t/t6110-rev-list-sparse.sh                    |  1 +
 t/t6114-keep-packs.sh                         |  2 +
 t/t6131-pathspec-icase.sh                     |  2 +
 t/t7008-filter-branch-null-sha1.sh            |  2 +
 t/t7702-repack-cyclic-alternate.sh            |  2 +
 t/t9001-send-email.sh                         |  1 +
 t/t9100-git-svn-basic.sh                      |  1 +
 t/t9101-git-svn-props.sh                      |  2 +
 t/t9104-git-svn-follow-parent.sh              |  2 +
 t/t9106-git-svn-commit-diff-clobber.sh        |  2 +
 t/t9122-git-svn-author.sh                     |  2 +
 t/t9129-git-svn-i18n-commitencoding.sh        |  1 +
 t/t9139-git-svn-non-utf8-commitencoding.sh    |  1 +
 t/t9148-git-svn-propset.sh                    |  1 +
 t/t9151-svn-mergeinfo.sh                      |  1 +
 t/t9157-git-svn-fetch-merge.sh                |  2 +
 t/t9159-git-svn-no-parent-mergeinfo.sh        |  2 +
 t/t9161-git-svn-mergeinfo-push.sh             |  1 +
 t/t9162-git-svn-dcommit-interactive.sh        |  2 +
 t/t9163-git-svn-reset-clears-caches.sh        |  1 +
 ...65-git-svn-fetch-merge-branch-of-branch.sh |  2 +
 ...6-git-svn-fetch-merge-branch-of-branch2.sh |  2 +
 wt-status.c                                   | 15 ++--
 103 files changed, 359 insertions(+), 90 deletions(-)

-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply	[flat|nested] 252+ messages in thread

* [PATCH 01/24] t/helper/test-fast-rebase.c: don't leak "struct strbuf"
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 13:16 ` [PATCH 02/24] blame: use "goto cleanup" for cleanup_scoreboard() Ævar Arnfjörð Bjarmason
                   ` (24 subsequent siblings)
  25 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

Fix a memory leak that's been with us since f9500261e0a (fast-rebase:
write conflict state to working tree, index, and HEAD, 2021-05-20)
changed this code to move these strbuf_release() into an if/else
block.

We'll also add to "reflog_msg" in the "else" arm of the "if" block
being modified here, and we'll append to "branch_msg" in both
cases. But after f9500261e0a only the "if" block would free these two
"struct strbuf".

This change is needed so that a subsequent addition of a "goto
cleanup" pattern when adding release_revisions() doesn't solve this
unrelated bug while it's at it.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 t/helper/test-fast-rebase.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c
index fc2d4609043..993b90eaedd 100644
--- a/t/helper/test-fast-rebase.c
+++ b/t/helper/test-fast-rebase.c
@@ -201,8 +201,6 @@ int cmd__fast_rebase(int argc, const char **argv)
 		}
 		if (create_symref("HEAD", branch_name.buf, reflog_msg.buf) < 0)
 			die(_("unable to update HEAD"));
-		strbuf_release(&reflog_msg);
-		strbuf_release(&branch_name);
 
 		prime_cache_tree(the_repository, the_repository->index,
 				 result.tree);
@@ -221,5 +219,8 @@ int cmd__fast_rebase(int argc, const char **argv)
 	if (write_locked_index(&the_index, &lock,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("unable to write %s"), get_index_file());
+
+	strbuf_release(&reflog_msg);
+	strbuf_release(&branch_name);
 	return (result.clean == 0);
 }
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 02/24] blame: use "goto cleanup" for cleanup_scoreboard()
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
  2022-03-09 13:16 ` [PATCH 01/24] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 13:16 ` [PATCH 03/24] format-patch: don't leak "extra_headers" or "ref_message_ids" Ævar Arnfjörð Bjarmason
                   ` (23 subsequent siblings)
  25 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

Amend a freeing pattern added in 0906ac2b54b (blame: use changed-path
Bloom filters, 2020-04-16) to use a "goto cleanup", so that we can be
sure that we call cleanup_scoreboard().

This change is needed so that a subsequent addition of a
release_revisions() doesn't solve this unrelated issue while it's at
it.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/blame.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/builtin/blame.c b/builtin/blame.c
index 8d15b68afc9..885b381ab83 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1167,7 +1167,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 	if (!incremental)
 		setup_pager();
 	else
-		return 0;
+		goto cleanup;
 
 	blame_sort_final(&sb);
 
@@ -1201,6 +1201,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 		printf("num commits: %d\n", sb.num_commits);
 	}
 
+cleanup:
 	cleanup_scoreboard(&sb);
 	return 0;
 }
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 03/24] format-patch: don't leak "extra_headers" or "ref_message_ids"
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
  2022-03-09 13:16 ` [PATCH 01/24] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
  2022-03-09 13:16 ` [PATCH 02/24] blame: use "goto cleanup" for cleanup_scoreboard() Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 20:34   ` Taylor Blau
  2022-03-09 13:16 ` [PATCH 04/24] revision.[ch]: split freeing of revs->commit into a function Ævar Arnfjörð Bjarmason
                   ` (22 subsequent siblings)
  25 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

Fix two memory leaks in "struct rev_info" by freeing that memory in
cmd_format_patch(). These two are unusual special-cases in being in
the "struct rev_info", but not being "owned" by the code in
revision.c. I.e. they're members of the struct so that this code in
"builtin/log.c" can pass information code in log-tree.c.

See 20ff06805c6 (format-patch: resurrect extra headers from config,
2006-06-02) and d1566f7883f (git-format-patch: Make the second and
subsequent mails replies to the first, 2006-07-14) for the initial
introduction of "extra_headers" and "ref_message_ids".

We can count on repo_init_revisions() memset()-ing this data to 0
however, so we can count on it being either NULL or something we
allocated. In the case of "extra_headers" let's add a local "char *"
variable to hold it, to avoid the eventual cast from "const char *"
when we free() it.

While we're at it let's also move to using string_list_init_nodup()
instead of relying on calloc() implicitly coming up with the same
result. See 770fedaf9fb (string-list.[ch]: add a
string_list_init_{nodup,dup}(), 2021-07-01) for the introduction of
the string_list_init_nodup() helper. The string_list_clear() here is
redundant due to the *_nodup() initialization, but let's add it anyway
for consistency with other API use.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/log.c | 15 ++++++++++++---
 1 file changed, 12 insertions(+), 3 deletions(-)

diff --git a/builtin/log.c b/builtin/log.c
index c211d66d1d0..00846c2c8ac 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1746,6 +1746,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	struct commit *commit;
 	struct commit **list = NULL;
 	struct rev_info rev;
+	char *extra_headers = NULL;
 	struct setup_revision_opt s_r_opt;
 	int nr = 0, total, i;
 	int use_stdout = 0;
@@ -1946,7 +1947,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		strbuf_addch(&buf, '\n');
 	}
 
-	rev.extra_headers = strbuf_detach(&buf, NULL);
+	extra_headers = strbuf_detach(&buf, NULL);
+	rev.extra_headers = extra_headers;
 
 	if (from) {
 		if (split_ident_line(&rev.from_ident, from, strlen(from)))
@@ -2173,8 +2175,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		prepare_bases(&bases, base, list, nr);
 	}
 
-	if (in_reply_to || thread || cover_letter)
-		rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
+	if (in_reply_to || thread || cover_letter) {
+		rev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));
+		string_list_init_nodup(rev.ref_message_ids);
+	}
 	if (in_reply_to) {
 		const char *msgid = clean_message_id(in_reply_to);
 		string_list_append(rev.ref_message_ids, msgid);
@@ -2281,6 +2285,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	strbuf_release(&rdiff1);
 	strbuf_release(&rdiff2);
 	strbuf_release(&rdiff_title);
+	free(extra_headers);
+	if (rev.ref_message_ids) {
+		string_list_clear(rev.ref_message_ids, 0);
+		free(rev.ref_message_ids);
+	}
 	UNLEAK(rev);
 	return 0;
 }
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 04/24] revision.[ch]: split freeing of revs->commit into a function
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (2 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 03/24] format-patch: don't leak "extra_headers" or "ref_message_ids" Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 13:16 ` [PATCH 05/24] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
                   ` (21 subsequent siblings)
  25 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

Move the existing code for invoking free_commit_list() and setting
revs->commits to NULL into a new release_revisions_commit_list()
function. This will be used as part of a general free()-ing mechanism
for "struct rev_info".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/revision.c b/revision.c
index 2bb913f2f3f..5824fdeddfd 100644
--- a/revision.c
+++ b/revision.c
@@ -2916,6 +2916,16 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 	return left;
 }
 
+static void release_revisions_commit_list(struct rev_info *revs)
+{
+	struct commit_list *commits = revs->commits;
+
+	if (!commits)
+		return;
+	free_commit_list(commits);
+	revs->commits = NULL;
+}
+
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
 {
 	struct commit_list *l = xcalloc(1, sizeof(*l));
@@ -4073,10 +4083,7 @@ static void create_boundary_commit_list(struct rev_info *revs)
 	 * boundary commits anyway.  (This is what the code has always
 	 * done.)
 	 */
-	if (revs->commits) {
-		free_commit_list(revs->commits);
-		revs->commits = NULL;
-	}
+	release_revisions_commit_list(revs);
 
 	/*
 	 * Put all of the actual boundary commits from revs->boundary_commits
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 05/24] revision.[ch]: provide and start using a release_revisions()
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (3 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 04/24] revision.[ch]: split freeing of revs->commit into a function Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 21:32   ` Taylor Blau
  2022-03-09 22:57   ` Junio C Hamano
  2022-03-09 13:16 ` [PATCH 06/24] revisions API users: add straightforward release_revisions() Ævar Arnfjörð Bjarmason
                   ` (20 subsequent siblings)
  25 siblings, 2 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

The users of the revision.[ch] API's "struct rev_info" are a major
source of memory leaks in the test suite under SANITIZE=leak, which in
turn adds a lot of noise when trying to mark up tests with
"TEST_PASSES_SANITIZE_LEAK=true".

The users of that API are largely one-shot, e.g. "git log", so
arguably freeing the memory is a waste of time, but in many cases
they've actually been trying to free the memory, and just doing that
in a buggy manner.

Let's provide a release_revisions() function for these users, and
start migrating them over per the plan outlined in [1]. Right now this
only handles the "pending" member of the struct, but more will be
added in subsequent commits.

Even though we only clear the "pending" member now, let's not leave a
trap in code like the pre-image of index_differs_from(), where we'd
start doing the wrong thing as soon as the release_revisions() learned
to clear its "diffopt". I.e. we need to call release_revisions() after
we've inspected any state in "struct rev_info".

1. https://lore.kernel.org/git/87a6k8daeu.fsf@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/checkout.c | 2 +-
 builtin/stash.c    | 3 +--
 diff-lib.c         | 6 ++++--
 range-diff.c       | 2 +-
 revision.c         | 7 +++++++
 revision.h         | 6 ++++++
 wt-status.c        | 7 ++++---
 7 files changed, 24 insertions(+), 9 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 9244827ca02..ed993383531 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -629,7 +629,7 @@ static void show_local_changes(struct object *head,
 	diff_setup_done(&rev.diffopt);
 	add_pending_object(&rev, head, NULL);
 	run_diff_index(&rev, 0);
-	object_array_clear(&rev.pending);
+	release_revisions(&rev);
 }
 
 static void describe_detached_head(const char *msg, struct commit *commit)
diff --git a/builtin/stash.c b/builtin/stash.c
index 3e8af210fde..af9b61ef17a 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1277,9 +1277,8 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
 
 done:
 	discard_index(&istate);
-	UNLEAK(rev);
-	object_array_clear(&rev.pending);
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 	strbuf_release(&diff_output);
 	remove_path(stash_index_path.buf);
 	return ret;
diff --git a/diff-lib.c b/diff-lib.c
index ca085a03efc..ad1dd4834f7 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -651,6 +651,7 @@ int index_differs_from(struct repository *r,
 {
 	struct rev_info rev;
 	struct setup_revision_opt opt;
+	unsigned int has_changes;
 
 	repo_init_revisions(r, &rev, NULL);
 	memset(&opt, 0, sizeof(opt));
@@ -662,8 +663,9 @@ int index_differs_from(struct repository *r,
 		diff_flags_or(&rev.diffopt.flags, flags);
 	rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
 	run_diff_index(&rev, 1);
-	object_array_clear(&rev.pending);
-	return (rev.diffopt.flags.has_changes != 0);
+	has_changes = rev.diffopt.flags.has_changes;
+	release_revisions(&rev);
+	return (has_changes != 0);
 }
 
 static struct strbuf *idiff_prefix_cb(struct diff_options *opt, void *data)
diff --git a/range-diff.c b/range-diff.c
index 30a4de5c2d8..2d0cd271a9e 100644
--- a/range-diff.c
+++ b/range-diff.c
@@ -600,6 +600,6 @@ int is_range_diff_range(const char *arg)
 	}
 
 	free(copy);
-	object_array_clear(&revs.pending);
+	release_revisions(&revs);
 	return negative > 0 && positive > 0;
 }
diff --git a/revision.c b/revision.c
index 5824fdeddfd..831f2cf977b 100644
--- a/revision.c
+++ b/revision.c
@@ -2926,6 +2926,13 @@ static void release_revisions_commit_list(struct rev_info *revs)
 	revs->commits = NULL;
 }
 
+void release_revisions(struct rev_info *revs)
+{
+	if (!revs)
+		return;
+	object_array_clear(&revs->pending);
+}
+
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
 {
 	struct commit_list *l = xcalloc(1, sizeof(*l));
diff --git a/revision.h b/revision.h
index b9c2421687f..c28e901021d 100644
--- a/revision.h
+++ b/revision.h
@@ -370,6 +370,12 @@ void repo_init_revisions(struct repository *r,
 int setup_revisions(int argc, const char **argv, struct rev_info *revs,
 		    struct setup_revision_opt *);
 
+/**
+ * Free data allocated in a "struct rev_info" after it's been setup
+ * with repo_init_revisions() and setup_revisions().
+ */
+void release_revisions(struct rev_info *revs);
+
 void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
 			const struct option *options,
 			const char * const usagestr[]);
diff --git a/wt-status.c b/wt-status.c
index 335e723a71e..398d0951aca 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -653,7 +653,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)
 	rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_index(&rev, 1);
-	object_array_clear(&rev.pending);
+	release_revisions(&rev);
 	clear_pathspec(&rev.prune_data);
 }
 
@@ -2568,8 +2568,9 @@ int has_uncommitted_changes(struct repository *r,
 
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_index(&rev_info, 1);
-	object_array_clear(&rev_info.pending);
-	return diff_result_code(&rev_info.diffopt, result);
+	result = diff_result_code(&rev_info.diffopt, result);
+	release_revisions(&rev_info);
+	return result;
 }
 
 /**
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 06/24] revisions API users: add straightforward release_revisions()
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (4 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 05/24] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 13:16 ` [PATCH 07/24] revisions API users: use release_revisions() needing "{ 0 }" init Ævar Arnfjörð Bjarmason
                   ` (19 subsequent siblings)
  25 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

Add a release_revisions() to various users of "struct rev_list" in
those straightforward cases where we only need to add the
release_revisions() call to the end of a block, and don't need to
e.g. refactor anything to use a "goto cleanup" pattern.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 add-interactive.c                | 1 +
 bisect.c                         | 2 ++
 builtin/add.c                    | 1 +
 builtin/am.c                     | 3 +++
 builtin/bisect--helper.c         | 2 ++
 builtin/blame.c                  | 1 +
 builtin/checkout.c               | 1 +
 builtin/commit.c                 | 1 +
 builtin/describe.c               | 2 ++
 builtin/fast-export.c            | 1 +
 builtin/merge.c                  | 2 ++
 builtin/pack-objects.c           | 2 ++
 builtin/prune.c                  | 1 +
 builtin/reflog.c                 | 1 +
 builtin/shortlog.c               | 2 ++
 builtin/submodule--helper.c      | 1 +
 fmt-merge-msg.c                  | 1 +
 merge-ort.c                      | 1 +
 merge-recursive.c                | 1 +
 midx.c                           | 1 +
 pack-bitmap-write.c              | 1 +
 ref-filter.c                     | 1 +
 remote.c                         | 1 +
 sequencer.c                      | 3 +++
 shallow.c                        | 1 +
 submodule.c                      | 2 ++
 t/helper/test-revision-walking.c | 1 +
 wt-status.c                      | 1 +
 28 files changed, 39 insertions(+)

diff --git a/add-interactive.c b/add-interactive.c
index e1ab39cce30..13d5ec6f49f 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -568,6 +568,7 @@ static int get_modified_files(struct repository *r,
 
 		if (ps)
 			clear_pathspec(&rev.prune_data);
+		release_revisions(&rev);
 	}
 	hashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);
 	if (unmerged_count)
diff --git a/bisect.c b/bisect.c
index 9e6a2b7f201..cc6b8b6230d 100644
--- a/bisect.c
+++ b/bisect.c
@@ -884,6 +884,7 @@ static int check_ancestors(struct repository *r, int rev_nr,
 	/* Clean up objects used, as they will be reused. */
 	clear_commit_marks_many(rev_nr, rev, ALL_REV_FLAGS);
 
+	release_revisions(&revs);
 	return res;
 }
 
@@ -964,6 +965,7 @@ static void show_diff_tree(struct repository *r,
 
 	setup_revisions(ARRAY_SIZE(argv) - 1, argv, &opt, NULL);
 	log_tree_commit(&opt, commit);
+	release_revisions(&opt);
 }
 
 /*
diff --git a/builtin/add.c b/builtin/add.c
index 3ffb86a4338..f507d2191cd 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -340,6 +340,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
 
 	unlink(file);
 	free(file);
+	release_revisions(&rev);
 	return 0;
 }
 
diff --git a/builtin/am.c b/builtin/am.c
index 0f4111bafa0..93bec62afa9 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1397,6 +1397,7 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm
 	add_pending_object(&rev_info, &commit->object, "");
 	diff_setup_done(&rev_info.diffopt);
 	log_tree_commit(&rev_info, commit);
+	release_revisions(&rev_info);
 }
 
 /**
@@ -1429,6 +1430,7 @@ static void write_index_patch(const struct am_state *state)
 	add_pending_object(&rev_info, &tree->object, "");
 	diff_setup_done(&rev_info.diffopt);
 	run_diff_index(&rev_info, 1);
+	release_revisions(&rev_info);
 }
 
 /**
@@ -1582,6 +1584,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
 		add_pending_oid(&rev_info, "HEAD", &our_tree, 0);
 		diff_setup_done(&rev_info.diffopt);
 		run_diff_index(&rev_info, 1);
+		release_revisions(&rev_info);
 	}
 
 	if (run_apply(state, index_path))
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 8b2b259ff0d..e4d7b6779ae 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -555,6 +555,7 @@ static int bisect_skipped_commits(struct bisect_terms *terms)
 	reset_revision_walk();
 
 	strbuf_release(&commit_name);
+	release_revisions(&revs);
 	fclose(fp);
 	return 0;
 }
@@ -1041,6 +1042,7 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar
 						oid_to_hex(&commit->object.oid));
 
 			reset_revision_walk();
+			release_revisions(&revs);
 		} else {
 			strvec_push(&argv_state, argv[i]);
 		}
diff --git a/builtin/blame.c b/builtin/blame.c
index 885b381ab83..24bac822c56 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1203,5 +1203,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 
 cleanup:
 	cleanup_scoreboard(&sb);
+	release_revisions(&revs);
 	return 0;
 }
diff --git a/builtin/checkout.c b/builtin/checkout.c
index ed993383531..93fe46c6424 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1075,6 +1075,7 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne
 
 	/* Clean up objects used, as they will be reused. */
 	repo_clear_commit_marks(the_repository, ALL_REV_FLAGS);
+	release_revisions(&revs);
 }
 
 static int switch_branches(const struct checkout_opts *opts,
diff --git a/builtin/commit.c b/builtin/commit.c
index 8b8bdad3959..74388a6b9a1 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1120,6 +1120,7 @@ static const char *find_author_by_nickname(const char *name)
 		strbuf_release(&buf);
 		format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
 		clear_mailmap(&mailmap);
+		release_revisions(&revs);
 		return strbuf_detach(&buf, NULL);
 	}
 	die(_("--author '%s' is not 'Name <email>' and matches no existing author"), name);
diff --git a/builtin/describe.c b/builtin/describe.c
index 42159cd26bd..a76f1a1a7a7 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -517,6 +517,7 @@ static void describe_blob(struct object_id oid, struct strbuf *dst)
 
 	traverse_commit_list(&revs, process_commit, process_object, &pcd);
 	reset_revision_walk();
+	release_revisions(&revs);
 }
 
 static void describe(const char *arg, int last_one)
@@ -667,6 +668,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
 				suffix = NULL;
 			else
 				suffix = dirty;
+			release_revisions(&revs);
 		}
 		describe("HEAD", 1);
 	} else if (dirty) {
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index 510139e9b54..ade0120b3a8 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -1275,6 +1275,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
 		printf("done\n");
 
 	refspec_clear(&refspecs);
+	release_revisions(&revs);
 
 	return 0;
 }
diff --git a/builtin/merge.c b/builtin/merge.c
index a94a03384ae..e99549db6fd 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -443,6 +443,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
 	}
 	write_file_buf(git_path_squash_msg(the_repository), out.buf, out.len);
 	strbuf_release(&out);
+	release_revisions(&rev);
 }
 
 static void finish(struct commit *head_commit,
@@ -992,6 +993,7 @@ static int evaluate_result(void)
 	 */
 	cnt += count_unmerged_entries();
 
+	release_revisions(&rev);
 	return cnt;
 }
 
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 178e611f09d..17194a58155 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -3384,6 +3384,7 @@ static void read_packs_list_from_stdin(void)
 	strbuf_release(&buf);
 	string_list_clear(&include_packs, 0);
 	string_list_clear(&exclude_packs, 0);
+	release_revisions(&revs);
 }
 
 static void read_object_list_from_stdin(void)
@@ -3799,6 +3800,7 @@ static void get_object_list(int ac, const char **av)
 	if (unpack_unreachable)
 		loosen_unused_packed_objects();
 
+	release_revisions(&revs);
 	oid_array_clear(&recent_objects);
 }
 
diff --git a/builtin/prune.c b/builtin/prune.c
index c2bcdc07db4..df376b2ed1e 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -196,5 +196,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
 		prune_shallow(show_only ? PRUNE_SHOW_ONLY : 0);
 	}
 
+	release_revisions(&revs);
 	return 0;
 }
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 016466852f1..5d870b4f2d1 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -648,6 +648,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 		if (verbose)
 			printf(_("Marking reachable objects..."));
 		mark_reachable_objects(&revs, 0, 0, NULL);
+		release_revisions(&revs);
 		if (verbose)
 			putchar('\n');
 	}
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 228d782754a..e609357bd23 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -420,6 +420,8 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 	else
 		get_from_rev(&rev, &log);
 
+	release_revisions(&rev);
+
 	shortlog_output(&log);
 	if (log.file != stdout)
 		fclose(log.file);
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index d8638434dcb..7fbba812583 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1259,6 +1259,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 		run_diff_files(&rev, 0);
 	prepare_submodule_summary(info, &list);
 	strvec_clear(&diff_args);
+	release_revisions(&rev);
 	return 0;
 }
 
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index baca57d5b64..f48f44f9cd1 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -699,6 +699,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 			shortlog(origins.items[i].string,
 				 origins.items[i].util,
 				 head, &rev, opts, out);
+		release_revisions(&rev);
 	}
 
 	strbuf_complete_line(out);
diff --git a/merge-ort.c b/merge-ort.c
index ff739d4b360..658c580ab91 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -1593,6 +1593,7 @@ static int find_first_merges(struct repository *repo,
 	}
 
 	object_array_clear(&merges);
+	release_revisions(&revs);
 	return result->nr;
 }
 
diff --git a/merge-recursive.c b/merge-recursive.c
index 9ec1e6d043a..6296685d00b 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -1160,6 +1160,7 @@ static int find_first_merges(struct repository *repo,
 	}
 
 	object_array_clear(&merges);
+	release_revisions(&revs);
 	return result->nr;
 }
 
diff --git a/midx.c b/midx.c
index 865170bad05..702c8a9b178 100644
--- a/midx.c
+++ b/midx.c
@@ -1061,6 +1061,7 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr
 	if (indexed_commits_nr_p)
 		*indexed_commits_nr_p = cb.commits_nr;
 
+	release_revisions(&revs);
 	return cb.commits;
 }
 
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index cab3eaa2acd..ea8e0b51cdf 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -326,6 +326,7 @@ static void bitmap_builder_init(struct bitmap_builder *bb,
 	trace2_data_intmax("pack-bitmap-write", the_repository,
 			   "num_maximal_commits", num_maximal);
 
+	release_revisions(&revs);
 	free_commit_list(reusable);
 }
 
diff --git a/ref-filter.c b/ref-filter.c
index 7838bd22b8d..a91688bbf17 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2392,6 +2392,7 @@ static void reach_filter(struct ref_array *array,
 		clear_commit_marks(merge_commit, ALL_REV_FLAGS);
 	}
 
+	release_revisions(&revs);
 	free(to_clear);
 }
 
diff --git a/remote.c b/remote.c
index c97c626eaa8..0a7d8757d99 100644
--- a/remote.c
+++ b/remote.c
@@ -2172,6 +2172,7 @@ static int stat_branch_pair(const char *branch_name, const char *base,
 	clear_commit_marks(theirs, ALL_REV_FLAGS);
 
 	strvec_clear(&argv);
+	release_revisions(&revs);
 	return 1;
 }
 
diff --git a/sequencer.c b/sequencer.c
index 35006c0cea6..b15f8d1ff50 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1347,6 +1347,7 @@ void print_commit_summary(struct repository *r,
 		log_tree_commit(&rev, commit);
 	}
 
+	release_revisions(&rev);
 	strbuf_release(&format);
 }
 
@@ -3415,6 +3416,7 @@ static int make_patch(struct repository *r,
 		unuse_commit_buffer(commit, commit_buffer);
 	}
 	strbuf_release(&buf);
+	release_revisions(&log_tree_opt);
 
 	return res;
 }
@@ -4527,6 +4529,7 @@ static int pick_commits(struct repository *r,
 					      &log_tree_opt.diffopt);
 				log_tree_diff_flush(&log_tree_opt);
 			}
+			release_revisions(&log_tree_opt);
 		}
 		flush_rewritten_pending();
 		if (!stat(rebase_path_rewritten_list(), &st) &&
diff --git a/shallow.c b/shallow.c
index 71e5876f377..2552f139f61 100644
--- a/shallow.c
+++ b/shallow.c
@@ -261,6 +261,7 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,
 		if ((o->flags & both_flags) == both_flags)
 			o->flags &= ~not_shallow_flag;
 	}
+	release_revisions(&revs);
 	return result;
 }
 
diff --git a/submodule.c b/submodule.c
index 5ace18a7d94..0510cb193b6 100644
--- a/submodule.c
+++ b/submodule.c
@@ -902,9 +902,11 @@ static void collect_changed_submodules(struct repository *r,
 		diff_rev.diffopt.format_callback_data = &data;
 		diff_rev.dense_combined_merges = 1;
 		diff_tree_combined_merge(commit, &diff_rev);
+		release_revisions(&diff_rev);
 	}
 
 	reset_revision_walk();
+	release_revisions(&rev);
 }
 
 static void free_submodules_oids(struct string_list *submodules)
diff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c
index 625b2dbf822..4a45d5bac2a 100644
--- a/t/helper/test-revision-walking.c
+++ b/t/helper/test-revision-walking.c
@@ -43,6 +43,7 @@ static int run_revision_walk(void)
 	}
 
 	reset_revision_walk();
+	release_revisions(&rev);
 	return got_revision;
 }
 
diff --git a/wt-status.c b/wt-status.c
index 398d0951aca..a68731ea0f7 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1143,6 +1143,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
 		rev.diffopt.b_prefix = "w/";
 		run_diff_files(&rev, 0);
 	}
+	release_revisions(&rev);
 }
 
 static void wt_longstatus_print_tracking(struct wt_status *s)
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 07/24] revisions API users: use release_revisions() needing "{ 0 }" init
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (5 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 06/24] revisions API users: add straightforward release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 13:16 ` [PATCH 08/24] revisions API users: add "goto cleanup" for release_revisions() Ævar Arnfjörð Bjarmason
                   ` (18 subsequent siblings)
  25 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

Use release_revisions() to various users of "struct rev_list" which
need to have their "struct rev_info" zero-initialized before we can
start using it. In all of these cases we might "goto cleanup" (or equivalent),

For the bundle.c code see the early exit case added in
3bbbe467f29 (bundle verify: error out if called without an object
database, 2019-05-27).

For the relevant bisect.c code see 45b6370812c (bisect: libify
`check_good_are_ancestors_of_bad` and its dependents, 2020-02-17).

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 bisect.c                    | 18 ++++++++++++------
 builtin/submodule--helper.c |  3 ++-
 bundle.c                    | 12 ++++++++----
 3 files changed, 22 insertions(+), 11 deletions(-)

diff --git a/bisect.c b/bisect.c
index cc6b8b6230d..159a4b644df 100644
--- a/bisect.c
+++ b/bisect.c
@@ -1010,7 +1010,7 @@ void read_bisect_terms(const char **read_bad, const char **read_good)
  */
 enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 {
-	struct rev_info revs;
+	struct rev_info revs = { 0 };
 	struct commit_list *tried;
 	int reaches = 0, all = 0, nr, steps;
 	enum bisect_error res = BISECT_OK;
@@ -1035,7 +1035,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 
 	res = check_good_are_ancestors_of_bad(r, prefix, no_checkout);
 	if (res)
-		return res;
+		goto cleanup;
 
 	bisect_rev_setup(r, &revs, prefix, "%s", "^%s", 1);
 
@@ -1060,14 +1060,16 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 		       term_good,
 		       term_bad);
 
-		return BISECT_FAILED;
+		res = BISECT_FAILED;
+		goto cleanup;
 	}
 
 	if (!all) {
 		fprintf(stderr, _("No testable commit found.\n"
 			"Maybe you started with bad path arguments?\n"));
 
-		return BISECT_NO_TESTABLE_COMMIT;
+		res = BISECT_NO_TESTABLE_COMMIT;
+		goto cleanup;
 	}
 
 	bisect_rev = &revs.commits->item->object.oid;
@@ -1087,7 +1089,8 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 		 * for negative return values for early returns up
 		 * until the cmd_bisect__helper() caller.
 		 */
-		return BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;
+		res = BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;
+		goto cleanup;
 	}
 
 	nr = all - reaches - 1;
@@ -1106,7 +1109,10 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 	/* Clean up objects used, as they will be reused. */
 	repo_clear_commit_marks(r, ALL_REV_FLAGS);
 
-	return bisect_checkout(bisect_rev, no_checkout);
+	res = bisect_checkout(bisect_rev, no_checkout);
+cleanup:
+	release_revisions(&revs);
+	return res;
 }
 
 static inline int log2i(int n)
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 7fbba812583..09db2620829 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -756,7 +756,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
 {
 	char *displaypath;
 	struct strvec diff_files_args = STRVEC_INIT;
-	struct rev_info rev;
+	struct rev_info rev = { 0 };
 	int diff_files_result;
 	struct strbuf buf = STRBUF_INIT;
 	const char *git_dir;
@@ -843,6 +843,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
 cleanup:
 	strvec_clear(&diff_files_args);
 	free(displaypath);
+	release_revisions(&rev);
 }
 
 static void status_submodule_cb(const struct cache_entry *list_item,
diff --git a/bundle.c b/bundle.c
index a0bb687b0f4..207541a3410 100644
--- a/bundle.c
+++ b/bundle.c
@@ -191,14 +191,16 @@ int verify_bundle(struct repository *r,
 	 * to be verbose about the errors
 	 */
 	struct string_list *p = &header->prerequisites;
-	struct rev_info revs;
+	struct rev_info revs = { 0 };
 	const char *argv[] = {NULL, "--all", NULL};
 	struct commit *commit;
 	int i, ret = 0, req_nr;
 	const char *message = _("Repository lacks these prerequisite commits:");
 
-	if (!r || !r->objects || !r->objects->odb)
-		return error(_("need a repository to verify a bundle"));
+	if (!r || !r->objects || !r->objects->odb) {
+		ret = error(_("need a repository to verify a bundle"));
+		goto cleanup;
+	}
 
 	repo_init_revisions(r, &revs, NULL);
 	for (i = 0; i < p->nr; i++) {
@@ -216,7 +218,7 @@ int verify_bundle(struct repository *r,
 		error("%s %s", oid_to_hex(oid), name);
 	}
 	if (revs.pending.nr != p->nr)
-		return ret;
+		goto cleanup;
 	req_nr = revs.pending.nr;
 	setup_revisions(2, argv, &revs, NULL);
 
@@ -270,6 +272,8 @@ int verify_bundle(struct repository *r,
 			list_refs(r, 0, NULL);
 		}
 	}
+cleanup:
+	release_revisions(&revs);
 	return ret;
 }
 
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 08/24] revisions API users: add "goto cleanup" for release_revisions()
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (6 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 07/24] revisions API users: use release_revisions() needing "{ 0 }" init Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 19:08   ` Derrick Stolee
  2022-03-09 13:16 ` [PATCH 09/24] revisions API users: use release_revisions() in http-push.c Ævar Arnfjörð Bjarmason
                   ` (17 subsequent siblings)
  25 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

Add a release_revisions() to various users of "struct rev_list" which
requires a minor refactoring to a "goto cleanup" pattern to use that
function.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/diff-files.c        |  8 ++++++--
 builtin/rev-list.c          | 19 ++++++++++++-------
 builtin/stash.c             | 14 ++++++++++----
 builtin/submodule--helper.c | 11 ++++++++---
 sequencer.c                 | 23 ++++++++++++++++-------
 t/helper/test-fast-rebase.c | 19 +++++++++++++------
 6 files changed, 65 insertions(+), 29 deletions(-)

diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index 70103c40952..92cf6e1e922 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -77,8 +77,12 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
 
 	if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
 		perror("read_cache_preload");
-		return -1;
+		result = -1;
+		goto cleanup;
 	}
 	result = run_diff_files(&rev, options);
-	return diff_result_code(&rev.diffopt, result);
+	result = diff_result_code(&rev.diffopt, result);
+cleanup:
+	release_revisions(&rev);
+	return result;
 }
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 38528c7f156..5db8e71645f 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -506,6 +506,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 	int use_bitmap_index = 0;
 	int filter_provided_objects = 0;
 	const char *show_progress = NULL;
+	int ret = 0;
 
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage(rev_list_usage);
@@ -589,7 +590,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 		}
 		if (!strcmp(arg, "--test-bitmap")) {
 			test_bitmap_walk(&revs);
-			return 0;
+			goto cleanup;
 		}
 		if (skip_prefix(arg, "--progress=", &arg)) {
 			show_progress = arg;
@@ -689,11 +690,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 
 	if (use_bitmap_index) {
 		if (!try_bitmap_count(&revs, &filter_options, filter_provided_objects))
-			return 0;
+			goto cleanup;
 		if (!try_bitmap_disk_usage(&revs, &filter_options, filter_provided_objects))
-			return 0;
+			goto cleanup;
 		if (!try_bitmap_traversal(&revs, &filter_options, filter_provided_objects))
-			return 0;
+			goto cleanup;
 	}
 
 	if (prepare_revision_walk(&revs))
@@ -713,8 +714,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 
 		find_bisection(&revs.commits, &reaches, &all, bisect_flags);
 
-		if (bisect_show_vars)
-			return show_bisect_vars(&info, reaches, all);
+		if (bisect_show_vars) {
+			ret = show_bisect_vars(&info, reaches, all);
+			goto cleanup;
+		}
 	}
 
 	if (filter_provided_objects) {
@@ -769,5 +772,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 	if (show_disk_usage)
 		printf("%"PRIuMAX"\n", (uintmax_t)total_disk_usage);
 
-	return 0;
+cleanup:
+	release_revisions(&revs);
+	return ret;
 }
diff --git a/builtin/stash.c b/builtin/stash.c
index af9b61ef17a..0b9d55d09bb 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -874,8 +874,10 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 
 	ret = get_stash_info(&info, stash_args.nr, stash_args.v);
 	strvec_clear(&stash_args);
-	if (ret)
-		return -1;
+	if (ret) {
+		ret = -1;
+		goto cleanup;
+	}
 
 	/*
 	 * The config settings are applied only if there are not passed
@@ -890,7 +892,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 
 		if (!show_stat && !show_patch) {
 			free_stash_info(&info);
-			return 0;
+			ret = 0;
+			goto cleanup;
 		}
 	}
 
@@ -924,7 +927,10 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 	log_tree_diff_flush(&rev);
 
 	free_stash_info(&info);
-	return diff_result_code(&rev.diffopt, 0);
+	ret = diff_result_code(&rev.diffopt, 0);
+cleanup:
+	release_revisions(&rev);
+	return ret;
 }
 
 static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 09db2620829..19393da4e31 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1222,6 +1222,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 	struct strvec diff_args = STRVEC_INIT;
 	struct rev_info rev;
 	struct module_cb_list list = MODULE_CB_LIST_INIT;
+	int ret = 0;
 
 	strvec_push(&diff_args, get_diff_cmd(diff_cmd));
 	if (info->cached)
@@ -1247,11 +1248,13 @@ static int compute_summary_module_list(struct object_id *head_oid,
 			setup_work_tree();
 		if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
 			perror("read_cache_preload");
-			return -1;
+			ret = -1;
+			goto cleanup;
 		}
 	} else if (read_cache() < 0) {
 		perror("read_cache");
-		return -1;
+		ret = -1;
+		goto cleanup;
 	}
 
 	if (diff_cmd == DIFF_INDEX)
@@ -1259,9 +1262,11 @@ static int compute_summary_module_list(struct object_id *head_oid,
 	else
 		run_diff_files(&rev, 0);
 	prepare_submodule_summary(info, &list);
+	ret = 0;
+cleanup:
 	strvec_clear(&diff_args);
 	release_revisions(&rev);
-	return 0;
+	return ret;
 }
 
 static int module_summary(int argc, const char **argv, const char *prefix)
diff --git a/sequencer.c b/sequencer.c
index b15f8d1ff50..40d41c09523 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -5356,6 +5356,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
 	int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
 	int reapply_cherry_picks = flags & TODO_LIST_REAPPLY_CHERRY_PICKS;
 	int skipped_commit = 0;
+	int ret = 0;
 
 	repo_init_revisions(r, &revs, NULL);
 	revs.verbose_header = 1;
@@ -5379,14 +5380,20 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
 	pp.fmt = revs.commit_format;
 	pp.output_encoding = get_log_output_encoding();
 
-	if (setup_revisions(argc, argv, &revs, NULL) > 1)
-		return error(_("make_script: unhandled options"));
+	if (setup_revisions(argc, argv, &revs, NULL) > 1) {
+		ret = error(_("make_script: unhandled options"));
+		goto cleanup;
+	}
 
-	if (prepare_revision_walk(&revs) < 0)
-		return error(_("make_script: error preparing revisions"));
+	if (prepare_revision_walk(&revs) < 0) {
+		ret = error(_("make_script: error preparing revisions"));
+		goto cleanup;
+	}
 
-	if (rebase_merges)
-		return make_script_with_merges(&pp, &revs, out, flags);
+	if (rebase_merges) {
+		ret = make_script_with_merges(&pp, &revs, out, flags);
+		goto cleanup;
+	}
 
 	while ((commit = get_revision(&revs))) {
 		int is_empty = is_original_commit_empty(commit);
@@ -5410,7 +5417,9 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
 	if (skipped_commit)
 		advise_if_enabled(ADVICE_SKIPPED_CHERRY_PICKS,
 				  _("use --reapply-cherry-picks to include skipped commits"));
-	return 0;
+cleanup:
+	release_revisions(&revs);
+	return ret;
 }
 
 /*
diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c
index 993b90eaedd..e49a0244836 100644
--- a/t/helper/test-fast-rebase.c
+++ b/t/helper/test-fast-rebase.c
@@ -99,6 +99,7 @@ int cmd__fast_rebase(int argc, const char **argv)
 	struct merge_result result;
 	struct strbuf reflog_msg = STRBUF_INIT;
 	struct strbuf branch_name = STRBUF_INIT;
+	int ret = 0;
 
 	/*
 	 * test-tool stuff doesn't set up the git directory by default; need to
@@ -137,13 +138,17 @@ int cmd__fast_rebase(int argc, const char **argv)
 	revs.topo_order = 1;
 	strvec_pushl(&rev_walk_args, "", argv[4], "--not", argv[3], NULL);
 
-	if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1)
-		return error(_("unhandled options"));
+	if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) {
+		ret =  error(_("unhandled options"));
+		goto cleanup;
+	}
 
 	strvec_clear(&rev_walk_args);
 
-	if (prepare_revision_walk(&revs) < 0)
-		return error(_("error preparing revisions"));
+	if (prepare_revision_walk(&revs) < 0) {
+		ret = error(_("error preparing revisions"));
+		goto cleanup;
+	}
 
 	init_merge_options(&merge_opt, the_repository);
 	memset(&result, 0, sizeof(result));
@@ -219,8 +224,10 @@ int cmd__fast_rebase(int argc, const char **argv)
 	if (write_locked_index(&the_index, &lock,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("unable to write %s"), get_index_file());
-
+	ret = (result.clean == 0);
+cleanup:
 	strbuf_release(&reflog_msg);
 	strbuf_release(&branch_name);
-	return (result.clean == 0);
+	release_revisions(&revs);
+	return ret;
 }
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 09/24] revisions API users: use release_revisions() in http-push.c
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (7 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 08/24] revisions API users: add "goto cleanup" for release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 13:16 ` [PATCH 10/24] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
                   ` (16 subsequent siblings)
  25 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

In the case of cmd_main() in http-push.c we need to move the
deceleration of the "struct rev-list" into the loop over the
"remote_refs" when adding a release_revisions().

We'd previously set up the "revs" for each remote, but would
potentially leak memory on each one.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 http-push.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/http-push.c b/http-push.c
index 3309aaf004a..8c18f01bb60 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1689,7 +1689,6 @@ int cmd_main(int argc, const char **argv)
 	struct refspec rs = REFSPEC_INIT_PUSH;
 	struct remote_lock *ref_lock = NULL;
 	struct remote_lock *info_ref_lock = NULL;
-	struct rev_info revs;
 	int delete_branch = 0;
 	int force_delete = 0;
 	int objects_to_send;
@@ -1825,6 +1824,7 @@ int cmd_main(int argc, const char **argv)
 
 	new_refs = 0;
 	for (ref = remote_refs; ref; ref = ref->next) {
+		struct rev_info revs;
 		struct strvec commit_argv = STRVEC_INIT;
 
 		if (!ref->peer_ref)
@@ -1941,6 +1941,7 @@ int cmd_main(int argc, const char **argv)
 		unlock_remote(ref_lock);
 		check_locks();
 		strvec_clear(&commit_argv);
+		release_revisions(&revs);
 	}
 
 	/* Update remote server info if appropriate */
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 10/24] revisions API users: use release_revisions() in builtin/log.c
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (8 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 09/24] revisions API users: use release_revisions() in http-push.c Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 19:12   ` Derrick Stolee
  2022-03-09 21:53   ` Taylor Blau
  2022-03-09 13:16 ` [PATCH 11/24] revisions API users: use release_revisions() with UNLEAK() Ævar Arnfjörð Bjarmason
                   ` (15 subsequent siblings)
  25 siblings, 2 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

In preparation for having the "log" family of functions make wider use
of release_revisions() let's have them call it just before
exiting. This changes the "log", "whatchanged", "show",
"format-patch", etc. commands, all of which live in this file.

The release_revisions() API still only frees the "pending" member, but
will learn to more members of "struct rev_info" in subsequent commits.

In the case of "format-patch" revert the addition of UNLEAK() in
dee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16), and
which will cause several tests that previously passed under
"TEST_PASSES_SANITIZE_LEAK=true" to start failing.

In subsequent commits we'll now be able to use those tests to check
whether that part of the API is really leaking memory, and will fix
all of those memory leaks. Removing the UNLEAK() allows us to make
incremental progress in that direction. See [1] for further details
about this approach.

1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/log.c          | 20 ++++++++++++--------
 t/t4126-apply-empty.sh |  2 --
 2 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/builtin/log.c b/builtin/log.c
index 00846c2c8ac..3c1edc27605 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -294,6 +294,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
 	cmd_log_init_finish(argc, argv, prefix, rev, opt);
 }
 
+static int cmd_log_deinit(int ret, struct rev_info *rev)
+{
+	release_revisions(rev);
+	return ret;
+}
+
 /*
  * This gives a rough estimate for how many commits we
  * will print out in the list.
@@ -557,7 +563,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
 	cmd_log_init(argc, argv, prefix, &rev, &opt);
 	if (!rev.diffopt.output_format)
 		rev.diffopt.output_format = DIFF_FORMAT_RAW;
-	return cmd_log_walk(&rev);
+	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 }
 
 static void show_tagger(const char *buf, struct rev_info *rev)
@@ -676,7 +682,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
 	cmd_log_init(argc, argv, prefix, &rev, &opt);
 
 	if (!rev.no_walk)
-		return cmd_log_walk(&rev);
+		return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 
 	count = rev.pending.nr;
 	objects = rev.pending.objects;
@@ -731,8 +737,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
 			ret = error(_("unknown type: %d"), o->type);
 		}
 	}
-	free(objects);
-	return ret;
+	return cmd_log_deinit(ret, &rev);
 }
 
 /*
@@ -760,7 +765,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
 	rev.always_show_header = 1;
 	cmd_log_init_finish(argc, argv, prefix, &rev, &opt);
 
-	return cmd_log_walk(&rev);
+	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 }
 
 static void log_setup_revisions_tweak(struct rev_info *rev,
@@ -791,7 +796,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)
 	opt.revarg_opt = REVARG_COMMITTISH;
 	opt.tweak = log_setup_revisions_tweak;
 	cmd_log_init(argc, argv, prefix, &rev, &opt);
-	return cmd_log_walk(&rev);
+	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 }
 
 /* format-patch */
@@ -2290,8 +2295,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		string_list_clear(rev.ref_message_ids, 0);
 		free(rev.ref_message_ids);
 	}
-	UNLEAK(rev);
-	return 0;
+	return cmd_log_deinit(0, &rev);
 }
 
 static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
diff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh
index 33860d38290..66a7ba8ab8f 100755
--- a/t/t4126-apply-empty.sh
+++ b/t/t4126-apply-empty.sh
@@ -2,8 +2,6 @@
 
 test_description='apply empty'
 
-
-TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 11/24] revisions API users: use release_revisions() with UNLEAK()
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (9 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 10/24] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 13:16 ` [PATCH 12/24] revisions API users: use release_revisions() in submodule.c edge case Ævar Arnfjörð Bjarmason
                   ` (14 subsequent siblings)
  25 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

Use a release_revisions() with those "struct rev_list" users which
already "UNLEAK" the struct. It may seem odd to simultaneously attempt
to free() memory, but also to explicitly ignore whether we have memory
leaks in the same.

As explained in preceding commits this is being done to use the
built-in commands as a guinea pig for whether the release_revisions()
function works as expected, we'd like to test e.g. whether we segfault
as we change it. In subsequent commits we'll then remove these
UNLEAK() as the function is made to free the memory that caused us to
add them in the first place.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/diff-index.c | 4 +++-
 builtin/diff.c       | 1 +
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 5fd23ab5b6c..3a83183c312 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -71,5 +71,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
 	}
 	result = run_diff_index(&rev, option);
 	UNLEAK(rev);
-	return diff_result_code(&rev.diffopt, result);
+	result = diff_result_code(&rev.diffopt, result);
+	release_revisions(&rev);
+	return result;
 }
diff --git a/builtin/diff.c b/builtin/diff.c
index bb7fafca618..dd48336da56 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -595,6 +595,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
 	if (1 < rev.diffopt.skip_stat_unmatch)
 		refresh_index_quietly();
 	UNLEAK(rev);
+	release_revisions(&rev);
 	UNLEAK(ent);
 	UNLEAK(blob);
 	return result;
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 12/24] revisions API users: use release_revisions() in submodule.c edge case
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (10 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 11/24] revisions API users: use release_revisions() with UNLEAK() Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 13:16 ` [PATCH 13/24] revisions API users: use release_revisions() in wt-status.c " Ævar Arnfjörð Bjarmason
                   ` (13 subsequent siblings)
  25 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

Use release_revisions() on the the "struct rev_info" in
show_submodule_diff_summary() where we need to pre-initialize with "{
0 }" to make sure that we won't segfault when calling
release_revisions().

Most functions that use "struct rev_info" will call
repo_init_revisions() early, which will do an equivalent memset(). In
this case we might "goto out" before doing the equivalent of that
operation, see the "goto" on "(!left || !right || !sub)" added in
8e6df65015f (submodule: refactor show_submodule_summary with helper
function, 2016-08-31).

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 submodule.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/submodule.c b/submodule.c
index 0510cb193b6..285c1896c9e 100644
--- a/submodule.c
+++ b/submodule.c
@@ -638,7 +638,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,
 		struct object_id *one, struct object_id *two,
 		unsigned dirty_submodule)
 {
-	struct rev_info rev;
+	struct rev_info rev = { 0 };
 	struct commit *left = NULL, *right = NULL;
 	struct commit_list *merge_bases = NULL;
 	struct repository *sub;
@@ -664,6 +664,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,
 	print_submodule_diff_summary(sub, &rev, o);
 
 out:
+	release_revisions(&rev);
 	if (merge_bases)
 		free_commit_list(merge_bases);
 	clear_commit_marks(left, ~0);
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 13/24] revisions API users: use release_revisions() in wt-status.c edge case
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (11 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 12/24] revisions API users: use release_revisions() in submodule.c edge case Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 13:16 ` [PATCH 14/24] revisions API users: use release_revisions() for "prune_data" users Ævar Arnfjörð Bjarmason
                   ` (12 subsequent siblings)
  25 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

Use release_revisions() for the "struct rev_info" in
has_unstaged_changes() which didn't require the addition of a "goto"
pattern, but needed us to refactor another "return fn(...)" pattern.

This change is split from preceding changes to make similar
conversions in those commits easier to review, as they are all
repetitions of the same pattern.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 wt-status.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/wt-status.c b/wt-status.c
index a68731ea0f7..89289e52603 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -2537,7 +2537,9 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)
 	rev_info.diffopt.flags.quick = 1;
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_files(&rev_info, 0);
-	return diff_result_code(&rev_info.diffopt, result);
+	result = diff_result_code(&rev_info.diffopt, result);
+	release_revisions(&rev_info);
+	return result;
 }
 
 /**
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 14/24] revisions API users: use release_revisions() for "prune_data" users
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (12 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 13/24] revisions API users: use release_revisions() in wt-status.c " Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 13:16 ` [PATCH 15/24] revisions API: have release_revisions() release "commits" Ævar Arnfjörð Bjarmason
                   ` (11 subsequent siblings)
  25 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

Use release_revisions() for users of "struct rev_list" that reach into
the "struct rev_info" and clear the "prune_data" already.

In a subsequent commit we'll teach release_revisions() to clear this
itself, but in the meantime let's invoke release_revisions() here to
clear anything else we may have missed, and for reasons of having
consistent boilerplate.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/add.c   | 1 +
 builtin/stash.c | 1 +
 diff-lib.c      | 1 +
 wt-status.c     | 1 +
 4 files changed, 4 insertions(+)

diff --git a/builtin/add.c b/builtin/add.c
index f507d2191cd..115a26ea633 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -143,6 +143,7 @@ int add_files_to_cache(const char *prefix,
 	rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
 	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 	return !!data.add_errors;
 }
 
diff --git a/builtin/stash.c b/builtin/stash.c
index 0b9d55d09bb..b9fe6f7c712 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1073,6 +1073,7 @@ static int check_changes_tracked_files(const struct pathspec *ps)
 
 done:
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 	return ret;
 }
 
diff --git a/diff-lib.c b/diff-lib.c
index ad1dd4834f7..68858a39964 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -642,6 +642,7 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
 	if (diff_cache(&revs, tree_oid, NULL, 1))
 		exit(128);
 	clear_pathspec(&revs.prune_data);
+	release_revisions(&revs);
 	return 0;
 }
 
diff --git a/wt-status.c b/wt-status.c
index 89289e52603..aaef10451f3 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -617,6 +617,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_files(&rev, 0);
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 }
 
 static void wt_status_collect_changes_index(struct wt_status *s)
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 15/24] revisions API: have release_revisions() release "commits"
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (13 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 14/24] revisions API users: use release_revisions() for "prune_data" users Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 13:16 ` [PATCH 16/24] revisions API: have release_revisions() release "mailmap" Ævar Arnfjörð Bjarmason
                   ` (10 subsequent siblings)
  25 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"commits" in the "struct ref_info".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/revision.c b/revision.c
index 831f2cf977b..475901d77e3 100644
--- a/revision.c
+++ b/revision.c
@@ -2930,6 +2930,7 @@ void release_revisions(struct rev_info *revs)
 {
 	if (!revs)
 		return;
+	release_revisions_commit_list(revs);
 	object_array_clear(&revs->pending);
 }
 
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 16/24] revisions API: have release_revisions() release "mailmap"
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (14 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 15/24] revisions API: have release_revisions() release "commits" Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 19:17   ` Derrick Stolee
  2022-03-09 13:16 ` [PATCH 17/24] revisions API: have release_revisions() release "cmdline" Ævar Arnfjörð Bjarmason
                   ` (9 subsequent siblings)
  25 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"prune_data" in the "struct ref_info".

The log family of functions now calls the clear_mailmap() function
added in fa8afd18e5a (revisions API: provide and use a
release_revisions(), 2021-09-19), allowing us to whitelist some tests
with "TEST_PASSES_SANITIZE_LEAK=true".

Unfortunately having a pointer to a mailmap in "struct rev_info"
instead of an embedded member that we "own" get a bit messy, as can be
seen in the change to builtin/commit.c.

When we free() this data we won't be able to tell apart a pointer to a
"mailmap" on the heap from one on the stack. As seen in
ea57bc0d41b (log: add --use-mailmap option, 2013-01-05) the "log"
family allocates it on the heap, but in the find_author_by_nickname()
code added in ea16794e430 (commit: search author pattern against
mailmap, 2013-08-23) we allocated it on the stack instead.

Ideally we'd simply change that member to a "struct string_list
mailmap" and never free() the "mailmap" itself, but that would be a
much larger change to the revisions API.

We have code that needs to hand an existing "mailmap" to a "struct
rev_info", while we could change all of that, let's not go there
now.

The complexity isn't in the ownership of the "mailmap" per-se, but
that various things assume a "rev_info.mailmap == NULL" means "doesn't
want mailmap", if we changed that to an init'd "struct string_list
we'd need to carefully refactor things to change those assumptions.

Let's instead always free() it, and simply declare that if you add
such a "mailmap" it must be allocated on the heap. Any modern libc
will correctly panic if we free() a stack variable, so this should be
safe going forward.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/commit.c                   | 6 +++---
 revision.c                         | 9 +++++++++
 t/t0056-git-C.sh                   | 1 +
 t/t4055-diff-context.sh            | 1 +
 t/t4066-diff-emit-delay.sh         | 1 +
 t/t7008-filter-branch-null-sha1.sh | 2 ++
 6 files changed, 17 insertions(+), 3 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index 74388a6b9a1..9a6f38db165 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1097,7 +1097,7 @@ static const char *find_author_by_nickname(const char *name)
 	struct rev_info revs;
 	struct commit *commit;
 	struct strbuf buf = STRBUF_INIT;
-	struct string_list mailmap = STRING_LIST_INIT_NODUP;
+	struct string_list *mailmap = xmalloc(sizeof(struct string_list));
 	const char *av[20];
 	int ac = 0;
 
@@ -1108,7 +1108,8 @@ static const char *find_author_by_nickname(const char *name)
 	av[++ac] = buf.buf;
 	av[++ac] = NULL;
 	setup_revisions(ac, av, &revs, NULL);
-	revs.mailmap = &mailmap;
+	string_list_init_nodup(mailmap);
+	revs.mailmap = mailmap;
 	read_mailmap(revs.mailmap);
 
 	if (prepare_revision_walk(&revs))
@@ -1119,7 +1120,6 @@ static const char *find_author_by_nickname(const char *name)
 		ctx.date_mode.type = DATE_NORMAL;
 		strbuf_release(&buf);
 		format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
-		clear_mailmap(&mailmap);
 		release_revisions(&revs);
 		return strbuf_detach(&buf, NULL);
 	}
diff --git a/revision.c b/revision.c
index 475901d77e3..2050a691683 100644
--- a/revision.c
+++ b/revision.c
@@ -2926,12 +2926,21 @@ static void release_revisions_commit_list(struct rev_info *revs)
 	revs->commits = NULL;
 }
 
+static void release_revisions_mailmap(struct string_list *mailmap)
+{
+	if (!mailmap)
+		return;
+	clear_mailmap(mailmap);
+	free(mailmap);
+}
+
 void release_revisions(struct rev_info *revs)
 {
 	if (!revs)
 		return;
 	release_revisions_commit_list(revs);
 	object_array_clear(&revs->pending);
+	release_revisions_mailmap(revs->mailmap);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
diff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh
index 2630e756dab..752aa8c9454 100755
--- a/t/t0056-git-C.sh
+++ b/t/t0056-git-C.sh
@@ -2,6 +2,7 @@
 
 test_description='"-C <path>" option and its effects on other path-related options'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success '"git -C <path>" runs git from the directory <path>' '
diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh
index 741e0803c1a..73048d0a526 100755
--- a/t/t4055-diff-context.sh
+++ b/t/t4055-diff-context.sh
@@ -5,6 +5,7 @@
 
 test_description='diff.context configuration'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t4066-diff-emit-delay.sh b/t/t4066-diff-emit-delay.sh
index a1de63b77f8..0ecb3915412 100755
--- a/t/t4066-diff-emit-delay.sh
+++ b/t/t4066-diff-emit-delay.sh
@@ -4,6 +4,7 @@ test_description='test combined/stat/moved interaction'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # This test covers a weird 3-way interaction between "--cc -p", which will run
diff --git a/t/t7008-filter-branch-null-sha1.sh b/t/t7008-filter-branch-null-sha1.sh
index 9ba9f24ad2f..0ce8fd2c895 100755
--- a/t/t7008-filter-branch-null-sha1.sh
+++ b/t/t7008-filter-branch-null-sha1.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='filter-branch removal of trees with null sha1'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup: base commits' '
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 17/24] revisions API: have release_revisions() release "cmdline"
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (15 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 16/24] revisions API: have release_revisions() release "mailmap" Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 19:19   ` Derrick Stolee
  2022-03-09 13:16 ` [PATCH 18/24] revisions API: have release_revisions() release "grep_filter" Ævar Arnfjörð Bjarmason
                   ` (8 subsequent siblings)
  25 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"cmdline" in the "struct ref_info". This in combination with a
preceding change to free "commits" and "mailmap" means that we can
whitelist another test under "TEST_PASSES_SANITIZE_LEAK=true".

There was a proposal in [1] to do away with xstrdup()-ing this
add_rev_cmdline(), perhaps that would be worthwhile, but for now let's
just free() it.

We could also make that a "char *" in "struct rev_cmdline_entry"
itself, but since we own it let's expose it as a constant to outside
callers. I proposed that in [2] but have since changed my mind. See
14d30cdfc04 (ref-filter: fix memory leak in `free_array_item()`,
2019-07-10), c514c62a4fd (checkout: fix leak of non-existent branch
names, 2020-08-14) and other log history hits for "free((char *)" for
prior art.

This includes the tests we had false-positive passes on before my
6798b08e848 (perl Git.pm: don't ignore signalled failure in
_cmd_close(), 2022-02-01), now they pass for real.

Since there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to
list those that don't pass than to touch most of those 66. So let's
introduce a "TEST_FAILS_SANITIZE_LEAK=true", which if set in the tests
won't cause lib-git-svn.sh to set "TEST_PASSES_SANITIZE_LEAK=true.

This change also marks all the tests that we removed
"TEST_FAILS_SANITIZE_LEAK=true" from in an earlier commit due to
removing the UNLEAK() from cmd_format_patch(), we can now assert that
its API use doesn't leak any "struct rev_info" memory.

This change also made commit "t5503-tagfollow.sh" pass on current
master, but that would regress when combined with
ps/fetch-atomic-fixup's de004e848a9 (t5503: simplify setup of test
which exercises failure of backfill, 2022-03-03) (through no fault of
that topic, that change started using "git clone" in the test, which
has an outstanding leak). Let's leave that test out for now to avoid
in-flight semantic conflicts.

1. https://lore.kernel.org/git/YUj%2FgFRh6pwrZalY@carlos-mbp.lan/
2. https://lore.kernel.org/git/87o88obkb1.fsf@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>

fixup with bc0e0439040?
---
 revision.c                                       | 12 ++++++++++++
 t/lib-git-svn.sh                                 |  4 ++++
 t/t0062-revision-walking.sh                      |  1 +
 t/t0101-at-syntax.sh                             |  2 ++
 t/t1060-object-corruption.sh                     |  2 ++
 t/t2015-checkout-unborn.sh                       |  1 +
 t/t3408-rebase-multi-line.sh                     |  1 +
 t/t4207-log-decoration-colors.sh                 |  1 +
 t/t4212-log-corrupt.sh                           |  1 +
 t/t5313-pack-bounds-checks.sh                    |  2 ++
 t/t5322-pack-objects-sparse.sh                   |  1 +
 t/t5506-remote-groups.sh                         |  1 +
 t/t5513-fetch-track.sh                           |  1 +
 t/t5515-fetch-merge-logic.sh                     |  1 +
 t/t5518-fetch-exit-status.sh                     |  1 +
 t/t5532-fetch-proxy.sh                           |  2 ++
 t/t5900-repo-selection.sh                        |  2 ++
 t/t6002-rev-list-bisect.sh                       |  1 +
 t/t6003-rev-list-topo-order.sh                   |  1 +
 t/t6018-rev-list-glob.sh                         |  1 +
 t/t6019-rev-list-ancestry-path.sh                |  1 +
 t/t6100-rev-list-in-order.sh                     |  1 +
 t/t6101-rev-parse-parents.sh                     |  1 +
 t/t6114-keep-packs.sh                            |  2 ++
 t/t7702-repack-cyclic-alternate.sh               |  2 ++
 t/t9100-git-svn-basic.sh                         |  1 +
 t/t9101-git-svn-props.sh                         |  2 ++
 t/t9104-git-svn-follow-parent.sh                 |  2 ++
 t/t9106-git-svn-commit-diff-clobber.sh           |  2 ++
 t/t9122-git-svn-author.sh                        |  2 ++
 t/t9129-git-svn-i18n-commitencoding.sh           |  1 +
 t/t9139-git-svn-non-utf8-commitencoding.sh       |  1 +
 t/t9148-git-svn-propset.sh                       |  1 +
 t/t9151-svn-mergeinfo.sh                         |  1 +
 t/t9157-git-svn-fetch-merge.sh                   |  2 ++
 t/t9159-git-svn-no-parent-mergeinfo.sh           |  2 ++
 t/t9161-git-svn-mergeinfo-push.sh                |  1 +
 t/t9162-git-svn-dcommit-interactive.sh           |  2 ++
 t/t9163-git-svn-reset-clears-caches.sh           |  1 +
 t/t9165-git-svn-fetch-merge-branch-of-branch.sh  |  2 ++
 t/t9166-git-svn-fetch-merge-branch-of-branch2.sh |  2 ++
 41 files changed, 71 insertions(+)

diff --git a/revision.c b/revision.c
index 2050a691683..6c83a715dee 100644
--- a/revision.c
+++ b/revision.c
@@ -2926,6 +2926,17 @@ static void release_revisions_commit_list(struct rev_info *revs)
 	revs->commits = NULL;
 }
 
+static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)
+{
+	unsigned int i;
+
+	if (!cmdline)
+		return;
+	for (i = 0; i < cmdline->nr; i++)
+		free((char *)cmdline->rev[i].name);
+	FREE_AND_NULL(cmdline->rev);
+}
+
 static void release_revisions_mailmap(struct string_list *mailmap)
 {
 	if (!mailmap)
@@ -2940,6 +2951,7 @@ void release_revisions(struct rev_info *revs)
 		return;
 	release_revisions_commit_list(revs);
 	object_array_clear(&revs->pending);
+	release_revisions_cmdline(&revs->cmdline);
 	release_revisions_mailmap(revs->mailmap);
 }
 
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 2fde2353fd3..ea28971e8ee 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -1,3 +1,7 @@
+if test -z "$TEST_FAILS_SANITIZE_LEAK"
+then
+	TEST_PASSES_SANITIZE_LEAK=true
+fi
 . ./test-lib.sh
 
 if test -n "$NO_SVN_TESTS"
diff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh
index 8e215867b8c..b9480c81781 100755
--- a/t/t0062-revision-walking.sh
+++ b/t/t0062-revision-walking.sh
@@ -5,6 +5,7 @@
 
 test_description='Test revision walking api'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 cat >run_twice_expected <<-EOF
diff --git a/t/t0101-at-syntax.sh b/t/t0101-at-syntax.sh
index a1998b558f9..878aadd64c9 100755
--- a/t/t0101-at-syntax.sh
+++ b/t/t0101-at-syntax.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='various @{whatever} syntax tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh
index bc89371f534..5b8e47e346c 100755
--- a/t/t1060-object-corruption.sh
+++ b/t/t1060-object-corruption.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='see how we handle various forms of corruption'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # convert "1234abcd" to ".git/objects/12/34abcd"
diff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh
index a9721215fab..9425aae6395 100755
--- a/t/t2015-checkout-unborn.sh
+++ b/t/t2015-checkout-unborn.sh
@@ -4,6 +4,7 @@ test_description='checkout from unborn branch'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh
index cde3562e3a6..7b4607d72f2 100755
--- a/t/t3408-rebase-multi-line.sh
+++ b/t/t3408-rebase-multi-line.sh
@@ -5,6 +5,7 @@ test_description='rebasing a commit with multi-line first paragraph.'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh
index b8709424981..36ac6aff1e4 100755
--- a/t/t4207-log-decoration-colors.sh
+++ b/t/t4207-log-decoration-colors.sh
@@ -8,6 +8,7 @@ test_description='Test for "git log --decorate" colors'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh
index 0244888a5a7..30a219894bb 100755
--- a/t/t4212-log-corrupt.sh
+++ b/t/t4212-log-corrupt.sh
@@ -2,6 +2,7 @@
 
 test_description='git log with invalid commit headers'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh
index 535313e4dc8..cc4cfaa9d37 100755
--- a/t/t5313-pack-bounds-checks.sh
+++ b/t/t5313-pack-bounds-checks.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='bounds-checking of access to mmapped on-disk file formats'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 clear_base () {
diff --git a/t/t5322-pack-objects-sparse.sh b/t/t5322-pack-objects-sparse.sh
index d39958c066d..770695c9278 100755
--- a/t/t5322-pack-objects-sparse.sh
+++ b/t/t5322-pack-objects-sparse.sh
@@ -4,6 +4,7 @@ test_description='pack-objects object selection using sparse algorithm'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup repo' '
diff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh
index 8f150c0793e..5bac03ede81 100755
--- a/t/t5506-remote-groups.sh
+++ b/t/t5506-remote-groups.sh
@@ -4,6 +4,7 @@ test_description='git remote group handling'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 mark() {
diff --git a/t/t5513-fetch-track.sh b/t/t5513-fetch-track.sh
index 65d1e05bd62..c46c4dbaefc 100755
--- a/t/t5513-fetch-track.sh
+++ b/t/t5513-fetch-track.sh
@@ -2,6 +2,7 @@
 
 test_description='fetch follows remote-tracking branches correctly'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh
index 320d26796d2..c100a809c5e 100755
--- a/t/t5515-fetch-merge-logic.sh
+++ b/t/t5515-fetch-merge-logic.sh
@@ -14,6 +14,7 @@ export GIT_TEST_PROTOCOL_VERSION
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 build_script () {
diff --git a/t/t5518-fetch-exit-status.sh b/t/t5518-fetch-exit-status.sh
index 5c4ac2556e7..c13120088fa 100755
--- a/t/t5518-fetch-exit-status.sh
+++ b/t/t5518-fetch-exit-status.sh
@@ -8,6 +8,7 @@ test_description='fetch exit status test'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh
index 9c2798603b4..d664912799b 100755
--- a/t/t5532-fetch-proxy.sh
+++ b/t/t5532-fetch-proxy.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='fetching via git:// using core.gitproxy'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup remote repo' '
diff --git a/t/t5900-repo-selection.sh b/t/t5900-repo-selection.sh
index 14e59c5b3e4..a84faac242d 100755
--- a/t/t5900-repo-selection.sh
+++ b/t/t5900-repo-selection.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='selecting remote repo in ambiguous cases'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 reset() {
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index b95a0212adf..162cf50778d 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -4,6 +4,7 @@
 #
 test_description='Tests git rev-list --bisect functionality'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
 
diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh
index 24d1836f417..1f7d7dd20c1 100755
--- a/t/t6003-rev-list-topo-order.sh
+++ b/t/t6003-rev-list-topo-order.sh
@@ -5,6 +5,7 @@
 
 test_description='Tests git rev-list --topo-order functionality'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
 
diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
index 24b34add833..e1abc5c2b32 100755
--- a/t/t6018-rev-list-glob.sh
+++ b/t/t6018-rev-list-glob.sh
@@ -5,6 +5,7 @@ test_description='rev-list/rev-parse --glob'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 commit () {
diff --git a/t/t6019-rev-list-ancestry-path.sh b/t/t6019-rev-list-ancestry-path.sh
index af57a04b7ff..66f787606ec 100755
--- a/t/t6019-rev-list-ancestry-path.sh
+++ b/t/t6019-rev-list-ancestry-path.sh
@@ -24,6 +24,7 @@ test_description='--ancestry-path'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_merge () {
diff --git a/t/t6100-rev-list-in-order.sh b/t/t6100-rev-list-in-order.sh
index e934bc239c5..88ed7bd75a7 100755
--- a/t/t6100-rev-list-in-order.sh
+++ b/t/t6100-rev-list-in-order.sh
@@ -2,6 +2,7 @@
 
 test_description='rev-list testing in-commit-order'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a commit history with trees, blobs' '
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
index c571fa51797..a3a41c7a3e4 100755
--- a/t/t6101-rev-parse-parents.sh
+++ b/t/t6101-rev-parse-parents.sh
@@ -8,6 +8,7 @@ test_description='Test git rev-parse with different parent options'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_cmp_rev_output () {
diff --git a/t/t6114-keep-packs.sh b/t/t6114-keep-packs.sh
index 9239d8aa46d..44246f8a63e 100755
--- a/t/t6114-keep-packs.sh
+++ b/t/t6114-keep-packs.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='rev-list with .keep packs'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t7702-repack-cyclic-alternate.sh b/t/t7702-repack-cyclic-alternate.sh
index 93b74867ac8..f3cdb98eec2 100755
--- a/t/t7702-repack-cyclic-alternate.sh
+++ b/t/t7702-repack-cyclic-alternate.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='repack involving cyclic alternate'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index fea41b3c360..7c5b847f584 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -8,6 +8,7 @@ test_description='git svn basic tests'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 prepare_utf8_locale
diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
index 8b5681dd68a..d043e80fc34 100755
--- a/t/t9101-git-svn-props.sh
+++ b/t/t9101-git-svn-props.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn property tests'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 mkdir import
diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
index c7d8e0bf00f..5cf2ef4b8b0 100755
--- a/t/t9104-git-svn-follow-parent.sh
+++ b/t/t9104-git-svn-follow-parent.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn fetching'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
index aec45bca3b7..3cab0b9720a 100755
--- a/t/t9106-git-svn-commit-diff-clobber.sh
+++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -2,6 +2,8 @@
 #
 # Copyright (c) 2006 Eric Wong
 test_description='git svn commit-diff clobber'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh
index 9e8fe38e7ef..527ba3d2932 100755
--- a/t/t9122-git-svn-author.sh
+++ b/t/t9122-git-svn-author.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='git svn authorship'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup svn repository' '
diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh
index 01e1e8a8f76..185248a4cd7 100755
--- a/t/t9129-git-svn-i18n-commitencoding.sh
+++ b/t/t9129-git-svn-i18n-commitencoding.sh
@@ -4,6 +4,7 @@
 
 test_description='git svn honors i18n.commitEncoding in config'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 compare_git_head_with () {
diff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh
index 22d80b0be2b..b7f756b2b7f 100755
--- a/t/t9139-git-svn-non-utf8-commitencoding.sh
+++ b/t/t9139-git-svn-non-utf8-commitencoding.sh
@@ -4,6 +4,7 @@
 
 test_description='git svn refuses to dcommit non-UTF8 messages'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 # ISO-2022-JP can pass for valid UTF-8, so skipping that in this test
diff --git a/t/t9148-git-svn-propset.sh b/t/t9148-git-svn-propset.sh
index aebb28995e5..6cc76a07b39 100755
--- a/t/t9148-git-svn-propset.sh
+++ b/t/t9148-git-svn-propset.sh
@@ -5,6 +5,7 @@
 
 test_description='git svn propset tests'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup propset via import' '
diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh
index c93a5beab25..85221d439bd 100755
--- a/t/t9151-svn-mergeinfo.sh
+++ b/t/t9151-svn-mergeinfo.sh
@@ -5,6 +5,7 @@
 
 test_description='git-svn svn mergeinfo properties'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'load svn dump' "
diff --git a/t/t9157-git-svn-fetch-merge.sh b/t/t9157-git-svn-fetch-merge.sh
index 991d2aa1be6..4d8cfdf8446 100755
--- a/t/t9157-git-svn-fetch-merge.sh
+++ b/t/t9157-git-svn-fetch-merge.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn merge detection'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 svn_ver="$(svn --version --quiet)"
diff --git a/t/t9159-git-svn-no-parent-mergeinfo.sh b/t/t9159-git-svn-no-parent-mergeinfo.sh
index 69e4815781c..ce42487a507 100755
--- a/t/t9159-git-svn-no-parent-mergeinfo.sh
+++ b/t/t9159-git-svn-no-parent-mergeinfo.sh
@@ -1,5 +1,7 @@
 #!/bin/sh
 test_description='git svn handling of root commits in merge ranges'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 svn_ver="$(svn --version --quiet)"
diff --git a/t/t9161-git-svn-mergeinfo-push.sh b/t/t9161-git-svn-mergeinfo-push.sh
index f113acaa6c2..89f2518685c 100755
--- a/t/t9161-git-svn-mergeinfo-push.sh
+++ b/t/t9161-git-svn-mergeinfo-push.sh
@@ -6,6 +6,7 @@
 
 test_description='git-svn svn mergeinfo propagation'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'load svn dump' "
diff --git a/t/t9162-git-svn-dcommit-interactive.sh b/t/t9162-git-svn-dcommit-interactive.sh
index e38d9fa37b5..e2aa8ed88a9 100755
--- a/t/t9162-git-svn-dcommit-interactive.sh
+++ b/t/t9162-git-svn-dcommit-interactive.sh
@@ -3,6 +3,8 @@
 # Copyright (c) 2011 Frédéric Heitzmann
 
 test_description='git svn dcommit --interactive series'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9163-git-svn-reset-clears-caches.sh b/t/t9163-git-svn-reset-clears-caches.sh
index 978eb62ff4f..e457cf28153 100755
--- a/t/t9163-git-svn-reset-clears-caches.sh
+++ b/t/t9163-git-svn-reset-clears-caches.sh
@@ -7,6 +7,7 @@ test_description='git svn reset clears memoized caches'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 svn_ver="$(svn --version --quiet)"
diff --git a/t/t9165-git-svn-fetch-merge-branch-of-branch.sh b/t/t9165-git-svn-fetch-merge-branch-of-branch.sh
index a4813c2b09c..58b736dc132 100755
--- a/t/t9165-git-svn-fetch-merge-branch-of-branch.sh
+++ b/t/t9165-git-svn-fetch-merge-branch-of-branch.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn merge detection'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 svn_ver="$(svn --version --quiet)"
diff --git a/t/t9166-git-svn-fetch-merge-branch-of-branch2.sh b/t/t9166-git-svn-fetch-merge-branch-of-branch2.sh
index 52f2e46a5b4..9f7deb26d0e 100755
--- a/t/t9166-git-svn-fetch-merge-branch-of-branch2.sh
+++ b/t/t9166-git-svn-fetch-merge-branch-of-branch2.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn merge detection'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 svn_ver="$(svn --version --quiet)"
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 18/24] revisions API: have release_revisions() release "grep_filter"
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (16 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 17/24] revisions API: have release_revisions() release "cmdline" Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 13:16 ` [PATCH 19/24] revisions API: have release_revisions() release "prune_data" Ævar Arnfjörð Bjarmason
                   ` (7 subsequent siblings)
  25 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"grep_filter" in the "struct ref_info".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/revision.c b/revision.c
index 6c83a715dee..f51a60b2a96 100644
--- a/revision.c
+++ b/revision.c
@@ -2953,6 +2953,7 @@ void release_revisions(struct rev_info *revs)
 	object_array_clear(&revs->pending);
 	release_revisions_cmdline(&revs->cmdline);
 	release_revisions_mailmap(revs->mailmap);
+	free_grep_patterns(&revs->grep_filter);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 19/24] revisions API: have release_revisions() release "prune_data"
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (17 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 18/24] revisions API: have release_revisions() release "grep_filter" Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 13:16 ` [PATCH 20/24] revisions API: clear "boundary_commits" in release_revisions() Ævar Arnfjörð Bjarmason
                   ` (6 subsequent siblings)
  25 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"prune_data" in the "struct ref_info". This means that any code that
calls "release_revisions()" already can get rid of adjacent calls to
clear_pathspec().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 add-interactive.c | 2 --
 builtin/add.c     | 1 -
 builtin/stash.c   | 2 --
 diff-lib.c        | 1 -
 revision.c        | 1 +
 wt-status.c       | 2 --
 6 files changed, 1 insertion(+), 8 deletions(-)

diff --git a/add-interactive.c b/add-interactive.c
index 13d5ec6f49f..bd612270001 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -566,8 +566,6 @@ static int get_modified_files(struct repository *r,
 			run_diff_files(&rev, 0);
 		}
 
-		if (ps)
-			clear_pathspec(&rev.prune_data);
 		release_revisions(&rev);
 	}
 	hashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);
diff --git a/builtin/add.c b/builtin/add.c
index 115a26ea633..fc729e14c17 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -142,7 +142,6 @@ int add_files_to_cache(const char *prefix,
 	rev.diffopt.flags.override_submodule_config = 1;
 	rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
 	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 	return !!data.add_errors;
 }
diff --git a/builtin/stash.c b/builtin/stash.c
index b9fe6f7c712..53bfa82eeb1 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1072,7 +1072,6 @@ static int check_changes_tracked_files(const struct pathspec *ps)
 	}
 
 done:
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 	return ret;
 }
@@ -1284,7 +1283,6 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
 
 done:
 	discard_index(&istate);
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 	strbuf_release(&diff_output);
 	remove_path(stash_index_path.buf);
diff --git a/diff-lib.c b/diff-lib.c
index 68858a39964..255abb4959f 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -641,7 +641,6 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
 
 	if (diff_cache(&revs, tree_oid, NULL, 1))
 		exit(128);
-	clear_pathspec(&revs.prune_data);
 	release_revisions(&revs);
 	return 0;
 }
diff --git a/revision.c b/revision.c
index f51a60b2a96..290700ea66f 100644
--- a/revision.c
+++ b/revision.c
@@ -2952,6 +2952,7 @@ void release_revisions(struct rev_info *revs)
 	release_revisions_commit_list(revs);
 	object_array_clear(&revs->pending);
 	release_revisions_cmdline(&revs->cmdline);
+	clear_pathspec(&revs->prune_data);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
 }
diff --git a/wt-status.c b/wt-status.c
index aaef10451f3..4e6b2ca9b82 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -616,7 +616,6 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
 	rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_files(&rev, 0);
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 }
 
@@ -655,7 +654,6 @@ static void wt_status_collect_changes_index(struct wt_status *s)
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_index(&rev, 1);
 	release_revisions(&rev);
-	clear_pathspec(&rev.prune_data);
 }
 
 static int add_file_to_list(const struct object_id *oid,
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 20/24] revisions API: clear "boundary_commits" in release_revisions()
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (18 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 19/24] revisions API: have release_revisions() release "prune_data" Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 19:21   ` Derrick Stolee
  2022-03-09 13:16 ` [PATCH 21/24] revisions API: release "reflog_info" in release revisions() Ævar Arnfjörð Bjarmason
                   ` (5 subsequent siblings)
  25 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

Clear the "boundary_commits" object_array in release_revisions(). This
makes a *lot* of tests pass under SANITIZE=leak, including most of the
t/t[0-9]*git-svn*.sh tests.

This includes the tests we had false-positive passes on before my
6798b08e848 (perl Git.pm: don't ignore signalled failure in
_cmd_close(), 2022-02-01), now they pass for real.

Since there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to
list those that don't pass than to touch most of those 66. So let's
introduce a "TEST_FAILS_SANITIZE_LEAK=true", which if set in the tests
won't cause lib-git-svn.sh to set "TEST_PASSES_SANITIZE_LEAK=true.

This change also marks all the tests that we removed
"TEST_FAILS_SANITIZE_LEAK=true" from in an earlier commit due to
removing the UNLEAK() from cmd_format_patch(), we can now assert that
its API use doesn't leak any "struct rev_info" memory.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c                           | 1 +
 t/t4021-format-patch-numbered.sh     | 1 +
 t/t4028-format-patch-mime-headers.sh | 2 ++
 t/t4036-format-patch-signer-mime.sh  | 1 +
 t/t4122-apply-symlink-inside.sh      | 1 +
 t/t4126-apply-empty.sh               | 1 +
 t/t6110-rev-list-sparse.sh           | 1 +
 t/t9001-send-email.sh                | 1 +
 8 files changed, 9 insertions(+)

diff --git a/revision.c b/revision.c
index 290700ea66f..a73e76bed4a 100644
--- a/revision.c
+++ b/revision.c
@@ -2952,6 +2952,7 @@ void release_revisions(struct rev_info *revs)
 	release_revisions_commit_list(revs);
 	object_array_clear(&revs->pending);
 	release_revisions_cmdline(&revs->cmdline);
+	object_array_clear(&revs->boundary_commits);
 	clear_pathspec(&revs->prune_data);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
diff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh
index 9be65fd4440..1219aa226dc 100755
--- a/t/t4021-format-patch-numbered.sh
+++ b/t/t4021-format-patch-numbered.sh
@@ -5,6 +5,7 @@
 
 test_description='Format-patch numbering options'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4028-format-patch-mime-headers.sh b/t/t4028-format-patch-mime-headers.sh
index 204ba673cb5..60cb819c42e 100755
--- a/t/t4028-format-patch-mime-headers.sh
+++ b/t/t4028-format-patch-mime-headers.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='format-patch mime headers and extra headers do not conflict'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create commit with utf-8 body' '
diff --git a/t/t4036-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh
index 98d9713d8b2..48655bcc789 100755
--- a/t/t4036-format-patch-signer-mime.sh
+++ b/t/t4036-format-patch-signer-mime.sh
@@ -2,6 +2,7 @@
 
 test_description='format-patch -s should force MIME encoding as needed'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh
index aa52de401b9..96965373036 100755
--- a/t/t4122-apply-symlink-inside.sh
+++ b/t/t4122-apply-symlink-inside.sh
@@ -4,6 +4,7 @@ test_description='apply to deeper directory without getting fooled with symlink'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh
index 66a7ba8ab8f..ece9fae207d 100755
--- a/t/t4126-apply-empty.sh
+++ b/t/t4126-apply-empty.sh
@@ -2,6 +2,7 @@
 
 test_description='apply empty'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t6110-rev-list-sparse.sh b/t/t6110-rev-list-sparse.sh
index 13c1da53528..ddefc7f24ee 100755
--- a/t/t6110-rev-list-sparse.sh
+++ b/t/t6110-rev-list-sparse.sh
@@ -4,6 +4,7 @@ test_description='operations that cull histories in unusual ways'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 84d0f40d76a..dfa6b20f7a6 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -4,6 +4,7 @@ test_description='git send-email'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # May be altered later in the test
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 21/24] revisions API: release "reflog_info" in release revisions()
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (19 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 20/24] revisions API: clear "boundary_commits" in release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 19:24   ` Derrick Stolee
  2022-03-09 13:16 ` [PATCH 22/24] revisions API: call diff_free(&revs->pruning) in revisions_release() Ævar Arnfjörð Bjarmason
                   ` (4 subsequent siblings)
  25 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

Add a missing reflog_walk_info_release() to "reflog-walk.c" and use it
in release_revisions().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 reflog-walk.c            | 26 ++++++++++++++++++++++++--
 reflog-walk.h            |  1 +
 revision.c               |  1 +
 t/t0100-previous.sh      |  1 +
 t/t1401-symbolic-ref.sh  |  2 ++
 t/t1411-reflog-show.sh   |  1 +
 t/t1412-reflog-loop.sh   |  2 ++
 t/t1415-worktree-refs.sh |  1 +
 8 files changed, 33 insertions(+), 2 deletions(-)

diff --git a/reflog-walk.c b/reflog-walk.c
index 8ac4b284b6b..4322228d122 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -7,7 +7,7 @@
 #include "reflog-walk.h"
 
 struct complete_reflogs {
-	char *ref;
+	const char *ref;
 	const char *short_ref;
 	struct reflog_info {
 		struct object_id ooid, noid;
@@ -50,10 +50,17 @@ static void free_complete_reflog(struct complete_reflogs *array)
 		free(array->items[i].message);
 	}
 	free(array->items);
-	free(array->ref);
+	free((char *)array->ref);
+	free((char *)array->short_ref);
 	free(array);
 }
 
+static void complete_reflogs_clear(void *util, const char *str)
+{
+	struct complete_reflogs *array = util;
+	free_complete_reflog(array);
+}
+
 static struct complete_reflogs *read_complete_reflog(const char *ref)
 {
 	struct complete_reflogs *reflogs =
@@ -116,6 +123,21 @@ void init_reflog_walk(struct reflog_walk_info **info)
 	(*info)->complete_reflogs.strdup_strings = 1;
 }
 
+void reflog_walk_info_release(struct reflog_walk_info *info)
+{
+	size_t i;
+
+	if (!info)
+		return;
+
+	for (i = 0; i < info->nr; i++)
+		free(info->logs[i]);
+	string_list_clear_func(&info->complete_reflogs,
+			       complete_reflogs_clear);
+	free(info->logs);
+	free(info);
+}
+
 int add_reflog_for_walk(struct reflog_walk_info *info,
 		struct commit *commit, const char *name)
 {
diff --git a/reflog-walk.h b/reflog-walk.h
index e9e00ffd479..8076f10d9fb 100644
--- a/reflog-walk.h
+++ b/reflog-walk.h
@@ -8,6 +8,7 @@ struct reflog_walk_info;
 struct date_mode;
 
 void init_reflog_walk(struct reflog_walk_info **info);
+void reflog_walk_info_release(struct reflog_walk_info *info);
 int add_reflog_for_walk(struct reflog_walk_info *info,
 			struct commit *commit, const char *name);
 void show_reflog_message(struct reflog_walk_info *info, int,
diff --git a/revision.c b/revision.c
index a73e76bed4a..ec6cd662467 100644
--- a/revision.c
+++ b/revision.c
@@ -2956,6 +2956,7 @@ void release_revisions(struct rev_info *revs)
 	clear_pathspec(&revs->prune_data);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
+	reflog_walk_info_release(revs->reflog_info);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh
index 69beb59f627..a16cc3d2983 100755
--- a/t/t0100-previous.sh
+++ b/t/t0100-previous.sh
@@ -5,6 +5,7 @@ test_description='previous branch syntax @{-n}'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'branch -d @{-1}' '
diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh
index 132a1b885ac..9fb0b90f252 100755
--- a/t/t1401-symbolic-ref.sh
+++ b/t/t1401-symbolic-ref.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='basic symbolic-ref tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # If the tests munging HEAD fail, they can break detection of
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
index 0bb319b944a..3770ceffafd 100755
--- a/t/t1411-reflog-show.sh
+++ b/t/t1411-reflog-show.sh
@@ -4,6 +4,7 @@ test_description='Test reflog display routines'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t1412-reflog-loop.sh b/t/t1412-reflog-loop.sh
index 977603f7f1c..ff30874f940 100755
--- a/t/t1412-reflog-loop.sh
+++ b/t/t1412-reflog-loop.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='reflog walk shows repeated commits again'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup commits' '
diff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh
index a3e6ea08088..3b531842dd4 100755
--- a/t/t1415-worktree-refs.sh
+++ b/t/t1415-worktree-refs.sh
@@ -2,6 +2,7 @@
 
 test_description='per-worktree refs'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 22/24] revisions API: call diff_free(&revs->pruning) in revisions_release()
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (20 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 21/24] revisions API: release "reflog_info" in release revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 13:16 ` [PATCH 23/24] revisions API: have release_revisions() release "date_mode" Ævar Arnfjörð Bjarmason
                   ` (3 subsequent siblings)
  25 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

Call diff_free() on the "pruning" member of "struct rev_info". We'll
handle the "diffopt" member of the same type some other time, doing so
will require us to untangle the "no_free" conditions I added in
e900d494dcf (diff: add an API for deferred freeing, 2021-02-11).

Even without "diffopt" addressing leaks in "pruning" makes several
tests pass under SANITIZE=leak.

This was also the last missing piece that allows us to remove the
UNLEAK() in "cmd_diff" and "cmd_diff_index", which allows us to use
those commands as a canary for general leaks in the revisions API. See
[1] for further rationale, and 886e1084d78 (builtin/: add UNLEAKs,
2017-10-01) for the commit that added the UNLEAK() there.

1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/diff-index.c                | 1 -
 builtin/diff.c                      | 1 -
 revision.c                          | 2 ++
 t/t2016-checkout-patch.sh           | 1 +
 t/t4039-diff-assume-unchanged.sh    | 1 +
 t/t4206-log-follow-harder-copies.sh | 2 ++
 t/t6131-pathspec-icase.sh           | 2 ++
 7 files changed, 8 insertions(+), 2 deletions(-)

diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 3a83183c312..7d158af6b6d 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -70,7 +70,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
 		return -1;
 	}
 	result = run_diff_index(&rev, option);
-	UNLEAK(rev);
 	result = diff_result_code(&rev.diffopt, result);
 	release_revisions(&rev);
 	return result;
diff --git a/builtin/diff.c b/builtin/diff.c
index dd48336da56..f539132ac68 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -594,7 +594,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
 	result = diff_result_code(&rev.diffopt, result);
 	if (1 < rev.diffopt.skip_stat_unmatch)
 		refresh_index_quietly();
-	UNLEAK(rev);
 	release_revisions(&rev);
 	UNLEAK(ent);
 	UNLEAK(blob);
diff --git a/revision.c b/revision.c
index ec6cd662467..81f4a36fc9d 100644
--- a/revision.c
+++ b/revision.c
@@ -2956,6 +2956,8 @@ void release_revisions(struct rev_info *revs)
 	clear_pathspec(&revs->prune_data);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
+	/* TODO (need to handle "no_free"): diff_free(&revs->diffopt) */
+	diff_free(&revs->pruning);
 	reflog_walk_info_release(revs->reflog_info);
 }
 
diff --git a/t/t2016-checkout-patch.sh b/t/t2016-checkout-patch.sh
index abfd586c32b..83c246ab0a5 100755
--- a/t/t2016-checkout-patch.sh
+++ b/t/t2016-checkout-patch.sh
@@ -2,6 +2,7 @@
 
 test_description='git checkout --patch'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./lib-patch-mode.sh
 
 test_expect_success PERL 'setup' '
diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh
index 0eb0314a8b3..78090e6852d 100755
--- a/t/t4039-diff-assume-unchanged.sh
+++ b/t/t4039-diff-assume-unchanged.sh
@@ -2,6 +2,7 @@
 
 test_description='diff with assume-unchanged entries'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # external diff has been tested in t4020-diff-external.sh
diff --git a/t/t4206-log-follow-harder-copies.sh b/t/t4206-log-follow-harder-copies.sh
index 4871a5dc92f..33ecf82c7f9 100755
--- a/t/t4206-log-follow-harder-copies.sh
+++ b/t/t4206-log-follow-harder-copies.sh
@@ -6,6 +6,8 @@
 test_description='Test --follow should always find copies hard in git log.
 
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh
 
diff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh
index 39fc3f6769b..770cce026cb 100755
--- a/t/t6131-pathspec-icase.sh
+++ b/t/t6131-pathspec-icase.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test case insensitive pathspec limiting'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 if test_have_prereq CASE_INSENSITIVE_FS
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 23/24] revisions API: have release_revisions() release "date_mode"
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (21 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 22/24] revisions API: call diff_free(&revs->pruning) in revisions_release() Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 13:16 ` [PATCH 24/24] revisions API: have release_revisions() release "topo_walk_info" Ævar Arnfjörð Bjarmason
                   ` (2 subsequent siblings)
  25 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"date_mode" in the "struct ref_info".

This uses the date_mode_release() function added in 974c919d36d (date
API: add and use a date_mode_release(), 2022-02-16). As that commit
notes "t7004-tag.sh" tests for the leaks that are being fixed
here. That test now fails "only" 44 tests, instead of the 46 it failed
before this change.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/revision.c b/revision.c
index 81f4a36fc9d..9d03e7805ff 100644
--- a/revision.c
+++ b/revision.c
@@ -2954,6 +2954,7 @@ void release_revisions(struct rev_info *revs)
 	release_revisions_cmdline(&revs->cmdline);
 	object_array_clear(&revs->boundary_commits);
 	clear_pathspec(&revs->prune_data);
+	date_mode_release(&revs->date_mode);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
 	/* TODO (need to handle "no_free"): diff_free(&revs->diffopt) */
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH 24/24] revisions API: have release_revisions() release "topo_walk_info"
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (22 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 23/24] revisions API: have release_revisions() release "date_mode" Ævar Arnfjörð Bjarmason
@ 2022-03-09 13:16 ` Ævar Arnfjörð Bjarmason
  2022-03-09 19:27   ` Derrick Stolee
  2022-03-09 14:34 ` [PATCH 00/24] revision.[ch]: add and use release_revisions() Derrick Stolee
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
  25 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 13:16 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	Ævar Arnfjörð Bjarmason

Refactor the existing reset_topo_walk() into a thin wrapper for a
release_revisions_topo_walk_info() + resetting the member to "NULL",
and call release_revisions_topo_walk_info() from release_revisions().

This fixes memory leaks that have been with us ever since
"topo_walk_info" was added to revision.[ch] in
f0d9cc4196a (revision.c: begin refactoring --topo-order logic,
2018-11-01).

Due to various other leaks this makes no tests pass in their entirety,
but e.g. before this running this on git.git:

    ./git -P log --pretty=tformat:"%P   %H | %s" --parents --full-history --topo-order -3 -- README.md

Would report under SANITIZE=leak:

    SUMMARY: LeakSanitizer: 531064 byte(s) leaked in 6 allocation(s).

Now we'll free all of that memory.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 18 ++++++++++++++----
 1 file changed, 14 insertions(+), 4 deletions(-)

diff --git a/revision.c b/revision.c
index 9d03e7805ff..5a9400667df 100644
--- a/revision.c
+++ b/revision.c
@@ -2945,6 +2945,8 @@ static void release_revisions_mailmap(struct string_list *mailmap)
 	free(mailmap);
 }
 
+static void release_revisions_topo_walk_info(struct topo_walk_info *info);
+
 void release_revisions(struct rev_info *revs)
 {
 	if (!revs)
@@ -2960,6 +2962,7 @@ void release_revisions(struct rev_info *revs)
 	/* TODO (need to handle "no_free"): diff_free(&revs->diffopt) */
 	diff_free(&revs->pruning);
 	reflog_walk_info_release(revs->reflog_info);
+	release_revisions_topo_walk_info(revs->topo_walk_info);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
@@ -3472,17 +3475,24 @@ static void compute_indegrees_to_depth(struct rev_info *revs,
 		indegree_walk_step(revs);
 }
 
-static void reset_topo_walk(struct rev_info *revs)
+static void release_revisions_topo_walk_info(struct topo_walk_info *info)
 {
-	struct topo_walk_info *info = revs->topo_walk_info;
-
+	if (!info)
+		return;
 	clear_prio_queue(&info->explore_queue);
 	clear_prio_queue(&info->indegree_queue);
 	clear_prio_queue(&info->topo_queue);
 	clear_indegree_slab(&info->indegree);
 	clear_author_date_slab(&info->author_date);
+	free(info);
+}
+
+static void reset_topo_walk(struct rev_info *revs)
+{
+	struct topo_walk_info *info = revs->topo_walk_info;
 
-	FREE_AND_NULL(revs->topo_walk_info);
+	release_revisions_topo_walk_info(info);
+	revs->topo_walk_info = NULL;
 }
 
 static void init_topo_walk(struct rev_info *revs)
-- 
2.35.1.1295.g6b025d3e231


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* Re: [PATCH 00/24] revision.[ch]: add and use release_revisions()
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (23 preceding siblings ...)
  2022-03-09 13:16 ` [PATCH 24/24] revisions API: have release_revisions() release "topo_walk_info" Ævar Arnfjörð Bjarmason
@ 2022-03-09 14:34 ` Derrick Stolee
  2022-03-09 19:30   ` Derrick Stolee
  2022-03-09 20:05   ` Junio C Hamano
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
  25 siblings, 2 replies; 252+ messages in thread
From: Derrick Stolee @ 2022-03-09 14:34 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren

On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:
> == For Junio ==
> 
> This has a trivially resolved conflict with Derrick Stolee's
> aaf251cdc5c (revision: put object filter into struct rev_info,
> 2022-03-08) currently in "seen" in builtin/rev-list.c.
> 
> The resolution is to just keep the "goto cleanup" in place of "return
> 0" in the conflicting lines, but to otherwise keep Derrick's version.
> 
> It will pass with/without SANITIZE=leak when applied to both "master"
> and "seen". I omitted one test change (described in a relevant commit
> message) due to the latter not being true (no fault of "seen", just a
> new leaking command being added to a test).

Since ds/partial-bundles will soon be updated in v4 to change the
pointer added to struct rev_info, it is even more likely that there
will be more important things to do with regards to clearing the
memory of rev_infos based on that change. It might be better to wait
for that update (coming soon) and then rebase directly on top.

Thanks,
-Stolee


^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 08/24] revisions API users: add "goto cleanup" for release_revisions()
  2022-03-09 13:16 ` [PATCH 08/24] revisions API users: add "goto cleanup" for release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-09 19:08   ` Derrick Stolee
  0 siblings, 0 replies; 252+ messages in thread
From: Derrick Stolee @ 2022-03-09 19:08 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren

On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:
> Add a release_revisions() to various users of "struct rev_list" which
> requires a minor refactoring to a "goto cleanup" pattern to use that
> function.

A straight-forward refactor. I have a couple nits on this one.

>  static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,
> diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
> index 09db2620829..19393da4e31 100644
> --- a/builtin/submodule--helper.c
> +++ b/builtin/submodule--helper.c
> @@ -1222,6 +1222,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
>  	struct strvec diff_args = STRVEC_INIT;
>  	struct rev_info rev;
>  	struct module_cb_list list = MODULE_CB_LIST_INIT;
> +	int ret = 0;

Since we initialize ret = 0 here...

> @@ -1259,9 +1262,11 @@ static int compute_summary_module_list(struct object_id *head_oid,
>  	else
>  		run_diff_files(&rev, 0);
>  	prepare_submodule_summary(info, &list);
> +	ret = 0;

... this is extraneous.

>  		die(_("unable to write %s"), get_index_file());
> -
> +	ret = (result.clean == 0);
> +cleanup:

Here (and other places) I feel the loss of this empty line
above the goto label. It just seems helpful to help see
these labels a little more clearly if they have an empty
line first, instead of immediately following a statement.

Thanks,
-Stolee

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 10/24] revisions API users: use release_revisions() in builtin/log.c
  2022-03-09 13:16 ` [PATCH 10/24] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
@ 2022-03-09 19:12   ` Derrick Stolee
  2022-03-09 21:58     ` Taylor Blau
  2022-03-09 21:53   ` Taylor Blau
  1 sibling, 1 reply; 252+ messages in thread
From: Derrick Stolee @ 2022-03-09 19:12 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren

On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:

> +static int cmd_log_deinit(int ret, struct rev_info *rev)
> +{
> +	release_revisions(rev);
> +	return ret;
> +}

This pattern of passing a return value through the helper
function is a clever way to get around adding "int ret = ...;
release(); return ret;" lines.

Thanks,
-Stolee

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 16/24] revisions API: have release_revisions() release "mailmap"
  2022-03-09 13:16 ` [PATCH 16/24] revisions API: have release_revisions() release "mailmap" Ævar Arnfjörð Bjarmason
@ 2022-03-09 19:17   ` Derrick Stolee
  0 siblings, 0 replies; 252+ messages in thread
From: Derrick Stolee @ 2022-03-09 19:17 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren

On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:

> @@ -1097,7 +1097,7 @@ static const char *find_author_by_nickname(const char *name)
>  	struct rev_info revs;
>  	struct commit *commit;
>  	struct strbuf buf = STRBUF_INIT;
> -	struct string_list mailmap = STRING_LIST_INIT_NODUP;
> +	struct string_list *mailmap = xmalloc(sizeof(struct string_list));
>  	const char *av[20];
>  	int ac = 0;
>  
> @@ -1108,7 +1108,8 @@ static const char *find_author_by_nickname(const char *name)
>  	av[++ac] = buf.buf;
>  	av[++ac] = NULL;
>  	setup_revisions(ac, av, &revs, NULL);
> -	revs.mailmap = &mailmap;
> +	string_list_init_nodup(mailmap);
> +	revs.mailmap = mailmap;
>  	read_mailmap(revs.mailmap);
>  
>  	if (prepare_revision_walk(&revs))
> @@ -1119,7 +1120,6 @@ static const char *find_author_by_nickname(const char *name)
>  		ctx.date_mode.type = DATE_NORMAL;
>  		strbuf_release(&buf);
>  		format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
> -		clear_mailmap(&mailmap);

It seems like you can completely remove the 'mailmap' variable and
instead malloc and init revs.mailmap directly.

Thanks,
-Stolee

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 17/24] revisions API: have release_revisions() release "cmdline"
  2022-03-09 13:16 ` [PATCH 17/24] revisions API: have release_revisions() release "cmdline" Ævar Arnfjörð Bjarmason
@ 2022-03-09 19:19   ` Derrick Stolee
  2022-03-10 15:10     ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 252+ messages in thread
From: Derrick Stolee @ 2022-03-09 19:19 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren

On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:
> Extend the the release_revisions() function so that it frees the
> "cmdline" in the "struct ref_info". This in combination with a
> preceding change to free "commits" and "mailmap" means that we can
> whitelist another test under "TEST_PASSES_SANITIZE_LEAK=true".

Could you consider changing your vocabulary to use "allowlist"
(with the opposite term being "denylist")?

Thanks,
-Stolee

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 20/24] revisions API: clear "boundary_commits" in release_revisions()
  2022-03-09 13:16 ` [PATCH 20/24] revisions API: clear "boundary_commits" in release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-09 19:21   ` Derrick Stolee
  2022-03-10 14:55     ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 252+ messages in thread
From: Derrick Stolee @ 2022-03-09 19:21 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren

On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:
> Clear the "boundary_commits" object_array in release_revisions(). This
> makes a *lot* of tests pass under SANITIZE=leak, including most of the
> t/t[0-9]*git-svn*.sh tests.
> 
> This includes the tests we had false-positive passes on before my
> 6798b08e848 (perl Git.pm: don't ignore signalled failure in
> _cmd_close(), 2022-02-01), now they pass for real.
> 
> Since there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to
> list those that don't pass than to touch most of those 66. So let's
> introduce a "TEST_FAILS_SANITIZE_LEAK=true", which if set in the tests
> won't cause lib-git-svn.sh to set "TEST_PASSES_SANITIZE_LEAK=true.

This paragraph perhaps belongs a few patches earlier in "revisions
API: have release_revisions() release "cmdline"", or else there was
some swap of order here.

Thanks,
-Stolee

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 21/24] revisions API: release "reflog_info" in release revisions()
  2022-03-09 13:16 ` [PATCH 21/24] revisions API: release "reflog_info" in release revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-09 19:24   ` Derrick Stolee
  2022-03-10  0:13     ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 252+ messages in thread
From: Derrick Stolee @ 2022-03-09 19:24 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren

On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:
> Add a missing reflog_walk_info_release() to "reflog-walk.c" and use it
> in release_revisions().
> 
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  reflog-walk.c            | 26 ++++++++++++++++++++++++--
>  reflog-walk.h            |  1 +
>  revision.c               |  1 +
>  t/t0100-previous.sh      |  1 +
>  t/t1401-symbolic-ref.sh  |  2 ++
>  t/t1411-reflog-show.sh   |  1 +
>  t/t1412-reflog-loop.sh   |  2 ++
>  t/t1415-worktree-refs.sh |  1 +
>  8 files changed, 33 insertions(+), 2 deletions(-)
> 
> diff --git a/reflog-walk.c b/reflog-walk.c
> index 8ac4b284b6b..4322228d122 100644
> --- a/reflog-walk.c
> +++ b/reflog-walk.c
> @@ -7,7 +7,7 @@
>  #include "reflog-walk.h"
>  
>  struct complete_reflogs {
> -	char *ref;
> +	const char *ref;
>  	const char *short_ref;

This seems like the opposite change from what I would
expect, because the 'const' implies non-ownership.

> -	free(array->ref);
> +	free((char *)array->ref);
> +	free((char *)array->short_ref);

This further makes the point that we should be keeping
non-const versions so we can clearly document ownership.

> +static void complete_reflogs_clear(void *util, const char *str)
> +{
> +	struct complete_reflogs *array = util;
> +	free_complete_reflog(array);
> +}

Is there a reason we don't do the cast inside?

	free_complete_reflog((struct complete_reflogs *)util);

Thanks,
-Stolee

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 24/24] revisions API: have release_revisions() release "topo_walk_info"
  2022-03-09 13:16 ` [PATCH 24/24] revisions API: have release_revisions() release "topo_walk_info" Ævar Arnfjörð Bjarmason
@ 2022-03-09 19:27   ` Derrick Stolee
  0 siblings, 0 replies; 252+ messages in thread
From: Derrick Stolee @ 2022-03-09 19:27 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren

On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:
> Refactor the existing reset_topo_walk() into a thin wrapper for a
> release_revisions_topo_walk_info() + resetting the member to "NULL",
> and call release_revisions_topo_walk_info() from release_revisions().
> 
> This fixes memory leaks that have been with us ever since
> "topo_walk_info" was added to revision.[ch] in
> f0d9cc4196a (revision.c: begin refactoring --topo-order logic,
> 2018-11-01).

Excellent.

> +static void reset_topo_walk(struct rev_info *revs)
> +{
> +	struct topo_walk_info *info = revs->topo_walk_info;

Why add this variable?
  
> -	FREE_AND_NULL(revs->topo_walk_info);
> +	release_revisions_topo_walk_info(info);

For only this use?

	release_revisions_topo_walk_info(revs->topo_walk_info);

seems to work just fine. Is the line too long?

Thanks,
-Stolee

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 00/24] revision.[ch]: add and use release_revisions()
  2022-03-09 14:34 ` [PATCH 00/24] revision.[ch]: add and use release_revisions() Derrick Stolee
@ 2022-03-09 19:30   ` Derrick Stolee
  2022-03-09 19:39     ` Ævar Arnfjörð Bjarmason
  2022-03-09 20:05   ` Junio C Hamano
  1 sibling, 1 reply; 252+ messages in thread
From: Derrick Stolee @ 2022-03-09 19:30 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren

On 3/9/2022 9:34 AM, Derrick Stolee wrote:
> On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:
>> == For Junio ==
>>
>> This has a trivially resolved conflict with Derrick Stolee's
>> aaf251cdc5c (revision: put object filter into struct rev_info,
>> 2022-03-08) currently in "seen" in builtin/rev-list.c.
>>
>> The resolution is to just keep the "goto cleanup" in place of "return
>> 0" in the conflicting lines, but to otherwise keep Derrick's version.
>>
>> It will pass with/without SANITIZE=leak when applied to both "master"
>> and "seen". I omitted one test change (described in a relevant commit
>> message) due to the latter not being true (no fault of "seen", just a
>> new leaking command being added to a test).
> 
> Since ds/partial-bundles will soon be updated in v4 to change the
> pointer added to struct rev_info, it is even more likely that there
> will be more important things to do with regards to clearing the
> memory of rev_infos based on that change. It might be better to wait
> for that update (coming soon) and then rebase directly on top.

I took a look at the series as it stands now and have a few nits
here and there. Generally, things are pretty standard in this kind
of series you've been working diligently on for a while.

The only thing I can recommend is to check that your leak-check
statements are still true when reaching the end of the series, now
that the filter member exists. Likely the tests that you are marking
as leak-free do nothing with object filters, so they will still be
true. Just something to keep in mind and maybe add a patch that
recursively frees the contents of 'revs->filter' at the end.

Thanks,
-Stolee

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 00/24] revision.[ch]: add and use release_revisions()
  2022-03-09 19:30   ` Derrick Stolee
@ 2022-03-09 19:39     ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-09 19:39 UTC (permalink / raw)
  To: Derrick Stolee; +Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren


On Wed, Mar 09 2022, Derrick Stolee wrote:

> On 3/9/2022 9:34 AM, Derrick Stolee wrote:
>> On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:
>>> == For Junio ==
>>>
>>> This has a trivially resolved conflict with Derrick Stolee's
>>> aaf251cdc5c (revision: put object filter into struct rev_info,
>>> 2022-03-08) currently in "seen" in builtin/rev-list.c.
>>>
>>> The resolution is to just keep the "goto cleanup" in place of "return
>>> 0" in the conflicting lines, but to otherwise keep Derrick's version.
>>>
>>> It will pass with/without SANITIZE=leak when applied to both "master"
>>> and "seen". I omitted one test change (described in a relevant commit
>>> message) due to the latter not being true (no fault of "seen", just a
>>> new leaking command being added to a test).
>> 
>> Since ds/partial-bundles will soon be updated in v4 to change the
>> pointer added to struct rev_info, it is even more likely that there
>> will be more important things to do with regards to clearing the
>> memory of rev_infos based on that change. It might be better to wait
>> for that update (coming soon) and then rebase directly on top.
>
> I took a look at the series as it stands now and have a few nits
> here and there. Generally, things are pretty standard in this kind
> of series you've been working diligently on for a while.

Thanks for the review. From a quick skim I didn't see anything I needed
to address other than with an eventual re-roll, so I'll incorporate it
into a v2, pending some further feedback.

> The only thing I can recommend is to check that your leak-check
> statements are still true when reaching the end of the series, now
> that the filter member exists. Likely the tests that you are marking
> as leak-free do nothing with object filters, so they will still be
> true. Just something to keep in mind and maybe add a patch that
> recursively frees the contents of 'revs->filter' at the end.

*nod*. For what it's worth I tested this with 'git rebase -x' and a
local patch I have that allows you to run the tests in a mode where it
asserts that there's a 1=1 mapping between tests marked for the
linux-leaks job, and those that currently pass. I.e. a passing test that
isn't opted-in via TEST_PASSES_SANITIZE_LEAK=true will fail if it passes
with SANITIZE=leak.

I'll run the v2 with that again, and will lean on the side of just
ejecting any changes that step on the toes of other in-flight topics.

I.e. as noted in the cover letter this isn't all of the revisions.[ch]
leaks, just most of them, so I can always eject a few and leave those
for a follow-up series (which I already have patches for, but those are
all a bit more complex, so I left them out for now).


^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 00/24] revision.[ch]: add and use release_revisions()
  2022-03-09 14:34 ` [PATCH 00/24] revision.[ch]: add and use release_revisions() Derrick Stolee
  2022-03-09 19:30   ` Derrick Stolee
@ 2022-03-09 20:05   ` Junio C Hamano
  1 sibling, 0 replies; 252+ messages in thread
From: Junio C Hamano @ 2022-03-09 20:05 UTC (permalink / raw)
  To: Derrick Stolee
  Cc: Ævar Arnfjörð Bjarmason, git, Martin Ågren,
	Elijah Newren

Derrick Stolee <derrickstolee@github.com> writes:

> On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:
>> == For Junio ==
>> 
>> This has a trivially resolved conflict with Derrick Stolee's
>> aaf251cdc5c (revision: put object filter into struct rev_info,
>> 2022-03-08) currently in "seen" in builtin/rev-list.c.
>> 
>> The resolution is to just keep the "goto cleanup" in place of "return
>> 0" in the conflicting lines, but to otherwise keep Derrick's version.
>> 
>> It will pass with/without SANITIZE=leak when applied to both "master"
>> and "seen". I omitted one test change (described in a relevant commit
>> message) due to the latter not being true (no fault of "seen", just a
>> new leaking command being added to a test).
>
> Since ds/partial-bundles will soon be updated in v4 to change the
> pointer added to struct rev_info, it is even more likely that there
> will be more important things to do with regards to clearing the
> memory of rev_infos based on that change. It might be better to wait
> for that update (coming soon) and then rebase directly on top.

;-)

I just saw both of these topics involved, and indeed the
interactions need to be cleaned up.

Thanks, both, for working on these.


^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 03/24] format-patch: don't leak "extra_headers" or "ref_message_ids"
  2022-03-09 13:16 ` [PATCH 03/24] format-patch: don't leak "extra_headers" or "ref_message_ids" Ævar Arnfjörð Bjarmason
@ 2022-03-09 20:34   ` Taylor Blau
  0 siblings, 0 replies; 252+ messages in thread
From: Taylor Blau @ 2022-03-09 20:34 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren,
	Derrick Stolee

On Wed, Mar 09, 2022 at 02:16:33PM +0100, Ævar Arnfjörð Bjarmason wrote:
> @@ -1946,7 +1947,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
>  		strbuf_addch(&buf, '\n');
>  	}
>
> -	rev.extra_headers = strbuf_detach(&buf, NULL);
> +	extra_headers = strbuf_detach(&buf, NULL);
> +	rev.extra_headers = extra_headers;

Small nit, these two assignments could be combined on the same line
without wrapping. But obviously not a big deal.

> @@ -2173,8 +2175,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
>  		prepare_bases(&bases, base, list, nr);
>  	}
>
> -	if (in_reply_to || thread || cover_letter)
> -		rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
> +	if (in_reply_to || thread || cover_letter) {
> +		rev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));
> +		string_list_init_nodup(rev.ref_message_ids);
> +	}

OK, and this is the "while we're at it..." part of your commit message.
I did find this a little confusing to read at first, so wouldn't have
minded to see this and the rest of the change split across two patches,
but I think this is fine, too.

> @@ -2281,6 +2285,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
>  	strbuf_release(&rdiff1);
>  	strbuf_release(&rdiff2);
>  	strbuf_release(&rdiff_title);
> +	free(extra_headers);
> +	if (rev.ref_message_ids) {
> +		string_list_clear(rev.ref_message_ids, 0);

I was surprised to learn that string_list_clear() is not a noop when its
first argument is NULL.

Looking around, it probably wouldn't help that much. E.g., running
something like:

    git grep -B1 'string_list_clear(' | grep if -A1

doesn't turn up many results like the above. So maybe some good
#leftoverbits there, although maybe not.

> +		free(rev.ref_message_ids);

This could go in or out of the if-statement.

Thanks,
Taylor

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 05/24] revision.[ch]: provide and start using a release_revisions()
  2022-03-09 13:16 ` [PATCH 05/24] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-09 21:32   ` Taylor Blau
  2022-03-09 22:57   ` Junio C Hamano
  1 sibling, 0 replies; 252+ messages in thread
From: Taylor Blau @ 2022-03-09 21:32 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren,
	Derrick Stolee

On Wed, Mar 09, 2022 at 02:16:35PM +0100, Ævar Arnfjörð Bjarmason wrote:
> Let's provide a release_revisions() function for these users, and
> start migrating them over per the plan outlined in [1]. Right now this
> only handles the "pending" member of the struct, but more will be
> added in subsequent commits.

I could not be more excited about this change! The lack of easy
mechanism to free everything in the rev_info structure has been a major
impediment for me to get much value out of the SANITIZE=leak builds, and
this is a huge step towards making those more useful.

This plan looks good to me, as does this first patch to introduce the
new API.

Should it be documented anywhere in e.g.,
Documentation/MyFirstObjectWalk.txt or elsewhere?

Thanks,
Taylor

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 10/24] revisions API users: use release_revisions() in builtin/log.c
  2022-03-09 13:16 ` [PATCH 10/24] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
  2022-03-09 19:12   ` Derrick Stolee
@ 2022-03-09 21:53   ` Taylor Blau
  1 sibling, 0 replies; 252+ messages in thread
From: Taylor Blau @ 2022-03-09 21:53 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren,
	Derrick Stolee

On Wed, Mar 09, 2022 at 02:16:40PM +0100, Ævar Arnfjörð Bjarmason wrote:
> In the case of "format-patch" revert the addition of UNLEAK() in
> dee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16), and
> which will cause several tests that previously passed under
> "TEST_PASSES_SANITIZE_LEAK=true" to start failing.

Thanks for calling this out. When I skimmed the diff first, I wasn't
sure whether the change in t4126 was intentional or not. But this is
helpful context to indicate that it was.

> @@ -731,8 +737,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
>  			ret = error(_("unknown type: %d"), o->type);
>  		}
>  	}
> -	free(objects);
> -	return ret;
> +	return cmd_log_deinit(ret, &rev);
>  }

Here `objects` points at `rev.pending.objects`, and that is now freed
along the cmd_log_deinit() -> release_revision() -> object_array_clear()
path.

Makes sense, and the rest looks good, too.

Thanks,
Taylor

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 10/24] revisions API users: use release_revisions() in builtin/log.c
  2022-03-09 19:12   ` Derrick Stolee
@ 2022-03-09 21:58     ` Taylor Blau
  0 siblings, 0 replies; 252+ messages in thread
From: Taylor Blau @ 2022-03-09 21:58 UTC (permalink / raw)
  To: Derrick Stolee
  Cc: Ævar Arnfjörð Bjarmason, git, Junio C Hamano,
	Martin Ågren, Elijah Newren

On Wed, Mar 09, 2022 at 02:12:15PM -0500, Derrick Stolee wrote:
> On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:
>
> > +static int cmd_log_deinit(int ret, struct rev_info *rev)
> > +{
> > +	release_revisions(rev);
> > +	return ret;
> > +}
>
> This pattern of passing a return value through the helper
> function is a clever way to get around adding "int ret = ...;
> release(); return ret;" lines.

Clever indeed. I originally thought that cmd_log_deinit() was going to
be a wrapper around release_revisions() that also freed any log-specific
bits of the rev_info structure that are used exclusively by this file.

But that is not a great pattern, since having to track which file(s) use
which field(s) of the rev_info structure sounds cumbersome, error-prone,
and fragile. So I'm glad to see that it's really just used to
temporarily hold a return value.

I wouldn't be sad to see this as a macro, either, maybe something like:

    #define LOG_TEARDOWN(ret, rev) \
        do { release_revisions(rev); return ret; } while (0);

to make it clear(er) that this has a pretty narrow single purpose. But
even that doesn't seem like a great idea, since many of the uses have
the result of a function call as the first argument, which obviously
would not work with this macro.

So it's probably fine as-is ;).

Thanks,
Taylor

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 05/24] revision.[ch]: provide and start using a release_revisions()
  2022-03-09 13:16 ` [PATCH 05/24] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
  2022-03-09 21:32   ` Taylor Blau
@ 2022-03-09 22:57   ` Junio C Hamano
  2022-03-10  0:28     ` Ævar Arnfjörð Bjarmason
  1 sibling, 1 reply; 252+ messages in thread
From: Junio C Hamano @ 2022-03-09 22:57 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index 9244827ca02..ed993383531 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
> @@ -629,7 +629,7 @@ static void show_local_changes(struct object *head,
>  	diff_setup_done(&rev.diffopt);
>  	add_pending_object(&rev, head, NULL);
>  	run_diff_index(&rev, 0);
> -	object_array_clear(&rev.pending);
> +	release_revisions(&rev);
>  }

I very much like this.

> -	object_array_clear(&rev.pending);
>  	clear_pathspec(&rev.prune_data);
> +	release_revisions(&rev);

But this makes readers wonder why .prune_data still needs a separate
call to clear.  We are making it unnecessary to clear .pending
separately, which was what made me say I very much like this in the
first place.

At least surviving clear_pathspec() now deserves an in-code comment?
We'll know when we see what release_revisions() actually does.

>  	run_diff_index(&rev, 1);
> -	object_array_clear(&rev.pending);
> -	return (rev.diffopt.flags.has_changes != 0);
> +	has_changes = rev.diffopt.flags.has_changes;
> +	release_revisions(&rev);
> +	return (has_changes != 0);

This is necessary because release_revisions(&rev) is a way to
release all resources held by rev, and .has_changes is stored
as a part of a resource that will be cleared.

> diff --git a/revision.c b/revision.c
> index 5824fdeddfd..831f2cf977b 100644
> --- a/revision.c
> +++ b/revision.c
> @@ -2926,6 +2926,13 @@ static void release_revisions_commit_list(struct rev_info *revs)
>  	revs->commits = NULL;
>  }
>  
> +void release_revisions(struct rev_info *revs)
> +{
> +	if (!revs)
> +		return;
> +	object_array_clear(&revs->pending);
> +}

Whoa.  

Earlier, we saw a code that indicates that a call to this function
will invalidate the revs->diffopt.flags.has_changes but that is not
the case, at least at this point in the series.  Is this a result of
an incorrect "rebase -i"?

Regarding the clear_pathspec() earlier we saw, it is OK to leave the
call there without any extra comment, if the plan is to first start
by introducing release_revisions() that does nothing but .pending
field.  But then the diffopt.flags.has_changes thing we saw earlier
should be postponed to a step where release_revisions() clears the
diffopt structure that is embedded in rev_info.

> @@ -2568,8 +2568,9 @@ int has_uncommitted_changes(struct repository *r,
>  
>  	diff_setup_done(&rev_info.diffopt);
>  	result = run_diff_index(&rev_info, 1);
> -	object_array_clear(&rev_info.pending);
> -	return diff_result_code(&rev_info.diffopt, result);
> +	result = diff_result_code(&rev_info.diffopt, result);
> +	release_revisions(&rev_info);
> +	return result;

Ditto.

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 21/24] revisions API: release "reflog_info" in release revisions()
  2022-03-09 19:24   ` Derrick Stolee
@ 2022-03-10  0:13     ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-10  0:13 UTC (permalink / raw)
  To: Derrick Stolee; +Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren


On Wed, Mar 09 2022, Derrick Stolee wrote:

> On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:
>> Add a missing reflog_walk_info_release() to "reflog-walk.c" and use it
>> in release_revisions().
>> 
>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
>> ---
>>  reflog-walk.c            | 26 ++++++++++++++++++++++++--
>>  reflog-walk.h            |  1 +
>>  revision.c               |  1 +
>>  t/t0100-previous.sh      |  1 +
>>  t/t1401-symbolic-ref.sh  |  2 ++
>>  t/t1411-reflog-show.sh   |  1 +
>>  t/t1412-reflog-loop.sh   |  2 ++
>>  t/t1415-worktree-refs.sh |  1 +
>>  8 files changed, 33 insertions(+), 2 deletions(-)
>> 
>> diff --git a/reflog-walk.c b/reflog-walk.c
>> index 8ac4b284b6b..4322228d122 100644
>> --- a/reflog-walk.c
>> +++ b/reflog-walk.c
>> @@ -7,7 +7,7 @@
>>  #include "reflog-walk.h"
>>  
>>  struct complete_reflogs {
>> -	char *ref;
>> +	const char *ref;
>>  	const char *short_ref;
>
> This seems like the opposite change from what I would
> expect, because the 'const' implies non-ownership.

Yes, I'll change it.

FWIW we've had recent discussions on this point & it's a bit
context-dependant. See
https://lore.kernel.org/git/patch-v2-1.1-e2a166a9733-20211021T201541Z-avarab@gmail.com/
and https://lore.kernel.org/git/xmqqlf2vbbl8.fsf@gitster.g/

I.e. the preferred pattern is:

 * Make it "char *" if it's your own private struct, because it's non-const
 * If it's a "public API" and it's not really "const char *", but we
   don't want the API user to think they can fiddle with it, make it
   "const char *" and cast it in the free().

In this case I think I just mixed those two up, or maybe I initially
wrote it before that was clarified. In this case it's our own private
struct within this file.

>> -	free(array->ref);
>> +	free((char *)array->ref);
>> +	free((char *)array->short_ref);
>
> This further makes the point that we should be keeping
> non-const versions so we can clearly document ownership.

*nod*

>> +static void complete_reflogs_clear(void *util, const char *str)
>> +{
>> +	struct complete_reflogs *array = util;
>> +	free_complete_reflog(array);
>> +}
>
> Is there a reason we don't do the cast inside?
>
> 	free_complete_reflog((struct complete_reflogs *)util);

I think we generally do that more often that not (although I should add
an extra \n) there. I.e. to immediately cast the void* into a variable.

I also find it handy when e.g. stepping through in a debugger, because
you'll have a variable you can inspect without de-referencing it every
time, and if the function uses "array" for something else in the future
it'll be less verbosity on the second use...

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 05/24] revision.[ch]: provide and start using a release_revisions()
  2022-03-09 22:57   ` Junio C Hamano
@ 2022-03-10  0:28     ` Ævar Arnfjörð Bjarmason
  2022-03-10  1:11       ` Junio C Hamano
  2022-03-10 14:56       ` Ævar Arnfjörð Bjarmason
  0 siblings, 2 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-10  0:28 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee


On Wed, Mar 09 2022, Junio C Hamano wrote:

> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
>
>> diff --git a/builtin/checkout.c b/builtin/checkout.c
>> index 9244827ca02..ed993383531 100644
>> --- a/builtin/checkout.c
>> +++ b/builtin/checkout.c
>> @@ -629,7 +629,7 @@ static void show_local_changes(struct object *head,
>>  	diff_setup_done(&rev.diffopt);
>>  	add_pending_object(&rev, head, NULL);
>>  	run_diff_index(&rev, 0);
>> -	object_array_clear(&rev.pending);
>> +	release_revisions(&rev);
>>  }
>
> I very much like this.
>
>> -	object_array_clear(&rev.pending);
>>  	clear_pathspec(&rev.prune_data);
>> +	release_revisions(&rev);
>
> But this makes readers wonder why .prune_data still needs a separate
> call to clear.  We are making it unnecessary to clear .pending
> separately, which was what made me say I very much like this in the
> first place.
>
> At least surviving clear_pathspec() now deserves an in-code comment?
> We'll know when we see what release_revisions() actually does.

I'll note it in a commit message. Generally in this series I then loop
back and remove the clear_pathspec() (or similar) once I start clearing
"prune_data", but in this case I seem to have missed this specific case
later in the series...

>>  	run_diff_index(&rev, 1);
>> -	object_array_clear(&rev.pending);
>> -	return (rev.diffopt.flags.has_changes != 0);
>> +	has_changes = rev.diffopt.flags.has_changes;
>> +	release_revisions(&rev);
>> +	return (has_changes != 0);
>
> This is necessary because release_revisions(&rev) is a way to
> release all resources held by rev, and .has_changes is stored
> as a part of a resource that will be cleared.

Yes, but...

>> diff --git a/revision.c b/revision.c
>> index 5824fdeddfd..831f2cf977b 100644
>> --- a/revision.c
>> +++ b/revision.c
>> @@ -2926,6 +2926,13 @@ static void release_revisions_commit_list(struct rev_info *revs)
>>  	revs->commits = NULL;
>>  }
>>  
>> +void release_revisions(struct rev_info *revs)
>> +{
>> +	if (!revs)
>> +		return;
>> +	object_array_clear(&revs->pending);
>> +}
>
> Whoa.  
>
> Earlier, we saw a code that indicates that a call to this function
> will invalidate the revs->diffopt.flags.has_changes but that is not
> the case, at least at this point in the series.  Is this a result of
> an incorrect "rebase -i"?
>
> Regarding the clear_pathspec() earlier we saw, it is OK to leave the
> call there without any extra comment, if the plan is to first start
> by introducing release_revisions() that does nothing but .pending
> field.  But then the diffopt.flags.has_changes thing we saw earlier
> should be postponed to a step where release_revisions() clears the
> diffopt structure that is embedded in rev_info.
>
>> @@ -2568,8 +2568,9 @@ int has_uncommitted_changes(struct repository *r,
>>  
>>  	diff_setup_done(&rev_info.diffopt);
>>  	result = run_diff_index(&rev_info, 1);
>> -	object_array_clear(&rev_info.pending);
>> -	return diff_result_code(&rev_info.diffopt, result);
>> +	result = diff_result_code(&rev_info.diffopt, result);
>> +	release_revisions(&rev_info);
>> +	return result;
>
> Ditto.

...indeed, those don't do anything yet.

I thought it better (and will explain so in a re-rolled commit message)
to start adding release_revisions() to existing API users, and not to
have those individidual users assume anything about what is and isn't
cleared yet.

Some users that end up with the release_revisions() don't need it at
all, because in those cases we just so happen not to actually allocate
anything.

But just like with strbuf_release() et al we generally just add it at
the end, and just have the destructor worry about the NOOP-ing.

So I'd prefer to keep this part of the general structure as-is,
i.e. even if we do nothing with "diffopt" *yet* we can assert that the
mere addition of the nascent release_revisions() isn't breaking anything
by merely existing as we add it to all "struct rev_info" users.

Then as we add more things to release_revisions() we get a nicely tested
before/after where we can see if/how adding new free()'s there breaks or
doesn't break.

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 05/24] revision.[ch]: provide and start using a release_revisions()
  2022-03-10  0:28     ` Ævar Arnfjörð Bjarmason
@ 2022-03-10  1:11       ` Junio C Hamano
  2022-03-10 14:57         ` Ævar Arnfjörð Bjarmason
  2022-03-10 14:56       ` Ævar Arnfjörð Bjarmason
  1 sibling, 1 reply; 252+ messages in thread
From: Junio C Hamano @ 2022-03-10  1:11 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> So I'd prefer to keep this part of the general structure as-is,
> i.e. even if we do nothing with "diffopt" *yet* we can assert ...

Please don't.

It will become hard to tell during the patch progression if "we can
do so even though we do not need to do so *yet*" is correct
(e.g. diffopt---which does not have a separate allocation to be
released), or if "pretending that the field is cleared by _release()
function is premature and will lead to a new leak" (e.g. if you lost
separate clearing of .prune_data at this step, that would be an
incorrect change because it does hold on to an allocated resource).

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 20/24] revisions API: clear "boundary_commits" in release_revisions()
  2022-03-09 19:21   ` Derrick Stolee
@ 2022-03-10 14:55     ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-10 14:55 UTC (permalink / raw)
  To: Derrick Stolee; +Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren


On Wed, Mar 09 2022, Derrick Stolee wrote:

> On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:
>> Clear the "boundary_commits" object_array in release_revisions(). This
>> makes a *lot* of tests pass under SANITIZE=leak, including most of the
>> t/t[0-9]*git-svn*.sh tests.
>> 
>> This includes the tests we had false-positive passes on before my
>> 6798b08e848 (perl Git.pm: don't ignore signalled failure in
>> _cmd_close(), 2022-02-01), now they pass for real.
>> 
>> Since there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to
>> list those that don't pass than to touch most of those 66. So let's
>> introduce a "TEST_FAILS_SANITIZE_LEAK=true", which if set in the tests
>> won't cause lib-git-svn.sh to set "TEST_PASSES_SANITIZE_LEAK=true.
>
> This paragraph perhaps belongs a few patches earlier in "revisions
> API: have release_revisions() release "cmdline"", or else there was
> some swap of order here.

Indeed, this was a bad case of a commit message being duplicated, will
fix. Sorry!

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 05/24] revision.[ch]: provide and start using a release_revisions()
  2022-03-10  0:28     ` Ævar Arnfjörð Bjarmason
  2022-03-10  1:11       ` Junio C Hamano
@ 2022-03-10 14:56       ` Ævar Arnfjörð Bjarmason
  1 sibling, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-10 14:56 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee


On Thu, Mar 10 2022, Ævar Arnfjörð Bjarmason wrote:

> On Wed, Mar 09 2022, Junio C Hamano wrote:
>
>> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
>>
>>> diff --git a/builtin/checkout.c b/builtin/checkout.c
>>> index 9244827ca02..ed993383531 100644
>>> --- a/builtin/checkout.c
>>> +++ b/builtin/checkout.c
>>> @@ -629,7 +629,7 @@ static void show_local_changes(struct object *head,
>>>  	diff_setup_done(&rev.diffopt);
>>>  	add_pending_object(&rev, head, NULL);
>>>  	run_diff_index(&rev, 0);
>>> -	object_array_clear(&rev.pending);
>>> +	release_revisions(&rev);
>>>  }
>>
>> I very much like this.
>>
>>> -	object_array_clear(&rev.pending);
>>>  	clear_pathspec(&rev.prune_data);
>>> +	release_revisions(&rev);
>>
>> But this makes readers wonder why .prune_data still needs a separate
>> call to clear.  We are making it unnecessary to clear .pending
>> separately, which was what made me say I very much like this in the
>> first place.
>>
>> At least surviving clear_pathspec() now deserves an in-code comment?
>> We'll know when we see what release_revisions() actually does.
>
> I'll note it in a commit message. Generally in this series I then loop
> back and remove the clear_pathspec() (or similar) once I start clearing
> "prune_data", but in this case I seem to have missed this specific case
> later in the series...

Just for completeness, I didn't miss a case here, but thought I did
because of the truncated diff mentioning "builtin/checkout.c",
nevermind... (but will fix the other raised issue).

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 05/24] revision.[ch]: provide and start using a release_revisions()
  2022-03-10  1:11       ` Junio C Hamano
@ 2022-03-10 14:57         ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-10 14:57 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee


On Wed, Mar 09 2022, Junio C Hamano wrote:

> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
>
>> So I'd prefer to keep this part of the general structure as-is,
>> i.e. even if we do nothing with "diffopt" *yet* we can assert ...
>
> Please don't.
>
> It will become hard to tell during the patch progression if "we can
> do so even though we do not need to do so *yet*" is correct
> (e.g. diffopt---which does not have a separate allocation to be
> released), or if "pretending that the field is cleared by _release()
> function is premature and will lead to a new leak" (e.g. if you lost
> separate clearing of .prune_data at this step, that would be an
> incorrect change because it does hold on to an allocated resource).

For a v2 I've rewritten those sorts of changes out. I've bundled them up
to a commit for "diffopt" at the end, even though whe don't free() it
yet.

Depending on what you think of that change we can either drop it or keep
it, but in either case it should be obvious what is and isn't in
anticipation of any future free() we aren't yet doing.

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH 17/24] revisions API: have release_revisions() release "cmdline"
  2022-03-09 19:19   ` Derrick Stolee
@ 2022-03-10 15:10     ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-10 15:10 UTC (permalink / raw)
  To: Derrick Stolee; +Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren


On Wed, Mar 09 2022, Derrick Stolee wrote:

> On 3/9/2022 8:16 AM, Ævar Arnfjörð Bjarmason wrote:
>> Extend the the release_revisions() function so that it frees the
>> "cmdline" in the "struct ref_info". This in combination with a
>> preceding change to free "commits" and "mailmap" means that we can
>> whitelist another test under "TEST_PASSES_SANITIZE_LEAK=true".
>
> Could you consider changing your vocabulary to use "allowlist"
> (with the opposite term being "denylist")?

I looked at some of the wording with past changes like that, which in
some cases was entirely different. See some of:

    git log --grep=SANITIZE

But I figred for the nascent v2 re-roll that avoiding the churn was
better, particularly since there's various similar phrasing on "master"
already.

^ permalink raw reply	[flat|nested] 252+ messages in thread

* [PATCH v2 00/27] revision.[ch]: add and use release_revisions()
  2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                   ` (24 preceding siblings ...)
  2022-03-09 14:34 ` [PATCH 00/24] revision.[ch]: add and use release_revisions() Derrick Stolee
@ 2022-03-23 20:31 ` Ævar Arnfjörð Bjarmason
  2022-03-23 20:31   ` [PATCH v2 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
                     ` (28 more replies)
  25 siblings, 29 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:31 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

This is a re-roll of the v1 of [1] now that 7391ecd338e (Merge branch
'ds/partial-bundles', 2022-03-21) has landed, which it had a conflict
with. I believe this v2 addresses all the feedback brought up on v1.

As the range-diff shows the v2 is a bit larger, I split up some
commits as suggested in review.

I've also rebased this locally on unsubmitted patches I have which
allow for testing that the tests we mark with SANITIZE=leak have a 1=1
correspondance with what we're marking as passing with
TEST_PASSES_SANITIZE_LEAK=true.

All of the individual commits here pass under that, so all the t/*.sh
changes are correct as far as exhaustively marking newly passing tests
with "TEST_PASSES_SANITIZE_LEAK=true" goes.

Now, as I was just about to submit this I noticed that Derrick has
submitted a follow-up series to 7391ecd338e[2]. It textually conflicts
with this one in a minor way, and the resolution is the following:

	diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
	index 5f6197a37bb..0c1d5586ed4 100644
	--- a/builtin/pack-objects.c
	+++ b/builtin/pack-objects.c
	@@ -3796,7 +3796,6 @@ static void get_object_list(struct rev_info *revs, int ac, const char **av)
	 	if (unpack_unreachable)
	 		loosen_unused_packed_objects();
	 
	-	release_revisions(&revs);
	 	oid_array_clear(&recent_objects);
	 }
	 
	@@ -4189,6 +4188,8 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
	 
	 cleanup:
	 	strvec_clear(&rp);
	+	UNLEAK(revs.diffopt);
	+	release_revisions(&revs);
	 
	 	return 0;
	 }

The UNLEAK() there is new. At the tip of this series we pass (among
many others) t5313-pack-bounds-checks.sh, t5316-pack-delta-depth.sh,
t6114-keep-packs.sh under SANITIZE=leak. When I rebase Derrick's [2]
on top of this series it requires the addition of that UNLEAK().

I didn't look in any detail (didn't have time just now) into why that
now fails exactly, we do the relevant revs.diffopt allocation before.

There's also a trivial conflict here with brian m. carlson's
bc/stash-export series. This series moves the free_stash_info()
function around, so the conflict is the following:
	
	<<<<<<< HEAD
				fprintf_ln(stderr, _("No stash entries found."));
	=======
				free_stash_info(info);
				if (!quiet)
					fprintf_ln(stderr, _("No stash entries found."));
	>>>>>>> origin/seen
	[...]
	<<<<<<< HEAD
		if (get_oid(revision, &info->w_commit))
			return error(_("%s is not a valid reference"), revision);
	=======
		if (get_oid_with_context(the_repository, revision,
					 GET_OID_QUIETLY | GET_OID_RETURN_FAILURE,
					 &info->w_commit, &unused)) {
			if (!quiet)
				error(_("%s is not a valid reference"), revision);
			free_stash_info(info);
			return -1;
		}
	>>>>>>> origin/seen

In both cases the solution is triavial: Keep brian's side, but delete
the two free_stash_info() calls, as we now call it in the invoking
function.
	
1. https://lore.kernel.org/git/cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com/
2. https://lore.kernel.org/git/pull.1186.git.1647970119.gitgitgadget@gmail.com/

Ævar Arnfjörð Bjarmason (27):
  t/helper/test-fast-rebase.c: don't leak "struct strbuf"
  blame: use "goto cleanup" for cleanup_scoreboard()
  string_list API users: use string_list_init_{no,}dup
  format-patch: don't leak "extra_headers" or "ref_message_ids"
  revision.[ch]: split freeing of revs->commit into a function
  revision.[ch]: provide and start using a release_revisions()
  revisions API users: add straightforward release_revisions()
  revisions API users: use release_revisions() needing "{ 0 }" init
  stash: always have the owner of "stash_info" free it
  revisions API users: add "goto cleanup" for release_revisions()
  revisions API users: use release_revisions() in http-push.c
  revisions API users: use release_revisions() in builtin/log.c
  revisions API users: use release_revisions() with UNLEAK()
  revisions API users: use release_revisions() in submodule.c edge case
  revisions API users: use release_revisions() for "prune_data" users
  revisions API: have release_revisions() release "commits"
  revisions API: have release_revisions() release "mailmap"
  revisions API: have release_revisions() release "cmdline"
  revisions API: have release_revisions() release "filter"
  revisions API: have release_revisions() release "grep_filter"
  revisions API: have release_revisions() release "prune_data"
  revisions API: clear "boundary_commits" in release_revisions()
  revisions API: release "reflog_info" in release revisions()
  revisions API: call diff_free(&revs->pruning) in revisions_release()
  revisions API: have release_revisions() release "date_mode"
  revisions API: have release_revisions() release "topo_walk_info"
  revisions API: add a TODO for diff_free(&revs->diffopt)

 add-interactive.c                          |  3 +-
 bisect.c                                   | 20 ++++--
 builtin/add.c                              |  3 +-
 builtin/am.c                               |  3 +
 builtin/bisect--helper.c                   |  2 +
 builtin/blame.c                            |  4 +-
 builtin/checkout.c                         |  3 +-
 builtin/commit.c                           |  6 +-
 builtin/describe.c                         |  2 +
 builtin/diff-files.c                       |  8 ++-
 builtin/diff-index.c                       |  5 +-
 builtin/diff.c                             |  2 +-
 builtin/fast-export.c                      |  1 +
 builtin/log.c                              | 36 ++++++----
 builtin/merge.c                            |  2 +
 builtin/pack-objects.c                     |  2 +
 builtin/prune.c                            |  1 +
 builtin/reflog.c                           |  1 +
 builtin/rev-list.c                         | 19 ++++--
 builtin/shortlog.c                         |  8 ++-
 builtin/stash.c                            | 77 +++++++++++-----------
 builtin/submodule--helper.c                | 14 ++--
 bundle.c                                   | 12 ++--
 diff-lib.c                                 |  8 ++-
 fmt-merge-msg.c                            |  1 +
 http-push.c                                |  3 +-
 merge-ort.c                                |  1 +
 merge-recursive.c                          |  5 +-
 midx.c                                     |  1 +
 pack-bitmap-write.c                        |  1 +
 range-diff.c                               |  2 +-
 ref-filter.c                               |  1 +
 reflog-walk.c                              | 24 ++++++-
 reflog-walk.h                              |  1 +
 remote.c                                   |  1 +
 revision.c                                 | 68 ++++++++++++++++---
 revision.h                                 |  6 ++
 sequencer.c                                | 26 ++++++--
 shallow.c                                  |  1 +
 submodule.c                                |  5 +-
 t/helper/test-fast-rebase.c                | 23 +++++--
 t/helper/test-revision-walking.c           |  1 +
 t/lib-git-svn.sh                           |  4 ++
 t/t0056-git-C.sh                           |  1 +
 t/t0062-revision-walking.sh                |  1 +
 t/t0100-previous.sh                        |  1 +
 t/t0101-at-syntax.sh                       |  2 +
 t/t1001-read-tree-m-2way.sh                |  1 +
 t/t1002-read-tree-m-u-2way.sh              |  1 +
 t/t1060-object-corruption.sh               |  2 +
 t/t1401-symbolic-ref.sh                    |  2 +
 t/t1411-reflog-show.sh                     |  1 +
 t/t1412-reflog-loop.sh                     |  2 +
 t/t1415-worktree-refs.sh                   |  1 +
 t/t2015-checkout-unborn.sh                 |  1 +
 t/t2200-add-update.sh                      |  1 +
 t/t3302-notes-index-expensive.sh           |  1 +
 t/t3303-notes-subtrees.sh                  |  1 +
 t/t3305-notes-fanout.sh                    |  1 +
 t/t3408-rebase-multi-line.sh               |  1 +
 t/t4021-format-patch-numbered.sh           |  1 +
 t/t4027-diff-submodule.sh                  |  1 +
 t/t4028-format-patch-mime-headers.sh       |  2 +
 t/t4036-format-patch-signer-mime.sh        |  1 +
 t/t4039-diff-assume-unchanged.sh           |  1 +
 t/t4055-diff-context.sh                    |  1 +
 t/t4066-diff-emit-delay.sh                 |  1 +
 t/t4122-apply-symlink-inside.sh            |  1 +
 t/t4126-apply-empty.sh                     |  1 -
 t/t4128-apply-root.sh                      |  1 +
 t/t4206-log-follow-harder-copies.sh        |  2 +
 t/t4207-log-decoration-colors.sh           |  1 +
 t/t4212-log-corrupt.sh                     |  1 +
 t/t5301-sliding-window.sh                  |  2 +
 t/t5313-pack-bounds-checks.sh              |  2 +
 t/t5316-pack-delta-depth.sh                |  2 +
 t/t5320-delta-islands.sh                   |  2 +
 t/t5322-pack-objects-sparse.sh             |  1 +
 t/t5506-remote-groups.sh                   |  1 +
 t/t5513-fetch-track.sh                     |  1 +
 t/t5515-fetch-merge-logic.sh               |  1 +
 t/t5518-fetch-exit-status.sh               |  1 +
 t/t5532-fetch-proxy.sh                     |  2 +
 t/t5600-clone-fail-cleanup.sh              |  1 +
 t/t5900-repo-selection.sh                  |  2 +
 t/t6002-rev-list-bisect.sh                 |  1 +
 t/t6003-rev-list-topo-order.sh             |  1 +
 t/t6005-rev-list-count.sh                  |  1 +
 t/t6018-rev-list-glob.sh                   |  1 +
 t/t6100-rev-list-in-order.sh               |  1 +
 t/t6101-rev-parse-parents.sh               |  1 +
 t/t6110-rev-list-sparse.sh                 |  1 +
 t/t6114-keep-packs.sh                      |  2 +
 t/t6131-pathspec-icase.sh                  |  2 +
 t/t7008-filter-branch-null-sha1.sh         |  1 +
 t/t7702-repack-cyclic-alternate.sh         |  2 +
 t/t9001-send-email.sh                      |  1 +
 t/t9100-git-svn-basic.sh                   |  1 +
 t/t9101-git-svn-props.sh                   |  2 +
 t/t9104-git-svn-follow-parent.sh           |  2 +
 t/t9106-git-svn-commit-diff-clobber.sh     |  2 +
 t/t9115-git-svn-dcommit-funky-renames.sh   |  1 +
 t/t9116-git-svn-log.sh                     |  1 +
 t/t9122-git-svn-author.sh                  |  2 +
 t/t9127-git-svn-partial-rebuild.sh         |  1 +
 t/t9129-git-svn-i18n-commitencoding.sh     |  1 +
 t/t9132-git-svn-broken-symlink.sh          |  1 +
 t/t9139-git-svn-non-utf8-commitencoding.sh |  1 +
 t/t9146-git-svn-empty-dirs.sh              |  2 +
 t/t9148-git-svn-propset.sh                 |  1 +
 t/t9160-git-svn-preserve-empty-dirs.sh     |  1 +
 t/t9162-git-svn-dcommit-interactive.sh     |  2 +
 t/t9164-git-svn-dcommit-concurrent.sh      |  2 +
 t/t9501-gitweb-standalone-http-status.sh   |  1 +
 wt-status.c                                | 15 +++--
 115 files changed, 402 insertions(+), 123 deletions(-)

Range-diff against v1:
 1:  017c67c7540 =  1:  8fbafe61053 t/helper/test-fast-rebase.c: don't leak "struct strbuf"
 2:  db0b04e53a9 =  2:  77e7c2c321e blame: use "goto cleanup" for cleanup_scoreboard()
 -:  ----------- >  3:  151d1175905 string_list API users: use string_list_init_{no,}dup
 3:  ab1afe6c0c8 !  4:  64a76629e23 format-patch: don't leak "extra_headers" or "ref_message_ids"
    @@ Commit message
         variable to hold it, to avoid the eventual cast from "const char *"
         when we free() it.
     
    -    While we're at it let's also move to using string_list_init_nodup()
    -    instead of relying on calloc() implicitly coming up with the same
    -    result. See 770fedaf9fb (string-list.[ch]: add a
    -    string_list_init_{nodup,dup}(), 2021-07-01) for the introduction of
    -    the string_list_init_nodup() helper. The string_list_clear() here is
    -    redundant due to the *_nodup() initialization, but let's add it anyway
    -    for consistency with other API use.
    -
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/log.c ##
    @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *pre
      	struct commit *commit;
      	struct commit **list = NULL;
      	struct rev_info rev;
    -+	char *extra_headers = NULL;
    ++	char *to_free = NULL;
      	struct setup_revision_opt s_r_opt;
      	int nr = 0, total, i;
      	int use_stdout = 0;
    @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *pre
      	}
      
     -	rev.extra_headers = strbuf_detach(&buf, NULL);
    -+	extra_headers = strbuf_detach(&buf, NULL);
    -+	rev.extra_headers = extra_headers;
    ++	rev.extra_headers = to_free = strbuf_detach(&buf, NULL);
      
      	if (from) {
      		if (split_ident_line(&rev.from_ident, from, strlen(from)))
    -@@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
    - 		prepare_bases(&bases, base, list, nr);
    - 	}
    - 
    --	if (in_reply_to || thread || cover_letter)
    --		rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
    -+	if (in_reply_to || thread || cover_letter) {
    -+		rev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));
    -+		string_list_init_nodup(rev.ref_message_ids);
    -+	}
    - 	if (in_reply_to) {
    - 		const char *msgid = clean_message_id(in_reply_to);
    - 		string_list_append(rev.ref_message_ids, msgid);
     @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
      	strbuf_release(&rdiff1);
      	strbuf_release(&rdiff2);
      	strbuf_release(&rdiff_title);
    -+	free(extra_headers);
    -+	if (rev.ref_message_ids) {
    ++	free(to_free);
    ++	if (rev.ref_message_ids)
     +		string_list_clear(rev.ref_message_ids, 0);
    -+		free(rev.ref_message_ids);
    -+	}
    ++	free(rev.ref_message_ids);
      	UNLEAK(rev);
      	return 0;
      }
 4:  763e0ab3425 =  5:  4c0718b43d7 revision.[ch]: split freeing of revs->commit into a function
 5:  b89dcadcc22 !  6:  7194aac88e1 revision.[ch]: provide and start using a release_revisions()
    @@ Commit message
         to clear its "diffopt". I.e. we need to call release_revisions() after
         we've inspected any state in "struct rev_info".
     
    +    This leaves in place e.g. clear_pathspec(&rev.prune_data) in
    +    stash_working_tree() in builtin/stash.c, subsequent commits will teach
    +    release_revisions() to free "prune_data" and other members that in
    +    some cases are individually cleared by users of "struct rev_info" by
    +    reaching into its members. Those subsequent commits will remove the
    +    relevant calls to e.g. clear_pathspec().
    +
    +    We avoid amending code in index_differs_from() in diff-lib.c as well
    +    as wt_status_collect_changes_index(), has_unstaged_changes() and
    +    has_uncommitted_changes() in wt-status.c in a way that assumes that we
    +    are already clearing the "diffopt" member. That will be handled in a
    +    subsequent commit.
    +
         1. https://lore.kernel.org/git/87a6k8daeu.fsf@evledraar.gmail.com/
     
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
    @@ builtin/stash.c: static int stash_working_tree(struct stash_info *info, const st
      	return ret;
     
      ## diff-lib.c ##
    -@@ diff-lib.c: int index_differs_from(struct repository *r,
    - {
    - 	struct rev_info rev;
    - 	struct setup_revision_opt opt;
    -+	unsigned int has_changes;
    - 
    - 	repo_init_revisions(r, &rev, NULL);
    - 	memset(&opt, 0, sizeof(opt));
     @@ diff-lib.c: int index_differs_from(struct repository *r,
      		diff_flags_or(&rev.diffopt.flags, flags);
      	rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
      	run_diff_index(&rev, 1);
     -	object_array_clear(&rev.pending);
    --	return (rev.diffopt.flags.has_changes != 0);
    -+	has_changes = rev.diffopt.flags.has_changes;
     +	release_revisions(&rev);
    -+	return (has_changes != 0);
    + 	return (rev.diffopt.flags.has_changes != 0);
      }
      
    - static struct strbuf *idiff_prefix_cb(struct diff_options *opt, void *data)
     
      ## range-diff.c ##
     @@ range-diff.c: int is_range_diff_range(const char *arg)
    @@ revision.h: void repo_init_revisions(struct repository *r,
     
      ## wt-status.c ##
     @@ wt-status.c: static void wt_status_collect_changes_index(struct wt_status *s)
    - 	rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
    + 
      	copy_pathspec(&rev.prune_data, &s->pathspec);
      	run_diff_index(&rev, 1);
     -	object_array_clear(&rev.pending);
    @@ wt-status.c: static void wt_status_collect_changes_index(struct wt_status *s)
      	clear_pathspec(&rev.prune_data);
      }
      
    +@@ wt-status.c: int has_unstaged_changes(struct repository *r, int ignore_submodules)
    + 	rev_info.diffopt.flags.quick = 1;
    + 	diff_setup_done(&rev_info.diffopt);
    + 	result = run_diff_files(&rev_info, 0);
    ++	release_revisions(&rev_info);
    + 	return diff_result_code(&rev_info.diffopt, result);
    + }
    + 
     @@ wt-status.c: int has_uncommitted_changes(struct repository *r,
      
      	diff_setup_done(&rev_info.diffopt);
      	result = run_diff_index(&rev_info, 1);
     -	object_array_clear(&rev_info.pending);
    --	return diff_result_code(&rev_info.diffopt, result);
    -+	result = diff_result_code(&rev_info.diffopt, result);
     +	release_revisions(&rev_info);
    -+	return result;
    + 	return diff_result_code(&rev_info.diffopt, result);
      }
      
    - /**
 6:  b5d55539b6d =  7:  3cb36d358d6 revisions API users: add straightforward release_revisions()
 7:  295fa35174f =  8:  42ad1208934 revisions API users: use release_revisions() needing "{ 0 }" init
 -:  ----------- >  9:  88289157533 stash: always have the owner of "stash_info" free it
 8:  0f9d313b338 ! 10:  eafe57663fc revisions API users: add "goto cleanup" for release_revisions()
    @@ builtin/diff-files.c: int cmd_diff_files(int argc, const char **argv, const char
     +		result = -1;
     +		goto cleanup;
      	}
    ++cleanup:
      	result = run_diff_files(&rev, options);
     -	return diff_result_code(&rev.diffopt, result);
     +	result = diff_result_code(&rev.diffopt, result);
    -+cleanup:
     +	release_revisions(&rev);
     +	return result;
      }
    @@ builtin/rev-list.c: int cmd_rev_list(int argc, const char **argv, const char *pr
     @@ builtin/rev-list.c: int cmd_rev_list(int argc, const char **argv, const char *prefix)
      
      	if (use_bitmap_index) {
    - 		if (!try_bitmap_count(&revs, &filter_options, filter_provided_objects))
    + 		if (!try_bitmap_count(&revs, filter_provided_objects))
     -			return 0;
     +			goto cleanup;
    - 		if (!try_bitmap_disk_usage(&revs, &filter_options, filter_provided_objects))
    + 		if (!try_bitmap_disk_usage(&revs, filter_provided_objects))
     -			return 0;
     +			goto cleanup;
    - 		if (!try_bitmap_traversal(&revs, &filter_options, filter_provided_objects))
    + 		if (!try_bitmap_traversal(&revs, filter_provided_objects))
     -			return 0;
     +			goto cleanup;
      	}
    @@ builtin/rev-list.c: int cmd_rev_list(int argc, const char **argv, const char *pr
     
      ## builtin/stash.c ##
     @@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *prefix)
    - 
    - 	ret = get_stash_info(&info, stash_args.nr, stash_args.v);
    + cleanup:
      	strvec_clear(&stash_args);
    --	if (ret)
    --		return -1;
    -+	if (ret) {
    -+		ret = -1;
    -+		goto cleanup;
    -+	}
    - 
    - 	/*
    - 	 * The config settings are applied only if there are not passed
    -@@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *prefix)
    - 
    - 		if (!show_stat && !show_patch) {
    - 			free_stash_info(&info);
    --			return 0;
    -+			ret = 0;
    -+			goto cleanup;
    - 		}
    - 	}
    - 
    -@@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *prefix)
    - 	log_tree_diff_flush(&rev);
    - 
      	free_stash_info(&info);
    --	return diff_result_code(&rev.diffopt, 0);
    -+	ret = diff_result_code(&rev.diffopt, 0);
    -+cleanup:
     +	release_revisions(&rev);
    -+	return ret;
    + 	return ret;
      }
      
    - static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,
     
      ## builtin/submodule--helper.c ##
     @@ builtin/submodule--helper.c: static int compute_summary_module_list(struct object_id *head_oid,
    @@ builtin/submodule--helper.c: static int compute_summary_module_list(struct objec
      	else
      		run_diff_files(&rev, 0);
      	prepare_submodule_summary(info, &list);
    -+	ret = 0;
     +cleanup:
      	strvec_clear(&diff_args);
      	release_revisions(&rev);
    @@ t/helper/test-fast-rebase.c: int cmd__fast_rebase(int argc, const char **argv)
     -	if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1)
     -		return error(_("unhandled options"));
     +	if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) {
    -+		ret =  error(_("unhandled options"));
    ++		ret = error(_("unhandled options"));
     +		goto cleanup;
     +	}
      
    @@ t/helper/test-fast-rebase.c: int cmd__fast_rebase(int argc, const char **argv)
      	init_merge_options(&merge_opt, the_repository);
      	memset(&result, 0, sizeof(result));
     @@ t/helper/test-fast-rebase.c: int cmd__fast_rebase(int argc, const char **argv)
    - 	if (write_locked_index(&the_index, &lock,
      			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
      		die(_("unable to write %s"), get_index_file());
    --
    + 
     +	ret = (result.clean == 0);
     +cleanup:
      	strbuf_release(&reflog_msg);
 9:  8f93b87f369 = 11:  6fb2caf4d17 revisions API users: use release_revisions() in http-push.c
10:  a89f0da4fd7 ! 12:  74818bc372f revisions API users: use release_revisions() in builtin/log.c
    @@ builtin/log.c: int cmd_log(int argc, const char **argv, const char *prefix)
      
      /* format-patch */
     @@ builtin/log.c: int cmd_format_patch(int argc, const char **argv, const char *prefix)
    + 	if (rev.ref_message_ids)
      		string_list_clear(rev.ref_message_ids, 0);
    - 		free(rev.ref_message_ids);
    - 	}
    + 	free(rev.ref_message_ids);
     -	UNLEAK(rev);
     -	return 0;
     +	return cmd_log_deinit(0, &rev);
11:  a93c8838257 = 13:  8717e6e8471 revisions API users: use release_revisions() with UNLEAK()
12:  8811a5ca840 = 14:  38f4f1c4ecb revisions API users: use release_revisions() in submodule.c edge case
14:  761bbd1763c = 15:  566b739ac6c revisions API users: use release_revisions() for "prune_data" users
15:  4a0adf6fd00 = 16:  bc36810e56e revisions API: have release_revisions() release "commits"
16:  1a988c96371 ! 17:  a93251edf85 revisions API: have release_revisions() release "mailmap"
    @@ builtin/commit.c: static const char *find_author_by_nickname(const char *name)
      	struct commit *commit;
      	struct strbuf buf = STRBUF_INIT;
     -	struct string_list mailmap = STRING_LIST_INIT_NODUP;
    -+	struct string_list *mailmap = xmalloc(sizeof(struct string_list));
      	const char *av[20];
      	int ac = 0;
      
    @@ builtin/commit.c: static const char *find_author_by_nickname(const char *name)
      	av[++ac] = NULL;
      	setup_revisions(ac, av, &revs, NULL);
     -	revs.mailmap = &mailmap;
    -+	string_list_init_nodup(mailmap);
    -+	revs.mailmap = mailmap;
    ++	revs.mailmap = xmalloc(sizeof(struct string_list));
    ++	string_list_init_nodup(revs.mailmap);
      	read_mailmap(revs.mailmap);
      
      	if (prepare_revision_walk(&revs))
    @@ t/t0056-git-C.sh
      
      test_expect_success '"git -C <path>" runs git from the directory <path>' '
     
    + ## t/t3302-notes-index-expensive.sh ##
    +@@ t/t3302-notes-index-expensive.sh: test_description='Test commit notes index (expensive!)'
    + GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
    + export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
    + 
    ++TEST_PASSES_SANITIZE_LEAK=true
    + . ./test-lib.sh
    + 
    + create_repo () {
    +
      ## t/t4055-diff-context.sh ##
     @@
      
    @@ t/t7008-filter-branch-null-sha1.sh
      
      test_description='filter-branch removal of trees with null sha1'
     +
    -+TEST_PASSES_SANITIZE_LEAK=true
      . ./test-lib.sh
      
      test_expect_success 'setup: base commits' '
17:  b5687ed1355 ! 18:  424fac7ab95 revisions API: have release_revisions() release "cmdline"
    @@ t/t1060-object-corruption.sh
      
      test_description='see how we handle various forms of corruption'
     +
    -+TEST_PASSES_SANITIZE_LEAK=true
      . ./test-lib.sh
      
      # convert "1234abcd" to ".git/objects/12/34abcd"
     
    - ## t/t2015-checkout-unborn.sh ##
    -@@ t/t2015-checkout-unborn.sh: test_description='checkout from unborn branch'
    + ## t/t3303-notes-subtrees.sh ##
    +@@ t/t3303-notes-subtrees.sh: test_description='Test commit notes organized in subtrees'
      GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
      export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
      
     +TEST_PASSES_SANITIZE_LEAK=true
      . ./test-lib.sh
      
    - test_expect_success 'setup' '
    + number_of_commits=100
     
    - ## t/t3408-rebase-multi-line.sh ##
    -@@ t/t3408-rebase-multi-line.sh: test_description='rebasing a commit with multi-line first paragraph.'
    - GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
    - export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
    + ## t/t3305-notes-fanout.sh ##
    +@@
    + 
    + test_description='Test that adding/removing many notes triggers automatic fanout restructuring'
      
     +TEST_PASSES_SANITIZE_LEAK=true
      . ./test-lib.sh
      
    - test_expect_success setup '
    + path_has_fanout() {
     
    - ## t/t4207-log-decoration-colors.sh ##
    -@@ t/t4207-log-decoration-colors.sh: test_description='Test for "git log --decorate" colors'
    + ## t/t3408-rebase-multi-line.sh ##
    +@@ t/t3408-rebase-multi-line.sh: test_description='rebasing a commit with multi-line first paragraph.'
      GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
      export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
      
    @@ t/t4207-log-decoration-colors.sh: test_description='Test for "git log --decorate
      
      test_expect_success setup '
     
    - ## t/t4212-log-corrupt.sh ##
    + ## t/t4027-diff-submodule.sh ##
     @@
      
    - test_description='git log with invalid commit headers'
    + test_description='difference in submodules'
      
     +TEST_PASSES_SANITIZE_LEAK=true
      . ./test-lib.sh
    + . "$TEST_DIRECTORY"/lib-diff.sh
      
    - test_expect_success 'setup' '
     
    - ## t/t5313-pack-bounds-checks.sh ##
    + ## t/t4128-apply-root.sh ##
     @@
    - #!/bin/sh
      
    - test_description='bounds-checking of access to mmapped on-disk file formats'
    -+
    -+TEST_PASSES_SANITIZE_LEAK=true
    - . ./test-lib.sh
    - 
    - clear_base () {
    -
    - ## t/t5322-pack-objects-sparse.sh ##
    -@@ t/t5322-pack-objects-sparse.sh: test_description='pack-objects object selection using sparse algorithm'
    - GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
    - export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
    - 
    -+TEST_PASSES_SANITIZE_LEAK=true
    - . ./test-lib.sh
    - 
    - test_expect_success 'setup repo' '
    -
    - ## t/t5506-remote-groups.sh ##
    -@@ t/t5506-remote-groups.sh: test_description='git remote group handling'
    - GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
    - export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
    + test_description='apply same filename'
      
     +TEST_PASSES_SANITIZE_LEAK=true
      . ./test-lib.sh
      
    - mark() {
    + test_expect_success 'setup' '
     
    - ## t/t5513-fetch-track.sh ##
    + ## t/t4212-log-corrupt.sh ##
     @@
      
    - test_description='fetch follows remote-tracking branches correctly'
    + test_description='git log with invalid commit headers'
      
     +TEST_PASSES_SANITIZE_LEAK=true
      . ./test-lib.sh
      
    - test_expect_success setup '
    + test_expect_success 'setup' '
     
      ## t/t5515-fetch-merge-logic.sh ##
     @@ t/t5515-fetch-merge-logic.sh: export GIT_TEST_PROTOCOL_VERSION
    @@ t/t5518-fetch-exit-status.sh: test_description='fetch exit status test'
      
      test_expect_success setup '
     
    - ## t/t5532-fetch-proxy.sh ##
    -@@
    - #!/bin/sh
    - 
    - test_description='fetching via git:// using core.gitproxy'
    -+
    -+TEST_PASSES_SANITIZE_LEAK=true
    - . ./test-lib.sh
    - 
    - test_expect_success 'setup remote repo' '
    -
    - ## t/t5900-repo-selection.sh ##
    -@@
    - #!/bin/sh
    - 
    - test_description='selecting remote repo in ambiguous cases'
    -+
    -+TEST_PASSES_SANITIZE_LEAK=true
    - . ./test-lib.sh
    - 
    - reset() {
    -
      ## t/t6002-rev-list-bisect.sh ##
     @@
      #
    @@ t/t6003-rev-list-topo-order.sh
      . "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
      
     
    - ## t/t6018-rev-list-glob.sh ##
    -@@ t/t6018-rev-list-glob.sh: test_description='rev-list/rev-parse --glob'
    - GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
    - export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
    + ## t/t6005-rev-list-count.sh ##
    +@@
    + 
    + test_description='git rev-list --max-count and --skip test'
      
     +TEST_PASSES_SANITIZE_LEAK=true
      . ./test-lib.sh
      
    - commit () {
    + test_expect_success 'setup' '
     
    - ## t/t6019-rev-list-ancestry-path.sh ##
    -@@ t/t6019-rev-list-ancestry-path.sh: test_description='--ancestry-path'
    + ## t/t6018-rev-list-glob.sh ##
    +@@ t/t6018-rev-list-glob.sh: test_description='rev-list/rev-parse --glob'
      GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
      export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
      
     +TEST_PASSES_SANITIZE_LEAK=true
      . ./test-lib.sh
      
    - test_merge () {
    + commit () {
     
      ## t/t6100-rev-list-in-order.sh ##
     @@
    @@ t/t6100-rev-list-in-order.sh
      
      test_expect_success 'setup a commit history with trees, blobs' '
     
    - ## t/t6101-rev-parse-parents.sh ##
    -@@ t/t6101-rev-parse-parents.sh: test_description='Test git rev-parse with different parent options'
    - GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
    - export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
    - 
    -+TEST_PASSES_SANITIZE_LEAK=true
    - . ./test-lib.sh
    - 
    - test_cmp_rev_output () {
    -
    - ## t/t6114-keep-packs.sh ##
    -@@
    - #!/bin/sh
    - 
    - test_description='rev-list with .keep packs'
    -+
    -+TEST_PASSES_SANITIZE_LEAK=true
    - . ./test-lib.sh
    - 
    - test_expect_success 'setup' '
    -
    - ## t/t7702-repack-cyclic-alternate.sh ##
    -@@
    - #
    - 
    - test_description='repack involving cyclic alternate'
    -+
    -+TEST_PASSES_SANITIZE_LEAK=true
    - . ./test-lib.sh
    - 
    - test_expect_success setup '
    -
      ## t/t9100-git-svn-basic.sh ##
     @@ t/t9100-git-svn-basic.sh: test_description='git svn basic tests'
      GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
    @@ t/t9106-git-svn-commit-diff-clobber.sh
      
      test_expect_success 'initialize repo' '
     
    + ## t/t9115-git-svn-dcommit-funky-renames.sh ##
    +@@
    + 
    + test_description='git svn dcommit can commit renames of files with ugly names'
    + 
    ++TEST_FAILS_SANITIZE_LEAK=true
    + . ./lib-git-svn.sh
    + 
    + test_expect_success 'load repository with strange names' '
    +
    + ## t/t9116-git-svn-log.sh ##
    +@@
    + #
    + 
    + test_description='git svn log tests'
    ++
    ++TEST_FAILS_SANITIZE_LEAK=true
    + . ./lib-git-svn.sh
    + 
    + test_expect_success 'setup repository and import' '
    +
      ## t/t9122-git-svn-author.sh ##
     @@
      #!/bin/sh
    @@ t/t9122-git-svn-author.sh
      
      test_expect_success 'setup svn repository' '
     
    - ## t/t9129-git-svn-i18n-commitencoding.sh ##
    + ## t/t9127-git-svn-partial-rebuild.sh ##
     @@
    + #
      
    - test_description='git svn honors i18n.commitEncoding in config'
    - 
    + test_description='git svn partial-rebuild tests'
    ++
     +TEST_FAILS_SANITIZE_LEAK=true
      . ./lib-git-svn.sh
      
    - compare_git_head_with () {
    + test_expect_success 'initialize svnrepo' '
     
    - ## t/t9139-git-svn-non-utf8-commitencoding.sh ##
    + ## t/t9129-git-svn-i18n-commitencoding.sh ##
     @@
      
    - test_description='git svn refuses to dcommit non-UTF8 messages'
    + test_description='git svn honors i18n.commitEncoding in config'
      
     +TEST_FAILS_SANITIZE_LEAK=true
      . ./lib-git-svn.sh
      
    - # ISO-2022-JP can pass for valid UTF-8, so skipping that in this test
    + compare_git_head_with () {
     
    - ## t/t9148-git-svn-propset.sh ##
    + ## t/t9132-git-svn-broken-symlink.sh ##
     @@
      
    - test_description='git svn propset tests'
    + test_description='test that git handles an svn repository with empty symlinks'
      
     +TEST_FAILS_SANITIZE_LEAK=true
      . ./lib-git-svn.sh
    - 
    - test_expect_success 'setup propset via import' '
    + test_expect_success 'load svn dumpfile' '
    + 	svnadmin load "$rawsvnrepo" <<EOF
     
    - ## t/t9151-svn-mergeinfo.sh ##
    + ## t/t9139-git-svn-non-utf8-commitencoding.sh ##
     @@
      
    - test_description='git-svn svn mergeinfo properties'
    + test_description='git svn refuses to dcommit non-UTF8 messages'
      
     +TEST_FAILS_SANITIZE_LEAK=true
      . ./lib-git-svn.sh
      
    - test_expect_success 'load svn dump' "
    + # ISO-2022-JP can pass for valid UTF-8, so skipping that in this test
     
    - ## t/t9157-git-svn-fetch-merge.sh ##
    + ## t/t9146-git-svn-empty-dirs.sh ##
     @@
    - #
    + # Copyright (c) 2009 Eric Wong
      
    - test_description='git svn merge detection'
    + test_description='git svn creates empty directories'
     +
     +TEST_FAILS_SANITIZE_LEAK=true
      . ./lib-git-svn.sh
      
    - svn_ver="$(svn --version --quiet)"
    + test_expect_success 'initialize repo' '
     
    - ## t/t9159-git-svn-no-parent-mergeinfo.sh ##
    + ## t/t9148-git-svn-propset.sh ##
     @@
    - #!/bin/sh
    - test_description='git svn handling of root commits in merge ranges'
    -+
    + 
    + test_description='git svn propset tests'
    + 
     +TEST_FAILS_SANITIZE_LEAK=true
      . ./lib-git-svn.sh
      
    - svn_ver="$(svn --version --quiet)"
    + test_expect_success 'setup propset via import' '
     
    - ## t/t9161-git-svn-mergeinfo-push.sh ##
    + ## t/t9151-svn-mergeinfo.sh ##
     @@
      
    - test_description='git-svn svn mergeinfo propagation'
    + test_description='git-svn svn mergeinfo properties'
      
     +TEST_FAILS_SANITIZE_LEAK=true
      . ./lib-git-svn.sh
      
      test_expect_success 'load svn dump' "
     
    + ## t/t9160-git-svn-preserve-empty-dirs.sh ##
    +@@ t/t9160-git-svn-preserve-empty-dirs.sh: This test uses git to clone a Subversion repository that contains empty
    + directories, and checks that corresponding directories are created in the
    + local Git repository with placeholder files.'
    + 
    ++TEST_FAILS_SANITIZE_LEAK=true
    + . ./lib-git-svn.sh
    + 
    + GIT_REPO=git-svn-repo
    +
      ## t/t9162-git-svn-dcommit-interactive.sh ##
     @@
      # Copyright (c) 2011 Frédéric Heitzmann
    @@ t/t9162-git-svn-dcommit-interactive.sh
      
      test_expect_success 'initialize repo' '
     
    - ## t/t9163-git-svn-reset-clears-caches.sh ##
    -@@ t/t9163-git-svn-reset-clears-caches.sh: test_description='git svn reset clears memoized caches'
    - GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
    - export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
    - 
    -+TEST_FAILS_SANITIZE_LEAK=true
    - . ./lib-git-svn.sh
    - 
    - svn_ver="$(svn --version --quiet)"
    -
    - ## t/t9165-git-svn-fetch-merge-branch-of-branch.sh ##
    + ## t/t9164-git-svn-dcommit-concurrent.sh ##
     @@
      #
      
    - test_description='git svn merge detection'
    + test_description='concurrent git svn dcommit'
     +
     +TEST_FAILS_SANITIZE_LEAK=true
      . ./lib-git-svn.sh
      
    - svn_ver="$(svn --version --quiet)"
    + 
     
    - ## t/t9166-git-svn-fetch-merge-branch-of-branch2.sh ##
    -@@
    - #
    + ## t/t9501-gitweb-standalone-http-status.sh ##
    +@@ t/t9501-gitweb-standalone-http-status.sh: code and message.'
    + GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
    + export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
      
    - test_description='git svn merge detection'
    -+
    -+TEST_FAILS_SANITIZE_LEAK=true
    - . ./lib-git-svn.sh
    ++TEST_PASSES_SANITIZE_LEAK=true
    + . ./lib-gitweb.sh
      
    - svn_ver="$(svn --version --quiet)"
    + #
 -:  ----------- > 19:  d5b7a8a690e revisions API: have release_revisions() release "filter"
18:  fc486e1bc46 ! 20:  857880bb4b6 revisions API: have release_revisions() release "grep_filter"
    @@ Commit message
         revisions API: have release_revisions() release "grep_filter"
     
         Extend the the release_revisions() function so that it frees the
    -    "grep_filter" in the "struct ref_info".
    +    "grep_filter" in the "struct ref_info".This allows us to mark a test
    +    as passing under "TEST_PASSES_SANITIZE_LEAK=true".
     
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## revision.c ##
     @@ revision.c: void release_revisions(struct rev_info *revs)
    - 	object_array_clear(&revs->pending);
      	release_revisions_cmdline(&revs->cmdline);
    + 	list_objects_filter_release(&revs->filter);
      	release_revisions_mailmap(revs->mailmap);
     +	free_grep_patterns(&revs->grep_filter);
      }
      
      static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
    +
    + ## t/t9151-svn-mergeinfo.sh ##
    +@@
    + 
    + test_description='git-svn svn mergeinfo properties'
    + 
    +-TEST_FAILS_SANITIZE_LEAK=true
    + . ./lib-git-svn.sh
    + 
    + test_expect_success 'load svn dump' "
19:  b5948be0d4e ! 21:  f98d473f08e revisions API: have release_revisions() release "prune_data"
    @@ diff-lib.c: int do_diff_cache(const struct object_id *tree_oid, struct diff_opti
     
      ## revision.c ##
     @@ revision.c: void release_revisions(struct rev_info *revs)
    - 	release_revisions_commit_list(revs);
      	object_array_clear(&revs->pending);
      	release_revisions_cmdline(&revs->cmdline);
    + 	list_objects_filter_release(&revs->filter);
     +	clear_pathspec(&revs->prune_data);
      	release_revisions_mailmap(revs->mailmap);
      	free_grep_patterns(&revs->grep_filter);
20:  fa53e81c7c0 ! 22:  827430315fa revisions API: clear "boundary_commits" in release_revisions()
    @@ Commit message
         revisions API: clear "boundary_commits" in release_revisions()
     
         Clear the "boundary_commits" object_array in release_revisions(). This
    -    makes a *lot* of tests pass under SANITIZE=leak, including most of the
    -    t/t[0-9]*git-svn*.sh tests.
    +    makes a few more tests pass under SANITIZE=leak, including
    +    "t/t4126-apply-empty.sh" which started failed as an UNLEAK() in
    +    cmd_format_patch() was removed in a preceding commit.
     
    -    This includes the tests we had false-positive passes on before my
    -    6798b08e848 (perl Git.pm: don't ignore signalled failure in
    -    _cmd_close(), 2022-02-01), now they pass for real.
    -
    -    Since there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to
    -    list those that don't pass than to touch most of those 66. So let's
    -    introduce a "TEST_FAILS_SANITIZE_LEAK=true", which if set in the tests
    -    won't cause lib-git-svn.sh to set "TEST_PASSES_SANITIZE_LEAK=true.
    -
    -    This change also marks all the tests that we removed
    -    "TEST_FAILS_SANITIZE_LEAK=true" from in an earlier commit due to
    -    removing the UNLEAK() from cmd_format_patch(), we can now assert that
    -    its API use doesn't leak any "struct rev_info" memory.
    +    This also re-marks the various tests relying on "git format-patch" as
    +    passing under "SANITIZE=leak", in the preceding "revisions API users:
    +    use release_revisions() in builtin/log.c" commit those were marked as
    +    failing as we removed the UNLEAK(rev) from cmd_format_patch() in
    +    "builtin/log.c".
     
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## revision.c ##
     @@ revision.c: void release_revisions(struct rev_info *revs)
    + 		return;
      	release_revisions_commit_list(revs);
      	object_array_clear(&revs->pending);
    - 	release_revisions_cmdline(&revs->cmdline);
     +	object_array_clear(&revs->boundary_commits);
    + 	release_revisions_cmdline(&revs->cmdline);
    + 	list_objects_filter_release(&revs->filter);
      	clear_pathspec(&revs->prune_data);
    - 	release_revisions_mailmap(revs->mailmap);
    - 	free_grep_patterns(&revs->grep_filter);
     
      ## t/t4021-format-patch-numbered.sh ##
     @@
    @@ t/t9001-send-email.sh: test_description='git send-email'
      . ./test-lib.sh
      
      # May be altered later in the test
    +
    + ## t/t9116-git-svn-log.sh ##
    +@@
    + 
    + test_description='git svn log tests'
    + 
    +-TEST_FAILS_SANITIZE_LEAK=true
    + . ./lib-git-svn.sh
    + 
    + test_expect_success 'setup repository and import' '
21:  ccf276641d6 ! 23:  c559b7d538b revisions API: release "reflog_info" in release revisions()
    @@ Commit message
     
      ## reflog-walk.c ##
     @@
    - #include "reflog-walk.h"
      
      struct complete_reflogs {
    --	char *ref;
    -+	const char *ref;
    - 	const char *short_ref;
    + 	char *ref;
    +-	const char *short_ref;
    ++	char *short_ref;
      	struct reflog_info {
      		struct object_id ooid, noid;
    + 		char *email;
     @@ reflog-walk.c: static void free_complete_reflog(struct complete_reflogs *array)
    - 		free(array->items[i].message);
      	}
      	free(array->items);
    --	free(array->ref);
    -+	free((char *)array->ref);
    -+	free((char *)array->short_ref);
    + 	free(array->ref);
    ++	free(array->short_ref);
      	free(array);
      }
      
22:  f387b9e744a ! 24:  c3a22f5b474 revisions API: call diff_free(&revs->pruning) in revisions_release()
    @@ Metadata
      ## Commit message ##
         revisions API: call diff_free(&revs->pruning) in revisions_release()
     
    -    Call diff_free() on the "pruning" member of "struct rev_info". We'll
    -    handle the "diffopt" member of the same type some other time, doing so
    -    will require us to untangle the "no_free" conditions I added in
    -    e900d494dcf (diff: add an API for deferred freeing, 2021-02-11).
    -
    -    Even without "diffopt" addressing leaks in "pruning" makes several
    -    tests pass under SANITIZE=leak.
    +    Call diff_free() on the "pruning" member of "struct rev_info".  Doing
    +    so makes several tests pass under SANITIZE=leak.
     
         This was also the last missing piece that allows us to remove the
         UNLEAK() in "cmd_diff" and "cmd_diff_index", which allows us to use
    @@ revision.c: void release_revisions(struct rev_info *revs)
      	clear_pathspec(&revs->prune_data);
      	release_revisions_mailmap(revs->mailmap);
      	free_grep_patterns(&revs->grep_filter);
    -+	/* TODO (need to handle "no_free"): diff_free(&revs->diffopt) */
     +	diff_free(&revs->pruning);
      	reflog_walk_info_release(revs->reflog_info);
      }
      
     
    - ## t/t2016-checkout-patch.sh ##
    -@@
    + ## t/t1001-read-tree-m-2way.sh ##
    +@@ t/t1001-read-tree-m-2way.sh: In the test, these paths are used:
    + 	yomin   - not in H or M
    + '
    + 
    ++TEST_PASSES_SANITIZE_LEAK=true
    + . ./test-lib.sh
    + . "$TEST_DIRECTORY"/lib-read-tree.sh
      
    - test_description='git checkout --patch'
    +
    + ## t/t1002-read-tree-m-u-2way.sh ##
    +@@ t/t1002-read-tree-m-u-2way.sh: This is identical to t1001, but uses -u to update the work tree as well.
    + 
    + '
      
     +TEST_PASSES_SANITIZE_LEAK=true
    - . ./lib-patch-mode.sh
    + . ./test-lib.sh
    + . "$TEST_DIRECTORY"/lib-read-tree.sh
    + 
    +
    + ## t/t2200-add-update.sh ##
    +@@ t/t2200-add-update.sh: only the updates to dir/sub.
    + Also tested are "git add -u" without limiting, and "git add -u"
    + without contents changes, and other conditions'
    + 
    ++TEST_PASSES_SANITIZE_LEAK=true
    + . ./test-lib.sh
      
    - test_expect_success PERL 'setup' '
    + test_expect_success setup '
     
      ## t/t4039-diff-assume-unchanged.sh ##
     @@
23:  fd457f0d85c ! 25:  dabbb430ece revisions API: have release_revisions() release "date_mode"
    @@ Commit message
      ## revision.c ##
     @@ revision.c: void release_revisions(struct rev_info *revs)
      	release_revisions_cmdline(&revs->cmdline);
    - 	object_array_clear(&revs->boundary_commits);
    + 	list_objects_filter_release(&revs->filter);
      	clear_pathspec(&revs->prune_data);
     +	date_mode_release(&revs->date_mode);
      	release_revisions_mailmap(revs->mailmap);
      	free_grep_patterns(&revs->grep_filter);
    - 	/* TODO (need to handle "no_free"): diff_free(&revs->diffopt) */
    + 	diff_free(&revs->pruning);
24:  e02a097b884 ! 26:  644d1b54afe revisions API: have release_revisions() release "topo_walk_info"
    @@ revision.c: static void release_revisions_mailmap(struct string_list *mailmap)
      {
      	if (!revs)
     @@ revision.c: void release_revisions(struct rev_info *revs)
    - 	/* TODO (need to handle "no_free"): diff_free(&revs->diffopt) */
    + 	free_grep_patterns(&revs->grep_filter);
      	diff_free(&revs->pruning);
      	reflog_walk_info_release(revs->reflog_info);
     +	release_revisions_topo_walk_info(revs->topo_walk_info);
    @@ revision.c: static void compute_indegrees_to_depth(struct rev_info *revs,
      	clear_author_date_slab(&info->author_date);
     +	free(info);
     +}
    -+
    -+static void reset_topo_walk(struct rev_info *revs)
    -+{
    -+	struct topo_walk_info *info = revs->topo_walk_info;
      
     -	FREE_AND_NULL(revs->topo_walk_info);
    -+	release_revisions_topo_walk_info(info);
    ++static void reset_topo_walk(struct rev_info *revs)
    ++{
    ++	release_revisions_topo_walk_info(revs->topo_walk_info);
     +	revs->topo_walk_info = NULL;
      }
      
13:  d18bfa5691f ! 27:  2a4d50a0ec8 revisions API users: use release_revisions() in wt-status.c edge case
    @@ Metadata
     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## Commit message ##
    -    revisions API users: use release_revisions() in wt-status.c edge case
    +    revisions API: add a TODO for diff_free(&revs->diffopt)
     
    -    Use release_revisions() for the "struct rev_info" in
    -    has_unstaged_changes() which didn't require the addition of a "goto"
    -    pattern, but needed us to refactor another "return fn(...)" pattern.
    +    Add a TODO comment indicating that we should release "diffopt" in
    +    release_revisions(). In a preceding commit we started releasing the
    +    "pruning" member of the same type, but handling "diffopt" will require
    +    us to untangle the "no_free" conditions I added in e900d494dcf (diff:
    +    add an API for deferred freeing, 2021-02-11).
     
    -    This change is split from preceding changes to make similar
    -    conversions in those commits easier to review, as they are all
    -    repetitions of the same pattern.
    +    Let's leave a TODO comment to that effect, and so that we don't forget
    +    refactor code that was changed to use release_revisions() in earlier
    +    commits to stop using the "diffopt" member after a call to
    +    release_revisions(). This works currently, but would become a logic
    +    error as soon as we started freeing "diffopt". Doing that change now
    +    doesn't harm anything, and future-proofs us against a later change to
    +    release_revisions().
     
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
    + ## diff-lib.c ##
    +@@ diff-lib.c: int index_differs_from(struct repository *r,
    + {
    + 	struct rev_info rev;
    + 	struct setup_revision_opt opt;
    ++	unsigned has_changes;
    + 
    + 	repo_init_revisions(r, &rev, NULL);
    + 	memset(&opt, 0, sizeof(opt));
    +@@ diff-lib.c: int index_differs_from(struct repository *r,
    + 		diff_flags_or(&rev.diffopt.flags, flags);
    + 	rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
    + 	run_diff_index(&rev, 1);
    ++	has_changes = rev.diffopt.flags.has_changes;
    + 	release_revisions(&rev);
    +-	return (rev.diffopt.flags.has_changes != 0);
    ++	return (has_changes != 0);
    + }
    + 
    + static struct strbuf *idiff_prefix_cb(struct diff_options *opt, void *data)
    +
    + ## revision.c ##
    +@@ revision.c: void release_revisions(struct rev_info *revs)
    + 	date_mode_release(&revs->date_mode);
    + 	release_revisions_mailmap(revs->mailmap);
    + 	free_grep_patterns(&revs->grep_filter);
    ++	/* TODO (need to handle "no_free"): diff_free(&revs->diffopt) */
    + 	diff_free(&revs->pruning);
    + 	reflog_walk_info_release(revs->reflog_info);
    + 	release_revisions_topo_walk_info(revs->topo_walk_info);
    +
      ## wt-status.c ##
     @@ wt-status.c: int has_unstaged_changes(struct repository *r, int ignore_submodules)
      	rev_info.diffopt.flags.quick = 1;
      	diff_setup_done(&rev_info.diffopt);
      	result = run_diff_files(&rev_info, 0);
    ++	result = diff_result_code(&rev_info.diffopt, result);
    + 	release_revisions(&rev_info);
     -	return diff_result_code(&rev_info.diffopt, result);
    ++	return result;
    + }
    + 
    + /**
    +@@ wt-status.c: int has_uncommitted_changes(struct repository *r,
    + 
    + 	diff_setup_done(&rev_info.diffopt);
    + 	result = run_diff_index(&rev_info, 1);
     +	result = diff_result_code(&rev_info.diffopt, result);
    -+	release_revisions(&rev_info);
    + 	release_revisions(&rev_info);
    +-	return diff_result_code(&rev_info.diffopt, result);
     +	return result;
      }
      
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply	[flat|nested] 252+ messages in thread

* [PATCH v2 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf"
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:31   ` Ævar Arnfjörð Bjarmason
  2022-03-24  4:13     ` Junio C Hamano
  2022-03-23 20:31   ` [PATCH v2 02/27] blame: use "goto cleanup" for cleanup_scoreboard() Ævar Arnfjörð Bjarmason
                     ` (27 subsequent siblings)
  28 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:31 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Fix a memory leak that's been with us since f9500261e0a (fast-rebase:
write conflict state to working tree, index, and HEAD, 2021-05-20)
changed this code to move these strbuf_release() into an if/else
block.

We'll also add to "reflog_msg" in the "else" arm of the "if" block
being modified here, and we'll append to "branch_msg" in both
cases. But after f9500261e0a only the "if" block would free these two
"struct strbuf".

This change is needed so that a subsequent addition of a "goto
cleanup" pattern when adding release_revisions() doesn't solve this
unrelated bug while it's at it.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 t/helper/test-fast-rebase.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c
index fc2d4609043..993b90eaedd 100644
--- a/t/helper/test-fast-rebase.c
+++ b/t/helper/test-fast-rebase.c
@@ -201,8 +201,6 @@ int cmd__fast_rebase(int argc, const char **argv)
 		}
 		if (create_symref("HEAD", branch_name.buf, reflog_msg.buf) < 0)
 			die(_("unable to update HEAD"));
-		strbuf_release(&reflog_msg);
-		strbuf_release(&branch_name);
 
 		prime_cache_tree(the_repository, the_repository->index,
 				 result.tree);
@@ -221,5 +219,8 @@ int cmd__fast_rebase(int argc, const char **argv)
 	if (write_locked_index(&the_index, &lock,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("unable to write %s"), get_index_file());
+
+	strbuf_release(&reflog_msg);
+	strbuf_release(&branch_name);
 	return (result.clean == 0);
 }
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 02/27] blame: use "goto cleanup" for cleanup_scoreboard()
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
  2022-03-23 20:31   ` [PATCH v2 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:31   ` Ævar Arnfjörð Bjarmason
  2022-03-24  4:23     ` Junio C Hamano
  2022-03-23 20:31   ` [PATCH v2 03/27] string_list API users: use string_list_init_{no,}dup Ævar Arnfjörð Bjarmason
                     ` (26 subsequent siblings)
  28 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:31 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Amend a freeing pattern added in 0906ac2b54b (blame: use changed-path
Bloom filters, 2020-04-16) to use a "goto cleanup", so that we can be
sure that we call cleanup_scoreboard().

This change is needed so that a subsequent addition of a
release_revisions() doesn't solve this unrelated issue while it's at
it.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/blame.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/builtin/blame.c b/builtin/blame.c
index 8d15b68afc9..885b381ab83 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1167,7 +1167,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 	if (!incremental)
 		setup_pager();
 	else
-		return 0;
+		goto cleanup;
 
 	blame_sort_final(&sb);
 
@@ -1201,6 +1201,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 		printf("num commits: %d\n", sb.num_commits);
 	}
 
+cleanup:
 	cleanup_scoreboard(&sb);
 	return 0;
 }
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 03/27] string_list API users: use string_list_init_{no,}dup
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
  2022-03-23 20:31   ` [PATCH v2 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
  2022-03-23 20:31   ` [PATCH v2 02/27] blame: use "goto cleanup" for cleanup_scoreboard() Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:31   ` Ævar Arnfjörð Bjarmason
  2022-03-23 20:31   ` [PATCH v2 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids" Ævar Arnfjörð Bjarmason
                     ` (25 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:31 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Follow-up on the introduction of string_list_init_nodup() and
string_list_init_dup() in the series merged in bd4232fac33 (Merge
branch 'ab/struct-init', 2021-07-16) and convert code that implicitly
relied on xcalloc() being equivalent to the initializer to use
xmalloc() and string_list_init_{no,}dup() instead.

In the case of get_unmerged() in merge-recursive.c we used the
combination of xcalloc() and assigning "1" to "strdup_strings" to get
what we'd get via string_list_init_dup(), let's use that instead.

Adjacent code in cmd_format_patch() will be changed in a subsequent
commit, since we're changing that let's change the other in-tree
patterns that do the same. Let's also convert a "x == NULL" to "!x"
per our CodingGuidelines, as we need to change the "if" line anyway.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/log.c      | 9 ++++++---
 builtin/shortlog.c | 6 ++++--
 merge-recursive.c  | 4 ++--
 3 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/builtin/log.c b/builtin/log.c
index c211d66d1d0..634dc782cce 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -231,7 +231,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
 	}
 
 	if (mailmap) {
-		rev->mailmap = xcalloc(1, sizeof(struct string_list));
+		rev->mailmap = xmalloc(sizeof(struct string_list));
+		string_list_init_nodup(rev->mailmap);
 		read_mailmap(rev->mailmap);
 	}
 
@@ -2173,8 +2174,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		prepare_bases(&bases, base, list, nr);
 	}
 
-	if (in_reply_to || thread || cover_letter)
-		rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
+	if (in_reply_to || thread || cover_letter) {
+		rev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));
+		string_list_init_nodup(rev.ref_message_ids);
+	}
 	if (in_reply_to) {
 		const char *msgid = clean_message_id(in_reply_to);
 		string_list_append(rev.ref_message_ids, msgid);
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 26c5c0cf935..fcde07c9367 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -81,8 +81,10 @@ static void insert_one_record(struct shortlog *log,
 		format_subject(&subject, oneline, " ");
 		buffer = strbuf_detach(&subject, NULL);
 
-		if (item->util == NULL)
-			item->util = xcalloc(1, sizeof(struct string_list));
+		if (!item->util) {
+			item->util = xmalloc(sizeof(struct string_list));
+			string_list_init_nodup(item->util);
+		}
 		string_list_append(item->util, buffer);
 	}
 }
diff --git a/merge-recursive.c b/merge-recursive.c
index 1ee6364e8b1..32bbba5fbb1 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -522,10 +522,10 @@ static struct stage_data *insert_stage_data(struct repository *r,
  */
 static struct string_list *get_unmerged(struct index_state *istate)
 {
-	struct string_list *unmerged = xcalloc(1, sizeof(struct string_list));
+	struct string_list *unmerged = xmalloc(sizeof(struct string_list));
 	int i;
 
-	unmerged->strdup_strings = 1;
+	string_list_init_dup(unmerged);
 
 	/* TODO: audit for interaction with sparse-index. */
 	ensure_full_index(istate);
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids"
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (2 preceding siblings ...)
  2022-03-23 20:31   ` [PATCH v2 03/27] string_list API users: use string_list_init_{no,}dup Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:31   ` Ævar Arnfjörð Bjarmason
  2022-03-24  4:33     ` Junio C Hamano
  2022-03-23 20:31   ` [PATCH v2 05/27] revision.[ch]: split freeing of revs->commit into a function Ævar Arnfjörð Bjarmason
                     ` (24 subsequent siblings)
  28 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:31 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Fix two memory leaks in "struct rev_info" by freeing that memory in
cmd_format_patch(). These two are unusual special-cases in being in
the "struct rev_info", but not being "owned" by the code in
revision.c. I.e. they're members of the struct so that this code in
"builtin/log.c" can pass information code in log-tree.c.

See 20ff06805c6 (format-patch: resurrect extra headers from config,
2006-06-02) and d1566f7883f (git-format-patch: Make the second and
subsequent mails replies to the first, 2006-07-14) for the initial
introduction of "extra_headers" and "ref_message_ids".

We can count on repo_init_revisions() memset()-ing this data to 0
however, so we can count on it being either NULL or something we
allocated. In the case of "extra_headers" let's add a local "char *"
variable to hold it, to avoid the eventual cast from "const char *"
when we free() it.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/log.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/builtin/log.c b/builtin/log.c
index 634dc782cce..6f9928fabfe 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1747,6 +1747,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	struct commit *commit;
 	struct commit **list = NULL;
 	struct rev_info rev;
+	char *to_free = NULL;
 	struct setup_revision_opt s_r_opt;
 	int nr = 0, total, i;
 	int use_stdout = 0;
@@ -1947,7 +1948,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		strbuf_addch(&buf, '\n');
 	}
 
-	rev.extra_headers = strbuf_detach(&buf, NULL);
+	rev.extra_headers = to_free = strbuf_detach(&buf, NULL);
 
 	if (from) {
 		if (split_ident_line(&rev.from_ident, from, strlen(from)))
@@ -2284,6 +2285,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	strbuf_release(&rdiff1);
 	strbuf_release(&rdiff2);
 	strbuf_release(&rdiff_title);
+	free(to_free);
+	if (rev.ref_message_ids)
+		string_list_clear(rev.ref_message_ids, 0);
+	free(rev.ref_message_ids);
 	UNLEAK(rev);
 	return 0;
 }
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 05/27] revision.[ch]: split freeing of revs->commit into a function
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (3 preceding siblings ...)
  2022-03-23 20:31   ` [PATCH v2 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids" Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:31   ` Ævar Arnfjörð Bjarmason
  2022-03-24  4:33     ` Junio C Hamano
  2022-03-23 20:31   ` [PATCH v2 06/27] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
                     ` (23 subsequent siblings)
  28 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:31 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Move the existing code for invoking free_commit_list() and setting
revs->commits to NULL into a new release_revisions_commit_list()
function. This will be used as part of a general free()-ing mechanism
for "struct rev_info".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 15 +++++++++++----
 1 file changed, 11 insertions(+), 4 deletions(-)

diff --git a/revision.c b/revision.c
index 2646b78990e..303d1188207 100644
--- a/revision.c
+++ b/revision.c
@@ -2923,6 +2923,16 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 	return left;
 }
 
+static void release_revisions_commit_list(struct rev_info *revs)
+{
+	struct commit_list *commits = revs->commits;
+
+	if (!commits)
+		return;
+	free_commit_list(commits);
+	revs->commits = NULL;
+}
+
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
 {
 	struct commit_list *l = xcalloc(1, sizeof(*l));
@@ -4080,10 +4090,7 @@ static void create_boundary_commit_list(struct rev_info *revs)
 	 * boundary commits anyway.  (This is what the code has always
 	 * done.)
 	 */
-	if (revs->commits) {
-		free_commit_list(revs->commits);
-		revs->commits = NULL;
-	}
+	release_revisions_commit_list(revs);
 
 	/*
 	 * Put all of the actual boundary commits from revs->boundary_commits
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 06/27] revision.[ch]: provide and start using a release_revisions()
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (4 preceding siblings ...)
  2022-03-23 20:31   ` [PATCH v2 05/27] revision.[ch]: split freeing of revs->commit into a function Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:31   ` Ævar Arnfjörð Bjarmason
  2022-03-24  4:43     ` Junio C Hamano
  2022-03-23 20:31   ` [PATCH v2 07/27] revisions API users: add straightforward release_revisions() Ævar Arnfjörð Bjarmason
                     ` (22 subsequent siblings)
  28 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:31 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

The users of the revision.[ch] API's "struct rev_info" are a major
source of memory leaks in the test suite under SANITIZE=leak, which in
turn adds a lot of noise when trying to mark up tests with
"TEST_PASSES_SANITIZE_LEAK=true".

The users of that API are largely one-shot, e.g. "git log", so
arguably freeing the memory is a waste of time, but in many cases
they've actually been trying to free the memory, and just doing that
in a buggy manner.

Let's provide a release_revisions() function for these users, and
start migrating them over per the plan outlined in [1]. Right now this
only handles the "pending" member of the struct, but more will be
added in subsequent commits.

Even though we only clear the "pending" member now, let's not leave a
trap in code like the pre-image of index_differs_from(), where we'd
start doing the wrong thing as soon as the release_revisions() learned
to clear its "diffopt". I.e. we need to call release_revisions() after
we've inspected any state in "struct rev_info".

This leaves in place e.g. clear_pathspec(&rev.prune_data) in
stash_working_tree() in builtin/stash.c, subsequent commits will teach
release_revisions() to free "prune_data" and other members that in
some cases are individually cleared by users of "struct rev_info" by
reaching into its members. Those subsequent commits will remove the
relevant calls to e.g. clear_pathspec().

We avoid amending code in index_differs_from() in diff-lib.c as well
as wt_status_collect_changes_index(), has_unstaged_changes() and
has_uncommitted_changes() in wt-status.c in a way that assumes that we
are already clearing the "diffopt" member. That will be handled in a
subsequent commit.

1. https://lore.kernel.org/git/87a6k8daeu.fsf@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/checkout.c | 2 +-
 builtin/stash.c    | 3 +--
 diff-lib.c         | 2 +-
 range-diff.c       | 2 +-
 revision.c         | 7 +++++++
 revision.h         | 6 ++++++
 wt-status.c        | 5 +++--
 7 files changed, 20 insertions(+), 7 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 797681481d1..4d9e0bd3ac1 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -629,7 +629,7 @@ static void show_local_changes(struct object *head,
 	diff_setup_done(&rev.diffopt);
 	add_pending_object(&rev, head, NULL);
 	run_diff_index(&rev, 0);
-	object_array_clear(&rev.pending);
+	release_revisions(&rev);
 }
 
 static void describe_detached_head(const char *msg, struct commit *commit)
diff --git a/builtin/stash.c b/builtin/stash.c
index 242e73cbb09..8a8093c5eea 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1267,9 +1267,8 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
 
 done:
 	discard_index(&istate);
-	UNLEAK(rev);
-	object_array_clear(&rev.pending);
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 	strbuf_release(&diff_output);
 	remove_path(stash_index_path.buf);
 	return ret;
diff --git a/diff-lib.c b/diff-lib.c
index ca085a03efc..d6800274bd5 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -662,7 +662,7 @@ int index_differs_from(struct repository *r,
 		diff_flags_or(&rev.diffopt.flags, flags);
 	rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
 	run_diff_index(&rev, 1);
-	object_array_clear(&rev.pending);
+	release_revisions(&rev);
 	return (rev.diffopt.flags.has_changes != 0);
 }
 
diff --git a/range-diff.c b/range-diff.c
index b72eb9fdbee..39cc010c628 100644
--- a/range-diff.c
+++ b/range-diff.c
@@ -596,6 +596,6 @@ int is_range_diff_range(const char *arg)
 	}
 
 	free(copy);
-	object_array_clear(&revs.pending);
+	release_revisions(&revs);
 	return negative > 0 && positive > 0;
 }
diff --git a/revision.c b/revision.c
index 303d1188207..90bac9ada03 100644
--- a/revision.c
+++ b/revision.c
@@ -2933,6 +2933,13 @@ static void release_revisions_commit_list(struct rev_info *revs)
 	revs->commits = NULL;
 }
 
+void release_revisions(struct rev_info *revs)
+{
+	if (!revs)
+		return;
+	object_array_clear(&revs->pending);
+}
+
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
 {
 	struct commit_list *l = xcalloc(1, sizeof(*l));
diff --git a/revision.h b/revision.h
index 5bc59c7bfe1..ad325a30453 100644
--- a/revision.h
+++ b/revision.h
@@ -377,6 +377,12 @@ void repo_init_revisions(struct repository *r,
 int setup_revisions(int argc, const char **argv, struct rev_info *revs,
 		    struct setup_revision_opt *);
 
+/**
+ * Free data allocated in a "struct rev_info" after it's been setup
+ * with repo_init_revisions() and setup_revisions().
+ */
+void release_revisions(struct rev_info *revs);
+
 void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
 			const struct option *options,
 			const char * const usagestr[]);
diff --git a/wt-status.c b/wt-status.c
index d33f9272b72..922cf787f95 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -662,7 +662,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)
 
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_index(&rev, 1);
-	object_array_clear(&rev.pending);
+	release_revisions(&rev);
 	clear_pathspec(&rev.prune_data);
 }
 
@@ -2545,6 +2545,7 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)
 	rev_info.diffopt.flags.quick = 1;
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_files(&rev_info, 0);
+	release_revisions(&rev_info);
 	return diff_result_code(&rev_info.diffopt, result);
 }
 
@@ -2577,7 +2578,7 @@ int has_uncommitted_changes(struct repository *r,
 
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_index(&rev_info, 1);
-	object_array_clear(&rev_info.pending);
+	release_revisions(&rev_info);
 	return diff_result_code(&rev_info.diffopt, result);
 }
 
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 07/27] revisions API users: add straightforward release_revisions()
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (5 preceding siblings ...)
  2022-03-23 20:31   ` [PATCH v2 06/27] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:31   ` Ævar Arnfjörð Bjarmason
  2022-03-23 20:31   ` [PATCH v2 08/27] revisions API users: use release_revisions() needing "{ 0 }" init Ævar Arnfjörð Bjarmason
                     ` (21 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:31 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Add a release_revisions() to various users of "struct rev_list" in
those straightforward cases where we only need to add the
release_revisions() call to the end of a block, and don't need to
e.g. refactor anything to use a "goto cleanup" pattern.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 add-interactive.c                | 1 +
 bisect.c                         | 2 ++
 builtin/add.c                    | 1 +
 builtin/am.c                     | 3 +++
 builtin/bisect--helper.c         | 2 ++
 builtin/blame.c                  | 1 +
 builtin/checkout.c               | 1 +
 builtin/commit.c                 | 1 +
 builtin/describe.c               | 2 ++
 builtin/fast-export.c            | 1 +
 builtin/merge.c                  | 2 ++
 builtin/pack-objects.c           | 2 ++
 builtin/prune.c                  | 1 +
 builtin/reflog.c                 | 1 +
 builtin/shortlog.c               | 2 ++
 builtin/submodule--helper.c      | 1 +
 fmt-merge-msg.c                  | 1 +
 merge-ort.c                      | 1 +
 merge-recursive.c                | 1 +
 midx.c                           | 1 +
 pack-bitmap-write.c              | 1 +
 ref-filter.c                     | 1 +
 remote.c                         | 1 +
 sequencer.c                      | 3 +++
 shallow.c                        | 1 +
 submodule.c                      | 2 ++
 t/helper/test-revision-walking.c | 1 +
 wt-status.c                      | 1 +
 28 files changed, 39 insertions(+)

diff --git a/add-interactive.c b/add-interactive.c
index 72472103017..54cdfc82017 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -570,6 +570,7 @@ static int get_modified_files(struct repository *r,
 
 		if (ps)
 			clear_pathspec(&rev.prune_data);
+		release_revisions(&rev);
 	}
 	hashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);
 	if (unmerged_count)
diff --git a/bisect.c b/bisect.c
index 9e6a2b7f201..cc6b8b6230d 100644
--- a/bisect.c
+++ b/bisect.c
@@ -884,6 +884,7 @@ static int check_ancestors(struct repository *r, int rev_nr,
 	/* Clean up objects used, as they will be reused. */
 	clear_commit_marks_many(rev_nr, rev, ALL_REV_FLAGS);
 
+	release_revisions(&revs);
 	return res;
 }
 
@@ -964,6 +965,7 @@ static void show_diff_tree(struct repository *r,
 
 	setup_revisions(ARRAY_SIZE(argv) - 1, argv, &opt, NULL);
 	log_tree_commit(&opt, commit);
+	release_revisions(&opt);
 }
 
 /*
diff --git a/builtin/add.c b/builtin/add.c
index 3ffb86a4338..f507d2191cd 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -340,6 +340,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
 
 	unlink(file);
 	free(file);
+	release_revisions(&rev);
 	return 0;
 }
 
diff --git a/builtin/am.c b/builtin/am.c
index 0f4111bafa0..93bec62afa9 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1397,6 +1397,7 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm
 	add_pending_object(&rev_info, &commit->object, "");
 	diff_setup_done(&rev_info.diffopt);
 	log_tree_commit(&rev_info, commit);
+	release_revisions(&rev_info);
 }
 
 /**
@@ -1429,6 +1430,7 @@ static void write_index_patch(const struct am_state *state)
 	add_pending_object(&rev_info, &tree->object, "");
 	diff_setup_done(&rev_info.diffopt);
 	run_diff_index(&rev_info, 1);
+	release_revisions(&rev_info);
 }
 
 /**
@@ -1582,6 +1584,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
 		add_pending_oid(&rev_info, "HEAD", &our_tree, 0);
 		diff_setup_done(&rev_info.diffopt);
 		run_diff_index(&rev_info, 1);
+		release_revisions(&rev_info);
 	}
 
 	if (run_apply(state, index_path))
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 8b2b259ff0d..e4d7b6779ae 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -555,6 +555,7 @@ static int bisect_skipped_commits(struct bisect_terms *terms)
 	reset_revision_walk();
 
 	strbuf_release(&commit_name);
+	release_revisions(&revs);
 	fclose(fp);
 	return 0;
 }
@@ -1041,6 +1042,7 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar
 						oid_to_hex(&commit->object.oid));
 
 			reset_revision_walk();
+			release_revisions(&revs);
 		} else {
 			strvec_push(&argv_state, argv[i]);
 		}
diff --git a/builtin/blame.c b/builtin/blame.c
index 885b381ab83..24bac822c56 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1203,5 +1203,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 
 cleanup:
 	cleanup_scoreboard(&sb);
+	release_revisions(&revs);
 	return 0;
 }
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 4d9e0bd3ac1..7ad4a7113c9 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1082,6 +1082,7 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne
 
 	/* Clean up objects used, as they will be reused. */
 	repo_clear_commit_marks(the_repository, ALL_REV_FLAGS);
+	release_revisions(&revs);
 }
 
 static int switch_branches(const struct checkout_opts *opts,
diff --git a/builtin/commit.c b/builtin/commit.c
index 009a1de0a3d..c7eda9bbb72 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1123,6 +1123,7 @@ static const char *find_author_by_nickname(const char *name)
 		strbuf_release(&buf);
 		format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
 		clear_mailmap(&mailmap);
+		release_revisions(&revs);
 		return strbuf_detach(&buf, NULL);
 	}
 	die(_("--author '%s' is not 'Name <email>' and matches no existing author"), name);
diff --git a/builtin/describe.c b/builtin/describe.c
index 42159cd26bd..a76f1a1a7a7 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -517,6 +517,7 @@ static void describe_blob(struct object_id oid, struct strbuf *dst)
 
 	traverse_commit_list(&revs, process_commit, process_object, &pcd);
 	reset_revision_walk();
+	release_revisions(&revs);
 }
 
 static void describe(const char *arg, int last_one)
@@ -667,6 +668,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
 				suffix = NULL;
 			else
 				suffix = dirty;
+			release_revisions(&revs);
 		}
 		describe("HEAD", 1);
 	} else if (dirty) {
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index a7d72697fba..f34ae451ee3 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -1275,6 +1275,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
 		printf("done\n");
 
 	refspec_clear(&refspecs);
+	release_revisions(&revs);
 
 	return 0;
 }
diff --git a/builtin/merge.c b/builtin/merge.c
index f178f5a3ee1..d9784d4891c 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -443,6 +443,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
 	}
 	write_file_buf(git_path_squash_msg(the_repository), out.buf, out.len);
 	strbuf_release(&out);
+	release_revisions(&rev);
 }
 
 static void finish(struct commit *head_commit,
@@ -998,6 +999,7 @@ static int evaluate_result(void)
 	 */
 	cnt += count_unmerged_entries();
 
+	release_revisions(&rev);
 	return cnt;
 }
 
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 829ca359cf9..0aa194f68dc 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -3384,6 +3384,7 @@ static void read_packs_list_from_stdin(void)
 	strbuf_release(&buf);
 	string_list_clear(&include_packs, 0);
 	string_list_clear(&exclude_packs, 0);
+	release_revisions(&revs);
 }
 
 static void read_object_list_from_stdin(void)
@@ -3800,6 +3801,7 @@ static void get_object_list(int ac, const char **av)
 	if (unpack_unreachable)
 		loosen_unused_packed_objects();
 
+	release_revisions(&revs);
 	oid_array_clear(&recent_objects);
 }
 
diff --git a/builtin/prune.c b/builtin/prune.c
index c2bcdc07db4..df376b2ed1e 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -196,5 +196,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
 		prune_shallow(show_only ? PRUNE_SHOW_ONLY : 0);
 	}
 
+	release_revisions(&revs);
 	return 0;
 }
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 9407f835cb6..592d5d33442 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -248,6 +248,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 		if (verbose)
 			printf(_("Marking reachable objects..."));
 		mark_reachable_objects(&revs, 0, 0, NULL);
+		release_revisions(&revs);
 		if (verbose)
 			putchar('\n');
 	}
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index fcde07c9367..35825f075e3 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -422,6 +422,8 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 	else
 		get_from_rev(&rev, &log);
 
+	release_revisions(&rev);
+
 	shortlog_output(&log);
 	if (log.file != stdout)
 		fclose(log.file);
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 2f7c58362b2..0eeced8a10d 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1259,6 +1259,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 		run_diff_files(&rev, 0);
 	prepare_submodule_summary(info, &list);
 	strvec_clear(&diff_args);
+	release_revisions(&rev);
 	return 0;
 }
 
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index baca57d5b64..f48f44f9cd1 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -699,6 +699,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 			shortlog(origins.items[i].string,
 				 origins.items[i].util,
 				 head, &rev, opts, out);
+		release_revisions(&rev);
 	}
 
 	strbuf_complete_line(out);
diff --git a/merge-ort.c b/merge-ort.c
index 8545354dafd..4c0d4eba19b 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -1594,6 +1594,7 @@ static int find_first_merges(struct repository *repo,
 	}
 
 	object_array_clear(&merges);
+	release_revisions(&revs);
 	return result->nr;
 }
 
diff --git a/merge-recursive.c b/merge-recursive.c
index 32bbba5fbb1..acd13b2b069 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -1160,6 +1160,7 @@ static int find_first_merges(struct repository *repo,
 	}
 
 	object_array_clear(&merges);
+	release_revisions(&revs);
 	return result->nr;
 }
 
diff --git a/midx.c b/midx.c
index 865170bad05..702c8a9b178 100644
--- a/midx.c
+++ b/midx.c
@@ -1061,6 +1061,7 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr
 	if (indexed_commits_nr_p)
 		*indexed_commits_nr_p = cb.commits_nr;
 
+	release_revisions(&revs);
 	return cb.commits;
 }
 
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index cab3eaa2acd..ea8e0b51cdf 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -326,6 +326,7 @@ static void bitmap_builder_init(struct bitmap_builder *bb,
 	trace2_data_intmax("pack-bitmap-write", the_repository,
 			   "num_maximal_commits", num_maximal);
 
+	release_revisions(&revs);
 	free_commit_list(reusable);
 }
 
diff --git a/ref-filter.c b/ref-filter.c
index 7838bd22b8d..a91688bbf17 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2392,6 +2392,7 @@ static void reach_filter(struct ref_array *array,
 		clear_commit_marks(merge_commit, ALL_REV_FLAGS);
 	}
 
+	release_revisions(&revs);
 	free(to_clear);
 }
 
diff --git a/remote.c b/remote.c
index 42a4e7106e1..fa3152a5d52 100644
--- a/remote.c
+++ b/remote.c
@@ -2172,6 +2172,7 @@ static int stat_branch_pair(const char *branch_name, const char *base,
 	clear_commit_marks(theirs, ALL_REV_FLAGS);
 
 	strvec_clear(&argv);
+	release_revisions(&revs);
 	return 1;
 }
 
diff --git a/sequencer.c b/sequencer.c
index 84eed9e96bc..29ec201af64 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1347,6 +1347,7 @@ void print_commit_summary(struct repository *r,
 		log_tree_commit(&rev, commit);
 	}
 
+	release_revisions(&rev);
 	strbuf_release(&format);
 }
 
@@ -3415,6 +3416,7 @@ static int make_patch(struct repository *r,
 		unuse_commit_buffer(commit, commit_buffer);
 	}
 	strbuf_release(&buf);
+	release_revisions(&log_tree_opt);
 
 	return res;
 }
@@ -4527,6 +4529,7 @@ static int pick_commits(struct repository *r,
 					      &log_tree_opt.diffopt);
 				log_tree_diff_flush(&log_tree_opt);
 			}
+			release_revisions(&log_tree_opt);
 		}
 		flush_rewritten_pending();
 		if (!stat(rebase_path_rewritten_list(), &st) &&
diff --git a/shallow.c b/shallow.c
index 71e5876f377..2552f139f61 100644
--- a/shallow.c
+++ b/shallow.c
@@ -261,6 +261,7 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,
 		if ((o->flags & both_flags) == both_flags)
 			o->flags &= ~not_shallow_flag;
 	}
+	release_revisions(&revs);
 	return result;
 }
 
diff --git a/submodule.c b/submodule.c
index 5ace18a7d94..0510cb193b6 100644
--- a/submodule.c
+++ b/submodule.c
@@ -902,9 +902,11 @@ static void collect_changed_submodules(struct repository *r,
 		diff_rev.diffopt.format_callback_data = &data;
 		diff_rev.dense_combined_merges = 1;
 		diff_tree_combined_merge(commit, &diff_rev);
+		release_revisions(&diff_rev);
 	}
 
 	reset_revision_walk();
+	release_revisions(&rev);
 }
 
 static void free_submodules_oids(struct string_list *submodules)
diff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c
index 625b2dbf822..4a45d5bac2a 100644
--- a/t/helper/test-revision-walking.c
+++ b/t/helper/test-revision-walking.c
@@ -43,6 +43,7 @@ static int run_revision_walk(void)
 	}
 
 	reset_revision_walk();
+	release_revisions(&rev);
 	return got_revision;
 }
 
diff --git a/wt-status.c b/wt-status.c
index 922cf787f95..f9100621375 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1152,6 +1152,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
 		rev.diffopt.b_prefix = "w/";
 		run_diff_files(&rev, 0);
 	}
+	release_revisions(&rev);
 }
 
 static void wt_longstatus_print_tracking(struct wt_status *s)
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 08/27] revisions API users: use release_revisions() needing "{ 0 }" init
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (6 preceding siblings ...)
  2022-03-23 20:31   ` [PATCH v2 07/27] revisions API users: add straightforward release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:31   ` Ævar Arnfjörð Bjarmason
  2022-03-24  4:53     ` Junio C Hamano
  2022-03-23 20:31   ` [PATCH v2 09/27] stash: always have the owner of "stash_info" free it Ævar Arnfjörð Bjarmason
                     ` (20 subsequent siblings)
  28 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:31 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Use release_revisions() to various users of "struct rev_list" which
need to have their "struct rev_info" zero-initialized before we can
start using it. In all of these cases we might "goto cleanup" (or equivalent),

For the bundle.c code see the early exit case added in
3bbbe467f29 (bundle verify: error out if called without an object
database, 2019-05-27).

For the relevant bisect.c code see 45b6370812c (bisect: libify
`check_good_are_ancestors_of_bad` and its dependents, 2020-02-17).

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 bisect.c                    | 18 ++++++++++++------
 builtin/submodule--helper.c |  3 ++-
 bundle.c                    | 12 ++++++++----
 3 files changed, 22 insertions(+), 11 deletions(-)

diff --git a/bisect.c b/bisect.c
index cc6b8b6230d..159a4b644df 100644
--- a/bisect.c
+++ b/bisect.c
@@ -1010,7 +1010,7 @@ void read_bisect_terms(const char **read_bad, const char **read_good)
  */
 enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 {
-	struct rev_info revs;
+	struct rev_info revs = { 0 };
 	struct commit_list *tried;
 	int reaches = 0, all = 0, nr, steps;
 	enum bisect_error res = BISECT_OK;
@@ -1035,7 +1035,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 
 	res = check_good_are_ancestors_of_bad(r, prefix, no_checkout);
 	if (res)
-		return res;
+		goto cleanup;
 
 	bisect_rev_setup(r, &revs, prefix, "%s", "^%s", 1);
 
@@ -1060,14 +1060,16 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 		       term_good,
 		       term_bad);
 
-		return BISECT_FAILED;
+		res = BISECT_FAILED;
+		goto cleanup;
 	}
 
 	if (!all) {
 		fprintf(stderr, _("No testable commit found.\n"
 			"Maybe you started with bad path arguments?\n"));
 
-		return BISECT_NO_TESTABLE_COMMIT;
+		res = BISECT_NO_TESTABLE_COMMIT;
+		goto cleanup;
 	}
 
 	bisect_rev = &revs.commits->item->object.oid;
@@ -1087,7 +1089,8 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 		 * for negative return values for early returns up
 		 * until the cmd_bisect__helper() caller.
 		 */
-		return BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;
+		res = BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;
+		goto cleanup;
 	}
 
 	nr = all - reaches - 1;
@@ -1106,7 +1109,10 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 	/* Clean up objects used, as they will be reused. */
 	repo_clear_commit_marks(r, ALL_REV_FLAGS);
 
-	return bisect_checkout(bisect_rev, no_checkout);
+	res = bisect_checkout(bisect_rev, no_checkout);
+cleanup:
+	release_revisions(&revs);
+	return res;
 }
 
 static inline int log2i(int n)
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 0eeced8a10d..e8e20cdcc53 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -756,7 +756,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
 {
 	char *displaypath;
 	struct strvec diff_files_args = STRVEC_INIT;
-	struct rev_info rev;
+	struct rev_info rev = { 0 };
 	int diff_files_result;
 	struct strbuf buf = STRBUF_INIT;
 	const char *git_dir;
@@ -843,6 +843,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
 cleanup:
 	strvec_clear(&diff_files_args);
 	free(displaypath);
+	release_revisions(&rev);
 }
 
 static void status_submodule_cb(const struct cache_entry *list_item,
diff --git a/bundle.c b/bundle.c
index e359370cfcd..a83d253a815 100644
--- a/bundle.c
+++ b/bundle.c
@@ -196,14 +196,16 @@ int verify_bundle(struct repository *r,
 	 * to be verbose about the errors
 	 */
 	struct string_list *p = &header->prerequisites;
-	struct rev_info revs;
+	struct rev_info revs = { 0 };
 	const char *argv[] = {NULL, "--all", NULL};
 	struct commit *commit;
 	int i, ret = 0, req_nr;
 	const char *message = _("Repository lacks these prerequisite commits:");
 
-	if (!r || !r->objects || !r->objects->odb)
-		return error(_("need a repository to verify a bundle"));
+	if (!r || !r->objects || !r->objects->odb) {
+		ret = error(_("need a repository to verify a bundle"));
+		goto cleanup;
+	}
 
 	repo_init_revisions(r, &revs, NULL);
 	for (i = 0; i < p->nr; i++) {
@@ -221,7 +223,7 @@ int verify_bundle(struct repository *r,
 		error("%s %s", oid_to_hex(oid), name);
 	}
 	if (revs.pending.nr != p->nr)
-		return ret;
+		goto cleanup;
 	req_nr = revs.pending.nr;
 	setup_revisions(2, argv, &revs, NULL);
 
@@ -283,6 +285,8 @@ int verify_bundle(struct repository *r,
 			list_refs(r, 0, NULL);
 		}
 	}
+cleanup:
+	release_revisions(&revs);
 	return ret;
 }
 
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 09/27] stash: always have the owner of "stash_info" free it
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (7 preceding siblings ...)
  2022-03-23 20:31   ` [PATCH v2 08/27] revisions API users: use release_revisions() needing "{ 0 }" init Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:31   ` Ævar Arnfjörð Bjarmason
  2022-03-25  0:51     ` Junio C Hamano
  2022-03-23 20:32   ` [PATCH v2 10/27] revisions API users: add "goto cleanup" for release_revisions() Ævar Arnfjörð Bjarmason
                     ` (19 subsequent siblings)
  28 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:31 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Change the initialization of the "revision" member of "struct
stash_info" to be initialized vi a macro, and more importantly that
that initializing function be tasked to free it, usually via "goto
cleanup" pattern.

Despite the "revision" name (and the topic of the series containing
this commit) the "stash info" has nothing to do with the "struct
rev_info". I'm making this change because in the subsequent commit
when we do want to free the "struct rev_info" via a "goto cleanup"
pattern we'd otherwise free() uninitialized memory in some cases, as
we only strbuf_init() the string in get_stash_info().

So while it's the smallest possible change, let's convert all users of
this pattern in the file while we're at it.

A good follow-up to this change would be to change all the "ret = -1;
goto done;" in this file to instead use a "goto cleanup", and
initialize "int ret = -1" at the start of the relevant functions. That
would allow us to drop a lot of needless brace verbosity on two-line
"if" statements, but let's leave that alone for now.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/stash.c | 70 ++++++++++++++++++++++++++-----------------------
 1 file changed, 37 insertions(+), 33 deletions(-)

diff --git a/builtin/stash.c b/builtin/stash.c
index 8a8093c5eea..4c8c8af8a09 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -117,6 +117,10 @@ struct stash_info {
 	int has_u;
 };
 
+#define STASH_INFO_INIT { \
+	.revision = STRBUF_INIT, \
+}
+
 static void free_stash_info(struct stash_info *info)
 {
 	strbuf_release(&info->revision);
@@ -158,10 +162,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
 	if (argc == 1)
 		commit = argv[0];
 
-	strbuf_init(&info->revision, 0);
 	if (!commit) {
 		if (!ref_exists(ref_stash)) {
-			free_stash_info(info);
 			fprintf_ln(stderr, _("No stash entries found."));
 			return -1;
 		}
@@ -175,11 +177,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
 
 	revision = info->revision.buf;
 
-	if (get_oid(revision, &info->w_commit)) {
-		error(_("%s is not a valid reference"), revision);
-		free_stash_info(info);
-		return -1;
-	}
+	if (get_oid(revision, &info->w_commit))
+		return error(_("%s is not a valid reference"), revision);
 
 	assert_stash_like(info, revision);
 
@@ -198,7 +197,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
 		info->is_stash_ref = !strcmp(expanded_ref, ref_stash);
 		break;
 	default: /* Invalid or ambiguous */
-		free_stash_info(info);
+		break;
 	}
 
 	free(expanded_ref);
@@ -599,10 +598,10 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
 
 static int apply_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret;
+	int ret = -1;
 	int quiet = 0;
 	int index = 0;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_BOOL(0, "index", &index,
@@ -614,9 +613,10 @@ static int apply_stash(int argc, const char **argv, const char *prefix)
 			     git_stash_apply_usage, 0);
 
 	if (get_stash_info(&info, argc, argv))
-		return -1;
+		goto cleanup;
 
 	ret = do_apply_stash(prefix, &info, index, quiet);
+cleanup:
 	free_stash_info(&info);
 	return ret;
 }
@@ -663,9 +663,9 @@ static void assert_stash_ref(struct stash_info *info)
 
 static int drop_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret;
+	int ret = -1;
 	int quiet = 0;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_END()
@@ -675,21 +675,22 @@ static int drop_stash(int argc, const char **argv, const char *prefix)
 			     git_stash_drop_usage, 0);
 
 	if (get_stash_info(&info, argc, argv))
-		return -1;
+		goto cleanup;
 
 	assert_stash_ref(&info);
 
 	ret = do_drop_stash(&info, quiet);
+cleanup:
 	free_stash_info(&info);
 	return ret;
 }
 
 static int pop_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret;
+	int ret = -1;
 	int index = 0;
 	int quiet = 0;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_BOOL(0, "index", &index,
@@ -701,7 +702,7 @@ static int pop_stash(int argc, const char **argv, const char *prefix)
 			     git_stash_pop_usage, 0);
 
 	if (get_stash_info(&info, argc, argv))
-		return -1;
+		goto cleanup;
 
 	assert_stash_ref(&info);
 	if ((ret = do_apply_stash(prefix, &info, index, quiet)))
@@ -710,15 +711,16 @@ static int pop_stash(int argc, const char **argv, const char *prefix)
 	else
 		ret = do_drop_stash(&info, quiet);
 
+cleanup:
 	free_stash_info(&info);
 	return ret;
 }
 
 static int branch_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret;
+	int ret = -1;
 	const char *branch = NULL;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct child_process cp = CHILD_PROCESS_INIT;
 	struct option options[] = {
 		OPT_END()
@@ -735,7 +737,7 @@ static int branch_stash(int argc, const char **argv, const char *prefix)
 	branch = argv[0];
 
 	if (get_stash_info(&info, argc - 1, argv + 1))
-		return -1;
+		goto cleanup;
 
 	cp.git_cmd = 1;
 	strvec_pushl(&cp.args, "checkout", "-b", NULL);
@@ -747,8 +749,8 @@ static int branch_stash(int argc, const char **argv, const char *prefix)
 	if (!ret && info.is_stash_ref)
 		ret = do_drop_stash(&info, 0);
 
+cleanup:
 	free_stash_info(&info);
-
 	return ret;
 }
 
@@ -826,8 +828,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op
 static int show_stash(int argc, const char **argv, const char *prefix)
 {
 	int i;
-	int ret = 0;
-	struct stash_info info;
+	int ret = -1;
+	struct stash_info info = STASH_INFO_INIT;
 	struct rev_info rev;
 	struct strvec stash_args = STRVEC_INIT;
 	struct strvec revision_args = STRVEC_INIT;
@@ -862,10 +864,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 			strvec_push(&revision_args, argv[i]);
 	}
 
-	ret = get_stash_info(&info, stash_args.nr, stash_args.v);
-	strvec_clear(&stash_args);
-	if (ret)
-		return -1;
+	if (get_stash_info(&info, stash_args.nr, stash_args.v))
+		goto cleanup;
 
 	/*
 	 * The config settings are applied only if there are not passed
@@ -879,8 +879,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 			rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
 
 		if (!show_stat && !show_patch) {
-			free_stash_info(&info);
-			return 0;
+			ret = 0;
+			goto cleanup;
 		}
 	}
 
@@ -913,8 +913,11 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 	}
 	log_tree_diff_flush(&rev);
 
+	ret = diff_result_code(&rev.diffopt, 0);;
+cleanup:
+	strvec_clear(&stash_args);
 	free_stash_info(&info);
-	return diff_result_code(&rev.diffopt, 0);
+	return ret;
 }
 
 static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,
@@ -1410,9 +1413,9 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
 
 static int create_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret = 0;
+	int ret;
 	struct strbuf stash_msg_buf = STRBUF_INIT;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct pathspec ps;
 
 	/* Starting with argv[1], since argv[0] is "create" */
@@ -1427,6 +1430,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)
 	if (!ret)
 		printf_ln("%s", oid_to_hex(&info.w_commit));
 
+	free_stash_info(&info);
 	strbuf_release(&stash_msg_buf);
 	return ret;
 }
@@ -1435,7 +1439,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
 			 int keep_index, int patch_mode, int include_untracked, int only_staged)
 {
 	int ret = 0;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct strbuf patch = STRBUF_INIT;
 	struct strbuf stash_msg_buf = STRBUF_INIT;
 	struct strbuf untracked_files = STRBUF_INIT;
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 10/27] revisions API users: add "goto cleanup" for release_revisions()
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (8 preceding siblings ...)
  2022-03-23 20:31   ` [PATCH v2 09/27] stash: always have the owner of "stash_info" free it Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:32   ` Ævar Arnfjörð Bjarmason
  2022-03-25  0:52     ` Junio C Hamano
  2022-03-23 20:32   ` [PATCH v2 11/27] revisions API users: use release_revisions() in http-push.c Ævar Arnfjörð Bjarmason
                     ` (18 subsequent siblings)
  28 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:32 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Add a release_revisions() to various users of "struct rev_list" which
requires a minor refactoring to a "goto cleanup" pattern to use that
function.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/diff-files.c        |  8 ++++++--
 builtin/rev-list.c          | 19 ++++++++++++-------
 builtin/stash.c             |  1 +
 builtin/submodule--helper.c | 10 +++++++---
 sequencer.c                 | 23 ++++++++++++++++-------
 t/helper/test-fast-rebase.c | 18 +++++++++++++-----
 6 files changed, 55 insertions(+), 24 deletions(-)

diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index 70103c40952..2bfaf9ba7ae 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -77,8 +77,12 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
 
 	if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
 		perror("read_cache_preload");
-		return -1;
+		result = -1;
+		goto cleanup;
 	}
+cleanup:
 	result = run_diff_files(&rev, options);
-	return diff_result_code(&rev.diffopt, result);
+	result = diff_result_code(&rev.diffopt, result);
+	release_revisions(&rev);
+	return result;
 }
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 572da1472e5..2f0b6c759e4 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -502,6 +502,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 	int use_bitmap_index = 0;
 	int filter_provided_objects = 0;
 	const char *show_progress = NULL;
+	int ret = 0;
 
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage(rev_list_usage);
@@ -585,7 +586,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 		}
 		if (!strcmp(arg, "--test-bitmap")) {
 			test_bitmap_walk(&revs);
-			return 0;
+			goto cleanup;
 		}
 		if (skip_prefix(arg, "--progress=", &arg)) {
 			show_progress = arg;
@@ -674,11 +675,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 
 	if (use_bitmap_index) {
 		if (!try_bitmap_count(&revs, filter_provided_objects))
-			return 0;
+			goto cleanup;
 		if (!try_bitmap_disk_usage(&revs, filter_provided_objects))
-			return 0;
+			goto cleanup;
 		if (!try_bitmap_traversal(&revs, filter_provided_objects))
-			return 0;
+			goto cleanup;
 	}
 
 	if (prepare_revision_walk(&revs))
@@ -698,8 +699,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 
 		find_bisection(&revs.commits, &reaches, &all, bisect_flags);
 
-		if (bisect_show_vars)
-			return show_bisect_vars(&info, reaches, all);
+		if (bisect_show_vars) {
+			ret = show_bisect_vars(&info, reaches, all);
+			goto cleanup;
+		}
 	}
 
 	if (filter_provided_objects) {
@@ -754,5 +757,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 	if (show_disk_usage)
 		printf("%"PRIuMAX"\n", (uintmax_t)total_disk_usage);
 
-	return 0;
+cleanup:
+	release_revisions(&revs);
+	return ret;
 }
diff --git a/builtin/stash.c b/builtin/stash.c
index 4c8c8af8a09..4fc717d5578 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -917,6 +917,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 cleanup:
 	strvec_clear(&stash_args);
 	free_stash_info(&info);
+	release_revisions(&rev);
 	return ret;
 }
 
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index e8e20cdcc53..78887779d8b 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1222,6 +1222,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 	struct strvec diff_args = STRVEC_INIT;
 	struct rev_info rev;
 	struct module_cb_list list = MODULE_CB_LIST_INIT;
+	int ret = 0;
 
 	strvec_push(&diff_args, get_diff_cmd(diff_cmd));
 	if (info->cached)
@@ -1247,11 +1248,13 @@ static int compute_summary_module_list(struct object_id *head_oid,
 			setup_work_tree();
 		if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
 			perror("read_cache_preload");
-			return -1;
+			ret = -1;
+			goto cleanup;
 		}
 	} else if (read_cache() < 0) {
 		perror("read_cache");
-		return -1;
+		ret = -1;
+		goto cleanup;
 	}
 
 	if (diff_cmd == DIFF_INDEX)
@@ -1259,9 +1262,10 @@ static int compute_summary_module_list(struct object_id *head_oid,
 	else
 		run_diff_files(&rev, 0);
 	prepare_submodule_summary(info, &list);
+cleanup:
 	strvec_clear(&diff_args);
 	release_revisions(&rev);
-	return 0;
+	return ret;
 }
 
 static int module_summary(int argc, const char **argv, const char *prefix)
diff --git a/sequencer.c b/sequencer.c
index 29ec201af64..efea320fb9b 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -5356,6 +5356,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
 	int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
 	int reapply_cherry_picks = flags & TODO_LIST_REAPPLY_CHERRY_PICKS;
 	int skipped_commit = 0;
+	int ret = 0;
 
 	repo_init_revisions(r, &revs, NULL);
 	revs.verbose_header = 1;
@@ -5379,14 +5380,20 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
 	pp.fmt = revs.commit_format;
 	pp.output_encoding = get_log_output_encoding();
 
-	if (setup_revisions(argc, argv, &revs, NULL) > 1)
-		return error(_("make_script: unhandled options"));
+	if (setup_revisions(argc, argv, &revs, NULL) > 1) {
+		ret = error(_("make_script: unhandled options"));
+		goto cleanup;
+	}
 
-	if (prepare_revision_walk(&revs) < 0)
-		return error(_("make_script: error preparing revisions"));
+	if (prepare_revision_walk(&revs) < 0) {
+		ret = error(_("make_script: error preparing revisions"));
+		goto cleanup;
+	}
 
-	if (rebase_merges)
-		return make_script_with_merges(&pp, &revs, out, flags);
+	if (rebase_merges) {
+		ret = make_script_with_merges(&pp, &revs, out, flags);
+		goto cleanup;
+	}
 
 	while ((commit = get_revision(&revs))) {
 		int is_empty = is_original_commit_empty(commit);
@@ -5410,7 +5417,9 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
 	if (skipped_commit)
 		advise_if_enabled(ADVICE_SKIPPED_CHERRY_PICKS,
 				  _("use --reapply-cherry-picks to include skipped commits"));
-	return 0;
+cleanup:
+	release_revisions(&revs);
+	return ret;
 }
 
 /*
diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c
index 993b90eaedd..4e5553e2024 100644
--- a/t/helper/test-fast-rebase.c
+++ b/t/helper/test-fast-rebase.c
@@ -99,6 +99,7 @@ int cmd__fast_rebase(int argc, const char **argv)
 	struct merge_result result;
 	struct strbuf reflog_msg = STRBUF_INIT;
 	struct strbuf branch_name = STRBUF_INIT;
+	int ret = 0;
 
 	/*
 	 * test-tool stuff doesn't set up the git directory by default; need to
@@ -137,13 +138,17 @@ int cmd__fast_rebase(int argc, const char **argv)
 	revs.topo_order = 1;
 	strvec_pushl(&rev_walk_args, "", argv[4], "--not", argv[3], NULL);
 
-	if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1)
-		return error(_("unhandled options"));
+	if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) {
+		ret = error(_("unhandled options"));
+		goto cleanup;
+	}
 
 	strvec_clear(&rev_walk_args);
 
-	if (prepare_revision_walk(&revs) < 0)
-		return error(_("error preparing revisions"));
+	if (prepare_revision_walk(&revs) < 0) {
+		ret = error(_("error preparing revisions"));
+		goto cleanup;
+	}
 
 	init_merge_options(&merge_opt, the_repository);
 	memset(&result, 0, sizeof(result));
@@ -220,7 +225,10 @@ int cmd__fast_rebase(int argc, const char **argv)
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("unable to write %s"), get_index_file());
 
+	ret = (result.clean == 0);
+cleanup:
 	strbuf_release(&reflog_msg);
 	strbuf_release(&branch_name);
-	return (result.clean == 0);
+	release_revisions(&revs);
+	return ret;
 }
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 11/27] revisions API users: use release_revisions() in http-push.c
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (9 preceding siblings ...)
  2022-03-23 20:32   ` [PATCH v2 10/27] revisions API users: add "goto cleanup" for release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:32   ` Ævar Arnfjörð Bjarmason
  2022-03-23 20:32   ` [PATCH v2 12/27] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
                     ` (17 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:32 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

In the case of cmd_main() in http-push.c we need to move the
deceleration of the "struct rev-list" into the loop over the
"remote_refs" when adding a release_revisions().

We'd previously set up the "revs" for each remote, but would
potentially leak memory on each one.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 http-push.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/http-push.c b/http-push.c
index f0c044dcf76..01e7c2ac5c8 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1689,7 +1689,6 @@ int cmd_main(int argc, const char **argv)
 	struct refspec rs = REFSPEC_INIT_PUSH;
 	struct remote_lock *ref_lock = NULL;
 	struct remote_lock *info_ref_lock = NULL;
-	struct rev_info revs;
 	int delete_branch = 0;
 	int force_delete = 0;
 	int objects_to_send;
@@ -1825,6 +1824,7 @@ int cmd_main(int argc, const char **argv)
 
 	new_refs = 0;
 	for (ref = remote_refs; ref; ref = ref->next) {
+		struct rev_info revs;
 		struct strvec commit_argv = STRVEC_INIT;
 
 		if (!ref->peer_ref)
@@ -1941,6 +1941,7 @@ int cmd_main(int argc, const char **argv)
 		unlock_remote(ref_lock);
 		check_locks();
 		strvec_clear(&commit_argv);
+		release_revisions(&revs);
 	}
 
 	/* Update remote server info if appropriate */
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 12/27] revisions API users: use release_revisions() in builtin/log.c
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (10 preceding siblings ...)
  2022-03-23 20:32   ` [PATCH v2 11/27] revisions API users: use release_revisions() in http-push.c Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:32   ` Ævar Arnfjörð Bjarmason
  2022-03-25  1:03     ` Junio C Hamano
  2022-03-23 20:32   ` [PATCH v2 13/27] revisions API users: use release_revisions() with UNLEAK() Ævar Arnfjörð Bjarmason
                     ` (16 subsequent siblings)
  28 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:32 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

In preparation for having the "log" family of functions make wider use
of release_revisions() let's have them call it just before
exiting. This changes the "log", "whatchanged", "show",
"format-patch", etc. commands, all of which live in this file.

The release_revisions() API still only frees the "pending" member, but
will learn to more members of "struct rev_info" in subsequent commits.

In the case of "format-patch" revert the addition of UNLEAK() in
dee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16), and
which will cause several tests that previously passed under
"TEST_PASSES_SANITIZE_LEAK=true" to start failing.

In subsequent commits we'll now be able to use those tests to check
whether that part of the API is really leaking memory, and will fix
all of those memory leaks. Removing the UNLEAK() allows us to make
incremental progress in that direction. See [1] for further details
about this approach.

1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/log.c          | 20 ++++++++++++--------
 t/t4126-apply-empty.sh |  2 --
 2 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/builtin/log.c b/builtin/log.c
index 6f9928fabfe..c40ebe1c3f4 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -295,6 +295,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
 	cmd_log_init_finish(argc, argv, prefix, rev, opt);
 }
 
+static int cmd_log_deinit(int ret, struct rev_info *rev)
+{
+	release_revisions(rev);
+	return ret;
+}
+
 /*
  * This gives a rough estimate for how many commits we
  * will print out in the list.
@@ -558,7 +564,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
 	cmd_log_init(argc, argv, prefix, &rev, &opt);
 	if (!rev.diffopt.output_format)
 		rev.diffopt.output_format = DIFF_FORMAT_RAW;
-	return cmd_log_walk(&rev);
+	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 }
 
 static void show_tagger(const char *buf, struct rev_info *rev)
@@ -677,7 +683,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
 	cmd_log_init(argc, argv, prefix, &rev, &opt);
 
 	if (!rev.no_walk)
-		return cmd_log_walk(&rev);
+		return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 
 	count = rev.pending.nr;
 	objects = rev.pending.objects;
@@ -732,8 +738,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
 			ret = error(_("unknown type: %d"), o->type);
 		}
 	}
-	free(objects);
-	return ret;
+	return cmd_log_deinit(ret, &rev);
 }
 
 /*
@@ -761,7 +766,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
 	rev.always_show_header = 1;
 	cmd_log_init_finish(argc, argv, prefix, &rev, &opt);
 
-	return cmd_log_walk(&rev);
+	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 }
 
 static void log_setup_revisions_tweak(struct rev_info *rev,
@@ -792,7 +797,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)
 	opt.revarg_opt = REVARG_COMMITTISH;
 	opt.tweak = log_setup_revisions_tweak;
 	cmd_log_init(argc, argv, prefix, &rev, &opt);
-	return cmd_log_walk(&rev);
+	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 }
 
 /* format-patch */
@@ -2289,8 +2294,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	if (rev.ref_message_ids)
 		string_list_clear(rev.ref_message_ids, 0);
 	free(rev.ref_message_ids);
-	UNLEAK(rev);
-	return 0;
+	return cmd_log_deinit(0, &rev);
 }
 
 static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
diff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh
index 33860d38290..66a7ba8ab8f 100755
--- a/t/t4126-apply-empty.sh
+++ b/t/t4126-apply-empty.sh
@@ -2,8 +2,6 @@
 
 test_description='apply empty'
 
-
-TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 13/27] revisions API users: use release_revisions() with UNLEAK()
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (11 preceding siblings ...)
  2022-03-23 20:32   ` [PATCH v2 12/27] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:32   ` Ævar Arnfjörð Bjarmason
  2022-03-23 20:32   ` [PATCH v2 14/27] revisions API users: use release_revisions() in submodule.c edge case Ævar Arnfjörð Bjarmason
                     ` (15 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:32 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Use a release_revisions() with those "struct rev_list" users which
already "UNLEAK" the struct. It may seem odd to simultaneously attempt
to free() memory, but also to explicitly ignore whether we have memory
leaks in the same.

As explained in preceding commits this is being done to use the
built-in commands as a guinea pig for whether the release_revisions()
function works as expected, we'd like to test e.g. whether we segfault
as we change it. In subsequent commits we'll then remove these
UNLEAK() as the function is made to free the memory that caused us to
add them in the first place.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/diff-index.c | 4 +++-
 builtin/diff.c       | 1 +
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 5fd23ab5b6c..3a83183c312 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -71,5 +71,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
 	}
 	result = run_diff_index(&rev, option);
 	UNLEAK(rev);
-	return diff_result_code(&rev.diffopt, result);
+	result = diff_result_code(&rev.diffopt, result);
+	release_revisions(&rev);
+	return result;
 }
diff --git a/builtin/diff.c b/builtin/diff.c
index bb7fafca618..dd48336da56 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -595,6 +595,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
 	if (1 < rev.diffopt.skip_stat_unmatch)
 		refresh_index_quietly();
 	UNLEAK(rev);
+	release_revisions(&rev);
 	UNLEAK(ent);
 	UNLEAK(blob);
 	return result;
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 14/27] revisions API users: use release_revisions() in submodule.c edge case
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (12 preceding siblings ...)
  2022-03-23 20:32   ` [PATCH v2 13/27] revisions API users: use release_revisions() with UNLEAK() Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:32   ` Ævar Arnfjörð Bjarmason
  2022-03-25  1:07     ` Junio C Hamano
  2022-03-23 20:32   ` [PATCH v2 15/27] revisions API users: use release_revisions() for "prune_data" users Ævar Arnfjörð Bjarmason
                     ` (14 subsequent siblings)
  28 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:32 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Use release_revisions() on the the "struct rev_info" in
show_submodule_diff_summary() where we need to pre-initialize with "{
0 }" to make sure that we won't segfault when calling
release_revisions().

Most functions that use "struct rev_info" will call
repo_init_revisions() early, which will do an equivalent memset(). In
this case we might "goto out" before doing the equivalent of that
operation, see the "goto" on "(!left || !right || !sub)" added in
8e6df65015f (submodule: refactor show_submodule_summary with helper
function, 2016-08-31).

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 submodule.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/submodule.c b/submodule.c
index 0510cb193b6..285c1896c9e 100644
--- a/submodule.c
+++ b/submodule.c
@@ -638,7 +638,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,
 		struct object_id *one, struct object_id *two,
 		unsigned dirty_submodule)
 {
-	struct rev_info rev;
+	struct rev_info rev = { 0 };
 	struct commit *left = NULL, *right = NULL;
 	struct commit_list *merge_bases = NULL;
 	struct repository *sub;
@@ -664,6 +664,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,
 	print_submodule_diff_summary(sub, &rev, o);
 
 out:
+	release_revisions(&rev);
 	if (merge_bases)
 		free_commit_list(merge_bases);
 	clear_commit_marks(left, ~0);
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 15/27] revisions API users: use release_revisions() for "prune_data" users
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (13 preceding siblings ...)
  2022-03-23 20:32   ` [PATCH v2 14/27] revisions API users: use release_revisions() in submodule.c edge case Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:32   ` Ævar Arnfjörð Bjarmason
  2022-03-23 20:32   ` [PATCH v2 16/27] revisions API: have release_revisions() release "commits" Ævar Arnfjörð Bjarmason
                     ` (13 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:32 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Use release_revisions() for users of "struct rev_list" that reach into
the "struct rev_info" and clear the "prune_data" already.

In a subsequent commit we'll teach release_revisions() to clear this
itself, but in the meantime let's invoke release_revisions() here to
clear anything else we may have missed, and for reasons of having
consistent boilerplate.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/add.c   | 1 +
 builtin/stash.c | 1 +
 diff-lib.c      | 1 +
 wt-status.c     | 1 +
 4 files changed, 4 insertions(+)

diff --git a/builtin/add.c b/builtin/add.c
index f507d2191cd..115a26ea633 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -143,6 +143,7 @@ int add_files_to_cache(const char *prefix,
 	rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
 	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 	return !!data.add_errors;
 }
 
diff --git a/builtin/stash.c b/builtin/stash.c
index 4fc717d5578..878eaf2e125 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1061,6 +1061,7 @@ static int check_changes_tracked_files(const struct pathspec *ps)
 
 done:
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 	return ret;
 }
 
diff --git a/diff-lib.c b/diff-lib.c
index d6800274bd5..0f16281253f 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -642,6 +642,7 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
 	if (diff_cache(&revs, tree_oid, NULL, 1))
 		exit(128);
 	clear_pathspec(&revs.prune_data);
+	release_revisions(&revs);
 	return 0;
 }
 
diff --git a/wt-status.c b/wt-status.c
index f9100621375..a14fad1e03a 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -617,6 +617,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_files(&rev, 0);
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 }
 
 static void wt_status_collect_changes_index(struct wt_status *s)
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 16/27] revisions API: have release_revisions() release "commits"
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (14 preceding siblings ...)
  2022-03-23 20:32   ` [PATCH v2 15/27] revisions API users: use release_revisions() for "prune_data" users Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:32   ` Ævar Arnfjörð Bjarmason
  2022-03-23 20:32   ` [PATCH v2 17/27] revisions API: have release_revisions() release "mailmap" Ævar Arnfjörð Bjarmason
                     ` (12 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:32 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"commits" in the "struct ref_info".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/revision.c b/revision.c
index 90bac9ada03..13b568aea76 100644
--- a/revision.c
+++ b/revision.c
@@ -2937,6 +2937,7 @@ void release_revisions(struct rev_info *revs)
 {
 	if (!revs)
 		return;
+	release_revisions_commit_list(revs);
 	object_array_clear(&revs->pending);
 }
 
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 17/27] revisions API: have release_revisions() release "mailmap"
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (15 preceding siblings ...)
  2022-03-23 20:32   ` [PATCH v2 16/27] revisions API: have release_revisions() release "commits" Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:32   ` Ævar Arnfjörð Bjarmason
  2022-03-25  1:11     ` Junio C Hamano
  2022-03-23 20:32   ` [PATCH v2 18/27] revisions API: have release_revisions() release "cmdline" Ævar Arnfjörð Bjarmason
                     ` (11 subsequent siblings)
  28 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:32 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"prune_data" in the "struct ref_info".

The log family of functions now calls the clear_mailmap() function
added in fa8afd18e5a (revisions API: provide and use a
release_revisions(), 2021-09-19), allowing us to whitelist some tests
with "TEST_PASSES_SANITIZE_LEAK=true".

Unfortunately having a pointer to a mailmap in "struct rev_info"
instead of an embedded member that we "own" get a bit messy, as can be
seen in the change to builtin/commit.c.

When we free() this data we won't be able to tell apart a pointer to a
"mailmap" on the heap from one on the stack. As seen in
ea57bc0d41b (log: add --use-mailmap option, 2013-01-05) the "log"
family allocates it on the heap, but in the find_author_by_nickname()
code added in ea16794e430 (commit: search author pattern against
mailmap, 2013-08-23) we allocated it on the stack instead.

Ideally we'd simply change that member to a "struct string_list
mailmap" and never free() the "mailmap" itself, but that would be a
much larger change to the revisions API.

We have code that needs to hand an existing "mailmap" to a "struct
rev_info", while we could change all of that, let's not go there
now.

The complexity isn't in the ownership of the "mailmap" per-se, but
that various things assume a "rev_info.mailmap == NULL" means "doesn't
want mailmap", if we changed that to an init'd "struct string_list
we'd need to carefully refactor things to change those assumptions.

Let's instead always free() it, and simply declare that if you add
such a "mailmap" it must be allocated on the heap. Any modern libc
will correctly panic if we free() a stack variable, so this should be
safe going forward.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/commit.c                   | 5 ++---
 revision.c                         | 9 +++++++++
 t/t0056-git-C.sh                   | 1 +
 t/t3302-notes-index-expensive.sh   | 1 +
 t/t4055-diff-context.sh            | 1 +
 t/t4066-diff-emit-delay.sh         | 1 +
 t/t7008-filter-branch-null-sha1.sh | 1 +
 7 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index c7eda9bbb72..cd6cebcf8c8 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1100,7 +1100,6 @@ static const char *find_author_by_nickname(const char *name)
 	struct rev_info revs;
 	struct commit *commit;
 	struct strbuf buf = STRBUF_INIT;
-	struct string_list mailmap = STRING_LIST_INIT_NODUP;
 	const char *av[20];
 	int ac = 0;
 
@@ -1111,7 +1110,8 @@ static const char *find_author_by_nickname(const char *name)
 	av[++ac] = buf.buf;
 	av[++ac] = NULL;
 	setup_revisions(ac, av, &revs, NULL);
-	revs.mailmap = &mailmap;
+	revs.mailmap = xmalloc(sizeof(struct string_list));
+	string_list_init_nodup(revs.mailmap);
 	read_mailmap(revs.mailmap);
 
 	if (prepare_revision_walk(&revs))
@@ -1122,7 +1122,6 @@ static const char *find_author_by_nickname(const char *name)
 		ctx.date_mode.type = DATE_NORMAL;
 		strbuf_release(&buf);
 		format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
-		clear_mailmap(&mailmap);
 		release_revisions(&revs);
 		return strbuf_detach(&buf, NULL);
 	}
diff --git a/revision.c b/revision.c
index 13b568aea76..d9e2b171f6d 100644
--- a/revision.c
+++ b/revision.c
@@ -2933,12 +2933,21 @@ static void release_revisions_commit_list(struct rev_info *revs)
 	revs->commits = NULL;
 }
 
+static void release_revisions_mailmap(struct string_list *mailmap)
+{
+	if (!mailmap)
+		return;
+	clear_mailmap(mailmap);
+	free(mailmap);
+}
+
 void release_revisions(struct rev_info *revs)
 {
 	if (!revs)
 		return;
 	release_revisions_commit_list(revs);
 	object_array_clear(&revs->pending);
+	release_revisions_mailmap(revs->mailmap);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
diff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh
index 2630e756dab..752aa8c9454 100755
--- a/t/t0056-git-C.sh
+++ b/t/t0056-git-C.sh
@@ -2,6 +2,7 @@
 
 test_description='"-C <path>" option and its effects on other path-related options'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success '"git -C <path>" runs git from the directory <path>' '
diff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh
index bb5fea02a03..d0c4d38b4d4 100755
--- a/t/t3302-notes-index-expensive.sh
+++ b/t/t3302-notes-index-expensive.sh
@@ -8,6 +8,7 @@ test_description='Test commit notes index (expensive!)'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 create_repo () {
diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh
index 741e0803c1a..73048d0a526 100755
--- a/t/t4055-diff-context.sh
+++ b/t/t4055-diff-context.sh
@@ -5,6 +5,7 @@
 
 test_description='diff.context configuration'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t4066-diff-emit-delay.sh b/t/t4066-diff-emit-delay.sh
index a1de63b77f8..0ecb3915412 100755
--- a/t/t4066-diff-emit-delay.sh
+++ b/t/t4066-diff-emit-delay.sh
@@ -4,6 +4,7 @@ test_description='test combined/stat/moved interaction'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # This test covers a weird 3-way interaction between "--cc -p", which will run
diff --git a/t/t7008-filter-branch-null-sha1.sh b/t/t7008-filter-branch-null-sha1.sh
index 9ba9f24ad2f..93fbc92b8db 100755
--- a/t/t7008-filter-branch-null-sha1.sh
+++ b/t/t7008-filter-branch-null-sha1.sh
@@ -1,6 +1,7 @@
 #!/bin/sh
 
 test_description='filter-branch removal of trees with null sha1'
+
 . ./test-lib.sh
 
 test_expect_success 'setup: base commits' '
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 18/27] revisions API: have release_revisions() release "cmdline"
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (16 preceding siblings ...)
  2022-03-23 20:32   ` [PATCH v2 17/27] revisions API: have release_revisions() release "mailmap" Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:32   ` Ævar Arnfjörð Bjarmason
  2022-03-25  1:16     ` Junio C Hamano
  2022-03-23 20:32   ` [PATCH v2 19/27] revisions API: have release_revisions() release "filter" Ævar Arnfjörð Bjarmason
                     ` (10 subsequent siblings)
  28 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:32 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"cmdline" in the "struct ref_info". This in combination with a
preceding change to free "commits" and "mailmap" means that we can
whitelist another test under "TEST_PASSES_SANITIZE_LEAK=true".

There was a proposal in [1] to do away with xstrdup()-ing this
add_rev_cmdline(), perhaps that would be worthwhile, but for now let's
just free() it.

We could also make that a "char *" in "struct rev_cmdline_entry"
itself, but since we own it let's expose it as a constant to outside
callers. I proposed that in [2] but have since changed my mind. See
14d30cdfc04 (ref-filter: fix memory leak in `free_array_item()`,
2019-07-10), c514c62a4fd (checkout: fix leak of non-existent branch
names, 2020-08-14) and other log history hits for "free((char *)" for
prior art.

This includes the tests we had false-positive passes on before my
6798b08e848 (perl Git.pm: don't ignore signalled failure in
_cmd_close(), 2022-02-01), now they pass for real.

Since there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to
list those that don't pass than to touch most of those 66. So let's
introduce a "TEST_FAILS_SANITIZE_LEAK=true", which if set in the tests
won't cause lib-git-svn.sh to set "TEST_PASSES_SANITIZE_LEAK=true.

This change also marks all the tests that we removed
"TEST_FAILS_SANITIZE_LEAK=true" from in an earlier commit due to
removing the UNLEAK() from cmd_format_patch(), we can now assert that
its API use doesn't leak any "struct rev_info" memory.

This change also made commit "t5503-tagfollow.sh" pass on current
master, but that would regress when combined with
ps/fetch-atomic-fixup's de004e848a9 (t5503: simplify setup of test
which exercises failure of backfill, 2022-03-03) (through no fault of
that topic, that change started using "git clone" in the test, which
has an outstanding leak). Let's leave that test out for now to avoid
in-flight semantic conflicts.

1. https://lore.kernel.org/git/YUj%2FgFRh6pwrZalY@carlos-mbp.lan/
2. https://lore.kernel.org/git/87o88obkb1.fsf@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>

fixup with bc0e0439040?
---
 revision.c                                 | 12 ++++++++++++
 t/lib-git-svn.sh                           |  4 ++++
 t/t0062-revision-walking.sh                |  1 +
 t/t0101-at-syntax.sh                       |  2 ++
 t/t1060-object-corruption.sh               |  1 +
 t/t3303-notes-subtrees.sh                  |  1 +
 t/t3305-notes-fanout.sh                    |  1 +
 t/t3408-rebase-multi-line.sh               |  1 +
 t/t4027-diff-submodule.sh                  |  1 +
 t/t4128-apply-root.sh                      |  1 +
 t/t4212-log-corrupt.sh                     |  1 +
 t/t5515-fetch-merge-logic.sh               |  1 +
 t/t5518-fetch-exit-status.sh               |  1 +
 t/t6002-rev-list-bisect.sh                 |  1 +
 t/t6003-rev-list-topo-order.sh             |  1 +
 t/t6005-rev-list-count.sh                  |  1 +
 t/t6018-rev-list-glob.sh                   |  1 +
 t/t6100-rev-list-in-order.sh               |  1 +
 t/t9100-git-svn-basic.sh                   |  1 +
 t/t9101-git-svn-props.sh                   |  2 ++
 t/t9104-git-svn-follow-parent.sh           |  2 ++
 t/t9106-git-svn-commit-diff-clobber.sh     |  2 ++
 t/t9115-git-svn-dcommit-funky-renames.sh   |  1 +
 t/t9116-git-svn-log.sh                     |  2 ++
 t/t9122-git-svn-author.sh                  |  2 ++
 t/t9127-git-svn-partial-rebuild.sh         |  2 ++
 t/t9129-git-svn-i18n-commitencoding.sh     |  1 +
 t/t9132-git-svn-broken-symlink.sh          |  1 +
 t/t9139-git-svn-non-utf8-commitencoding.sh |  1 +
 t/t9146-git-svn-empty-dirs.sh              |  2 ++
 t/t9148-git-svn-propset.sh                 |  1 +
 t/t9151-svn-mergeinfo.sh                   |  1 +
 t/t9160-git-svn-preserve-empty-dirs.sh     |  1 +
 t/t9162-git-svn-dcommit-interactive.sh     |  2 ++
 t/t9164-git-svn-dcommit-concurrent.sh      |  2 ++
 t/t9501-gitweb-standalone-http-status.sh   |  1 +
 36 files changed, 60 insertions(+)

diff --git a/revision.c b/revision.c
index d9e2b171f6d..f44298299a4 100644
--- a/revision.c
+++ b/revision.c
@@ -2933,6 +2933,17 @@ static void release_revisions_commit_list(struct rev_info *revs)
 	revs->commits = NULL;
 }
 
+static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)
+{
+	unsigned int i;
+
+	if (!cmdline)
+		return;
+	for (i = 0; i < cmdline->nr; i++)
+		free((char *)cmdline->rev[i].name);
+	FREE_AND_NULL(cmdline->rev);
+}
+
 static void release_revisions_mailmap(struct string_list *mailmap)
 {
 	if (!mailmap)
@@ -2947,6 +2958,7 @@ void release_revisions(struct rev_info *revs)
 		return;
 	release_revisions_commit_list(revs);
 	object_array_clear(&revs->pending);
+	release_revisions_cmdline(&revs->cmdline);
 	release_revisions_mailmap(revs->mailmap);
 }
 
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 2fde2353fd3..ea28971e8ee 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -1,3 +1,7 @@
+if test -z "$TEST_FAILS_SANITIZE_LEAK"
+then
+	TEST_PASSES_SANITIZE_LEAK=true
+fi
 . ./test-lib.sh
 
 if test -n "$NO_SVN_TESTS"
diff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh
index 8e215867b8c..b9480c81781 100755
--- a/t/t0062-revision-walking.sh
+++ b/t/t0062-revision-walking.sh
@@ -5,6 +5,7 @@
 
 test_description='Test revision walking api'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 cat >run_twice_expected <<-EOF
diff --git a/t/t0101-at-syntax.sh b/t/t0101-at-syntax.sh
index a1998b558f9..878aadd64c9 100755
--- a/t/t0101-at-syntax.sh
+++ b/t/t0101-at-syntax.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='various @{whatever} syntax tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh
index bc89371f534..e8a58b15897 100755
--- a/t/t1060-object-corruption.sh
+++ b/t/t1060-object-corruption.sh
@@ -1,6 +1,7 @@
 #!/bin/sh
 
 test_description='see how we handle various forms of corruption'
+
 . ./test-lib.sh
 
 # convert "1234abcd" to ".git/objects/12/34abcd"
diff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh
index eac193757bf..bc9b791d1b9 100755
--- a/t/t3303-notes-subtrees.sh
+++ b/t/t3303-notes-subtrees.sh
@@ -5,6 +5,7 @@ test_description='Test commit notes organized in subtrees'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 number_of_commits=100
diff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh
index 9976d787f47..64a9915761a 100755
--- a/t/t3305-notes-fanout.sh
+++ b/t/t3305-notes-fanout.sh
@@ -2,6 +2,7 @@
 
 test_description='Test that adding/removing many notes triggers automatic fanout restructuring'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 path_has_fanout() {
diff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh
index cde3562e3a6..7b4607d72f2 100755
--- a/t/t3408-rebase-multi-line.sh
+++ b/t/t3408-rebase-multi-line.sh
@@ -5,6 +5,7 @@ test_description='rebasing a commit with multi-line first paragraph.'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
index 295da987cce..40164ae07d2 100755
--- a/t/t4027-diff-submodule.sh
+++ b/t/t4027-diff-submodule.sh
@@ -2,6 +2,7 @@
 
 test_description='difference in submodules'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh
 
diff --git a/t/t4128-apply-root.sh b/t/t4128-apply-root.sh
index f6db5a79dd9..ed94c90204e 100755
--- a/t/t4128-apply-root.sh
+++ b/t/t4128-apply-root.sh
@@ -2,6 +2,7 @@
 
 test_description='apply same filename'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh
index 0244888a5a7..30a219894bb 100755
--- a/t/t4212-log-corrupt.sh
+++ b/t/t4212-log-corrupt.sh
@@ -2,6 +2,7 @@
 
 test_description='git log with invalid commit headers'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh
index 320d26796d2..c100a809c5e 100755
--- a/t/t5515-fetch-merge-logic.sh
+++ b/t/t5515-fetch-merge-logic.sh
@@ -14,6 +14,7 @@ export GIT_TEST_PROTOCOL_VERSION
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 build_script () {
diff --git a/t/t5518-fetch-exit-status.sh b/t/t5518-fetch-exit-status.sh
index 5c4ac2556e7..c13120088fa 100755
--- a/t/t5518-fetch-exit-status.sh
+++ b/t/t5518-fetch-exit-status.sh
@@ -8,6 +8,7 @@ test_description='fetch exit status test'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index b95a0212adf..162cf50778d 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -4,6 +4,7 @@
 #
 test_description='Tests git rev-list --bisect functionality'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
 
diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh
index 24d1836f417..1f7d7dd20c1 100755
--- a/t/t6003-rev-list-topo-order.sh
+++ b/t/t6003-rev-list-topo-order.sh
@@ -5,6 +5,7 @@
 
 test_description='Tests git rev-list --topo-order functionality'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
 
diff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh
index e960049f647..0729f800c3c 100755
--- a/t/t6005-rev-list-count.sh
+++ b/t/t6005-rev-list-count.sh
@@ -2,6 +2,7 @@
 
 test_description='git rev-list --max-count and --skip test'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
index 24b34add833..e1abc5c2b32 100755
--- a/t/t6018-rev-list-glob.sh
+++ b/t/t6018-rev-list-glob.sh
@@ -5,6 +5,7 @@ test_description='rev-list/rev-parse --glob'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 commit () {
diff --git a/t/t6100-rev-list-in-order.sh b/t/t6100-rev-list-in-order.sh
index e934bc239c5..88ed7bd75a7 100755
--- a/t/t6100-rev-list-in-order.sh
+++ b/t/t6100-rev-list-in-order.sh
@@ -2,6 +2,7 @@
 
 test_description='rev-list testing in-commit-order'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a commit history with trees, blobs' '
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index fea41b3c360..7c5b847f584 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -8,6 +8,7 @@ test_description='git svn basic tests'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 prepare_utf8_locale
diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
index 8b5681dd68a..d043e80fc34 100755
--- a/t/t9101-git-svn-props.sh
+++ b/t/t9101-git-svn-props.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn property tests'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 mkdir import
diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
index c7d8e0bf00f..5cf2ef4b8b0 100755
--- a/t/t9104-git-svn-follow-parent.sh
+++ b/t/t9104-git-svn-follow-parent.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn fetching'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
index aec45bca3b7..3cab0b9720a 100755
--- a/t/t9106-git-svn-commit-diff-clobber.sh
+++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -2,6 +2,8 @@
 #
 # Copyright (c) 2006 Eric Wong
 test_description='git svn commit-diff clobber'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh
index 743fbe1fe46..419f055721d 100755
--- a/t/t9115-git-svn-dcommit-funky-renames.sh
+++ b/t/t9115-git-svn-dcommit-funky-renames.sh
@@ -5,6 +5,7 @@
 
 test_description='git svn dcommit can commit renames of files with ugly names'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'load repository with strange names' '
diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh
index 0a9f1ef366d..34f6c80dea3 100755
--- a/t/t9116-git-svn-log.sh
+++ b/t/t9116-git-svn-log.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn log tests'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup repository and import' '
diff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh
index 9e8fe38e7ef..527ba3d2932 100755
--- a/t/t9122-git-svn-author.sh
+++ b/t/t9122-git-svn-author.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='git svn authorship'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup svn repository' '
diff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh
index 2e4789d061f..90b1b30dde5 100755
--- a/t/t9127-git-svn-partial-rebuild.sh
+++ b/t/t9127-git-svn-partial-rebuild.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn partial-rebuild tests'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize svnrepo' '
diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh
index 01e1e8a8f76..185248a4cd7 100755
--- a/t/t9129-git-svn-i18n-commitencoding.sh
+++ b/t/t9129-git-svn-i18n-commitencoding.sh
@@ -4,6 +4,7 @@
 
 test_description='git svn honors i18n.commitEncoding in config'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 compare_git_head_with () {
diff --git a/t/t9132-git-svn-broken-symlink.sh b/t/t9132-git-svn-broken-symlink.sh
index aeceffaf7b0..4d8d0584b79 100755
--- a/t/t9132-git-svn-broken-symlink.sh
+++ b/t/t9132-git-svn-broken-symlink.sh
@@ -2,6 +2,7 @@
 
 test_description='test that git handles an svn repository with empty symlinks'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 test_expect_success 'load svn dumpfile' '
 	svnadmin load "$rawsvnrepo" <<EOF
diff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh
index 22d80b0be2b..b7f756b2b7f 100755
--- a/t/t9139-git-svn-non-utf8-commitencoding.sh
+++ b/t/t9139-git-svn-non-utf8-commitencoding.sh
@@ -4,6 +4,7 @@
 
 test_description='git svn refuses to dcommit non-UTF8 messages'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 # ISO-2022-JP can pass for valid UTF-8, so skipping that in this test
diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh
index 80cb55fee70..79c26ed69c1 100755
--- a/t/t9146-git-svn-empty-dirs.sh
+++ b/t/t9146-git-svn-empty-dirs.sh
@@ -3,6 +3,8 @@
 # Copyright (c) 2009 Eric Wong
 
 test_description='git svn creates empty directories'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9148-git-svn-propset.sh b/t/t9148-git-svn-propset.sh
index aebb28995e5..6cc76a07b39 100755
--- a/t/t9148-git-svn-propset.sh
+++ b/t/t9148-git-svn-propset.sh
@@ -5,6 +5,7 @@
 
 test_description='git svn propset tests'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup propset via import' '
diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh
index c93a5beab25..85221d439bd 100755
--- a/t/t9151-svn-mergeinfo.sh
+++ b/t/t9151-svn-mergeinfo.sh
@@ -5,6 +5,7 @@
 
 test_description='git-svn svn mergeinfo properties'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'load svn dump' "
diff --git a/t/t9160-git-svn-preserve-empty-dirs.sh b/t/t9160-git-svn-preserve-empty-dirs.sh
index 36c6b1a12ff..9cf7a1427ab 100755
--- a/t/t9160-git-svn-preserve-empty-dirs.sh
+++ b/t/t9160-git-svn-preserve-empty-dirs.sh
@@ -9,6 +9,7 @@ This test uses git to clone a Subversion repository that contains empty
 directories, and checks that corresponding directories are created in the
 local Git repository with placeholder files.'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 GIT_REPO=git-svn-repo
diff --git a/t/t9162-git-svn-dcommit-interactive.sh b/t/t9162-git-svn-dcommit-interactive.sh
index e38d9fa37b5..e2aa8ed88a9 100755
--- a/t/t9162-git-svn-dcommit-interactive.sh
+++ b/t/t9162-git-svn-dcommit-interactive.sh
@@ -3,6 +3,8 @@
 # Copyright (c) 2011 Frédéric Heitzmann
 
 test_description='git svn dcommit --interactive series'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9164-git-svn-dcommit-concurrent.sh b/t/t9164-git-svn-dcommit-concurrent.sh
index 8466269bf50..1465156072e 100755
--- a/t/t9164-git-svn-dcommit-concurrent.sh
+++ b/t/t9164-git-svn-dcommit-concurrent.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='concurrent git svn dcommit'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 
diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh
index 32814e75df5..c900231079c 100755
--- a/t/t9501-gitweb-standalone-http-status.sh
+++ b/t/t9501-gitweb-standalone-http-status.sh
@@ -13,6 +13,7 @@ code and message.'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./lib-gitweb.sh
 
 #
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 19/27] revisions API: have release_revisions() release "filter"
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (17 preceding siblings ...)
  2022-03-23 20:32   ` [PATCH v2 18/27] revisions API: have release_revisions() release "cmdline" Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:32   ` Ævar Arnfjörð Bjarmason
  2022-03-25  1:17     ` Junio C Hamano
  2022-03-23 20:32   ` [PATCH v2 20/27] revisions API: have release_revisions() release "grep_filter" Ævar Arnfjörð Bjarmason
                     ` (9 subsequent siblings)
  28 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:32 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"cmdline" in the "struct ref_info". This in combination with a
preceding change to free "cmdline" means that we can mark another set
of tests as passing under "TEST_PASSES_SANITIZE_LEAK=true".

The "filter" member was added recently in ffaa137f646 (revision: put
object filter into struct rev_info, 2022-03-09), and this fixes leaks
intruded in the subsequent leak 7940941de1f (pack-objects: use
rev.filter when possible, 2022-03-09) and 105c6f14ad3 (bundle: parse
filter capability, 2022-03-09).

The "builtin/pack-objects.c" leak in 7940941de1f was effectively with
us already, but the variable was referred to by a "static" file-scoped
variable. The "bundle.c " leak in 105c6f14ad3 was newly introduced
with the new "filter" feature for bundles.

The "t5600-clone-fail-cleanup.sh" change here to add
"TEST_PASSES_SANITIZE_LEAK=true" is one of the cases where
run-command.c in not carrying the abort() exit code upwards would have
had that test passing before, but now it *actually* passes[1]. We
should fix the lack of 1=1 mapping of SANITIZE=leak testing to actual
leaks some other time, but it's an existing edge case, let's just mark
the really-passing test as passing for now.

1. https://lore.kernel.org/git/220303.86fsnz5o9w.gmgdl@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c                         | 1 +
 t/t1060-object-corruption.sh       | 1 +
 t/t2015-checkout-unborn.sh         | 1 +
 t/t4207-log-decoration-colors.sh   | 1 +
 t/t5301-sliding-window.sh          | 2 ++
 t/t5313-pack-bounds-checks.sh      | 2 ++
 t/t5316-pack-delta-depth.sh        | 2 ++
 t/t5320-delta-islands.sh           | 2 ++
 t/t5322-pack-objects-sparse.sh     | 1 +
 t/t5506-remote-groups.sh           | 1 +
 t/t5513-fetch-track.sh             | 1 +
 t/t5532-fetch-proxy.sh             | 2 ++
 t/t5600-clone-fail-cleanup.sh      | 1 +
 t/t5900-repo-selection.sh          | 2 ++
 t/t6101-rev-parse-parents.sh       | 1 +
 t/t6114-keep-packs.sh              | 2 ++
 t/t7702-repack-cyclic-alternate.sh | 2 ++
 t/t9127-git-svn-partial-rebuild.sh | 1 -
 18 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/revision.c b/revision.c
index f44298299a4..71f4ef3530d 100644
--- a/revision.c
+++ b/revision.c
@@ -2959,6 +2959,7 @@ void release_revisions(struct rev_info *revs)
 	release_revisions_commit_list(revs);
 	object_array_clear(&revs->pending);
 	release_revisions_cmdline(&revs->cmdline);
+	list_objects_filter_release(&revs->filter);
 	release_revisions_mailmap(revs->mailmap);
 }
 
diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh
index e8a58b15897..5b8e47e346c 100755
--- a/t/t1060-object-corruption.sh
+++ b/t/t1060-object-corruption.sh
@@ -2,6 +2,7 @@
 
 test_description='see how we handle various forms of corruption'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # convert "1234abcd" to ".git/objects/12/34abcd"
diff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh
index a9721215fab..9425aae6395 100755
--- a/t/t2015-checkout-unborn.sh
+++ b/t/t2015-checkout-unborn.sh
@@ -4,6 +4,7 @@ test_description='checkout from unborn branch'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh
index b8709424981..36ac6aff1e4 100755
--- a/t/t4207-log-decoration-colors.sh
+++ b/t/t4207-log-decoration-colors.sh
@@ -8,6 +8,7 @@ test_description='Test for "git log --decorate" colors'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t5301-sliding-window.sh b/t/t5301-sliding-window.sh
index 76f9798ab95..3ccaaeb3977 100755
--- a/t/t5301-sliding-window.sh
+++ b/t/t5301-sliding-window.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='mmap sliding window tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success \
diff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh
index 535313e4dc8..cc4cfaa9d37 100755
--- a/t/t5313-pack-bounds-checks.sh
+++ b/t/t5313-pack-bounds-checks.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='bounds-checking of access to mmapped on-disk file formats'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 clear_base () {
diff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh
index e9045009a11..eb4ef3dda4d 100755
--- a/t/t5316-pack-delta-depth.sh
+++ b/t/t5316-pack-delta-depth.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='pack-objects breaks long cross-pack delta chains'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # This mirrors a repeated push setup:
diff --git a/t/t5320-delta-islands.sh b/t/t5320-delta-islands.sh
index fea92a5777f..124d47603df 100755
--- a/t/t5320-delta-islands.sh
+++ b/t/t5320-delta-islands.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='exercise delta islands'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # returns true iff $1 is a delta based on $2
diff --git a/t/t5322-pack-objects-sparse.sh b/t/t5322-pack-objects-sparse.sh
index d39958c066d..770695c9278 100755
--- a/t/t5322-pack-objects-sparse.sh
+++ b/t/t5322-pack-objects-sparse.sh
@@ -4,6 +4,7 @@ test_description='pack-objects object selection using sparse algorithm'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup repo' '
diff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh
index 8f150c0793e..5bac03ede81 100755
--- a/t/t5506-remote-groups.sh
+++ b/t/t5506-remote-groups.sh
@@ -4,6 +4,7 @@ test_description='git remote group handling'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 mark() {
diff --git a/t/t5513-fetch-track.sh b/t/t5513-fetch-track.sh
index 65d1e05bd62..c46c4dbaefc 100755
--- a/t/t5513-fetch-track.sh
+++ b/t/t5513-fetch-track.sh
@@ -2,6 +2,7 @@
 
 test_description='fetch follows remote-tracking branches correctly'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh
index 9c2798603b4..d664912799b 100755
--- a/t/t5532-fetch-proxy.sh
+++ b/t/t5532-fetch-proxy.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='fetching via git:// using core.gitproxy'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup remote repo' '
diff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh
index 34b3df40275..c814afa5656 100755
--- a/t/t5600-clone-fail-cleanup.sh
+++ b/t/t5600-clone-fail-cleanup.sh
@@ -13,6 +13,7 @@ Unless the directory already exists, in which case we clean up only what we
 wrote.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 corrupt_repo () {
diff --git a/t/t5900-repo-selection.sh b/t/t5900-repo-selection.sh
index 14e59c5b3e4..a84faac242d 100755
--- a/t/t5900-repo-selection.sh
+++ b/t/t5900-repo-selection.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='selecting remote repo in ambiguous cases'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 reset() {
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
index c571fa51797..a3a41c7a3e4 100755
--- a/t/t6101-rev-parse-parents.sh
+++ b/t/t6101-rev-parse-parents.sh
@@ -8,6 +8,7 @@ test_description='Test git rev-parse with different parent options'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_cmp_rev_output () {
diff --git a/t/t6114-keep-packs.sh b/t/t6114-keep-packs.sh
index 9239d8aa46d..44246f8a63e 100755
--- a/t/t6114-keep-packs.sh
+++ b/t/t6114-keep-packs.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='rev-list with .keep packs'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t7702-repack-cyclic-alternate.sh b/t/t7702-repack-cyclic-alternate.sh
index 93b74867ac8..f3cdb98eec2 100755
--- a/t/t7702-repack-cyclic-alternate.sh
+++ b/t/t7702-repack-cyclic-alternate.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='repack involving cyclic alternate'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh
index 90b1b30dde5..97f495bd49b 100755
--- a/t/t9127-git-svn-partial-rebuild.sh
+++ b/t/t9127-git-svn-partial-rebuild.sh
@@ -5,7 +5,6 @@
 
 test_description='git svn partial-rebuild tests'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize svnrepo' '
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 20/27] revisions API: have release_revisions() release "grep_filter"
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (18 preceding siblings ...)
  2022-03-23 20:32   ` [PATCH v2 19/27] revisions API: have release_revisions() release "filter" Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:32   ` Ævar Arnfjörð Bjarmason
  2022-03-23 20:32   ` [PATCH v2 21/27] revisions API: have release_revisions() release "prune_data" Ævar Arnfjörð Bjarmason
                     ` (8 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:32 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"grep_filter" in the "struct ref_info".This allows us to mark a test
as passing under "TEST_PASSES_SANITIZE_LEAK=true".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c               | 1 +
 t/t9151-svn-mergeinfo.sh | 1 -
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/revision.c b/revision.c
index 71f4ef3530d..eefb188d05e 100644
--- a/revision.c
+++ b/revision.c
@@ -2961,6 +2961,7 @@ void release_revisions(struct rev_info *revs)
 	release_revisions_cmdline(&revs->cmdline);
 	list_objects_filter_release(&revs->filter);
 	release_revisions_mailmap(revs->mailmap);
+	free_grep_patterns(&revs->grep_filter);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh
index 85221d439bd..c93a5beab25 100755
--- a/t/t9151-svn-mergeinfo.sh
+++ b/t/t9151-svn-mergeinfo.sh
@@ -5,7 +5,6 @@
 
 test_description='git-svn svn mergeinfo properties'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'load svn dump' "
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 21/27] revisions API: have release_revisions() release "prune_data"
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (19 preceding siblings ...)
  2022-03-23 20:32   ` [PATCH v2 20/27] revisions API: have release_revisions() release "grep_filter" Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:32   ` Ævar Arnfjörð Bjarmason
  2022-03-25  1:20     ` Junio C Hamano
  2022-03-23 20:32   ` [PATCH v2 22/27] revisions API: clear "boundary_commits" in release_revisions() Ævar Arnfjörð Bjarmason
                     ` (7 subsequent siblings)
  28 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:32 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"prune_data" in the "struct ref_info". This means that any code that
calls "release_revisions()" already can get rid of adjacent calls to
clear_pathspec().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 add-interactive.c | 2 --
 builtin/add.c     | 1 -
 builtin/stash.c   | 2 --
 diff-lib.c        | 1 -
 revision.c        | 1 +
 wt-status.c       | 2 --
 6 files changed, 1 insertion(+), 8 deletions(-)

diff --git a/add-interactive.c b/add-interactive.c
index 54cdfc82017..6047e8f6489 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -568,8 +568,6 @@ static int get_modified_files(struct repository *r,
 			run_diff_files(&rev, 0);
 		}
 
-		if (ps)
-			clear_pathspec(&rev.prune_data);
 		release_revisions(&rev);
 	}
 	hashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);
diff --git a/builtin/add.c b/builtin/add.c
index 115a26ea633..fc729e14c17 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -142,7 +142,6 @@ int add_files_to_cache(const char *prefix,
 	rev.diffopt.flags.override_submodule_config = 1;
 	rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
 	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 	return !!data.add_errors;
 }
diff --git a/builtin/stash.c b/builtin/stash.c
index 878eaf2e125..ac83d7f8af4 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1060,7 +1060,6 @@ static int check_changes_tracked_files(const struct pathspec *ps)
 	}
 
 done:
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 	return ret;
 }
@@ -1272,7 +1271,6 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
 
 done:
 	discard_index(&istate);
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 	strbuf_release(&diff_output);
 	remove_path(stash_index_path.buf);
diff --git a/diff-lib.c b/diff-lib.c
index 0f16281253f..298265e5b54 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -641,7 +641,6 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
 
 	if (diff_cache(&revs, tree_oid, NULL, 1))
 		exit(128);
-	clear_pathspec(&revs.prune_data);
 	release_revisions(&revs);
 	return 0;
 }
diff --git a/revision.c b/revision.c
index eefb188d05e..0f9ded78f5b 100644
--- a/revision.c
+++ b/revision.c
@@ -2960,6 +2960,7 @@ void release_revisions(struct rev_info *revs)
 	object_array_clear(&revs->pending);
 	release_revisions_cmdline(&revs->cmdline);
 	list_objects_filter_release(&revs->filter);
+	clear_pathspec(&revs->prune_data);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
 }
diff --git a/wt-status.c b/wt-status.c
index a14fad1e03a..61e0c1022f5 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -616,7 +616,6 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
 	rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_files(&rev, 0);
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 }
 
@@ -664,7 +663,6 @@ static void wt_status_collect_changes_index(struct wt_status *s)
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_index(&rev, 1);
 	release_revisions(&rev);
-	clear_pathspec(&rev.prune_data);
 }
 
 static int add_file_to_list(const struct object_id *oid,
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 22/27] revisions API: clear "boundary_commits" in release_revisions()
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (20 preceding siblings ...)
  2022-03-23 20:32   ` [PATCH v2 21/27] revisions API: have release_revisions() release "prune_data" Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:32   ` Ævar Arnfjörð Bjarmason
  2022-03-23 20:32   ` [PATCH v2 23/27] revisions API: release "reflog_info" in release revisions() Ævar Arnfjörð Bjarmason
                     ` (6 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:32 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Clear the "boundary_commits" object_array in release_revisions(). This
makes a few more tests pass under SANITIZE=leak, including
"t/t4126-apply-empty.sh" which started failed as an UNLEAK() in
cmd_format_patch() was removed in a preceding commit.

This also re-marks the various tests relying on "git format-patch" as
passing under "SANITIZE=leak", in the preceding "revisions API users:
use release_revisions() in builtin/log.c" commit those were marked as
failing as we removed the UNLEAK(rev) from cmd_format_patch() in
"builtin/log.c".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c                           | 1 +
 t/t4021-format-patch-numbered.sh     | 1 +
 t/t4028-format-patch-mime-headers.sh | 2 ++
 t/t4036-format-patch-signer-mime.sh  | 1 +
 t/t4122-apply-symlink-inside.sh      | 1 +
 t/t4126-apply-empty.sh               | 1 +
 t/t6110-rev-list-sparse.sh           | 1 +
 t/t9001-send-email.sh                | 1 +
 t/t9116-git-svn-log.sh               | 1 -
 9 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/revision.c b/revision.c
index 0f9ded78f5b..020f32f6eca 100644
--- a/revision.c
+++ b/revision.c
@@ -2958,6 +2958,7 @@ void release_revisions(struct rev_info *revs)
 		return;
 	release_revisions_commit_list(revs);
 	object_array_clear(&revs->pending);
+	object_array_clear(&revs->boundary_commits);
 	release_revisions_cmdline(&revs->cmdline);
 	list_objects_filter_release(&revs->filter);
 	clear_pathspec(&revs->prune_data);
diff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh
index 9be65fd4440..1219aa226dc 100755
--- a/t/t4021-format-patch-numbered.sh
+++ b/t/t4021-format-patch-numbered.sh
@@ -5,6 +5,7 @@
 
 test_description='Format-patch numbering options'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4028-format-patch-mime-headers.sh b/t/t4028-format-patch-mime-headers.sh
index 204ba673cb5..60cb819c42e 100755
--- a/t/t4028-format-patch-mime-headers.sh
+++ b/t/t4028-format-patch-mime-headers.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='format-patch mime headers and extra headers do not conflict'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create commit with utf-8 body' '
diff --git a/t/t4036-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh
index 98d9713d8b2..48655bcc789 100755
--- a/t/t4036-format-patch-signer-mime.sh
+++ b/t/t4036-format-patch-signer-mime.sh
@@ -2,6 +2,7 @@
 
 test_description='format-patch -s should force MIME encoding as needed'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh
index aa52de401b9..96965373036 100755
--- a/t/t4122-apply-symlink-inside.sh
+++ b/t/t4122-apply-symlink-inside.sh
@@ -4,6 +4,7 @@ test_description='apply to deeper directory without getting fooled with symlink'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh
index 66a7ba8ab8f..ece9fae207d 100755
--- a/t/t4126-apply-empty.sh
+++ b/t/t4126-apply-empty.sh
@@ -2,6 +2,7 @@
 
 test_description='apply empty'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t6110-rev-list-sparse.sh b/t/t6110-rev-list-sparse.sh
index 13c1da53528..ddefc7f24ee 100755
--- a/t/t6110-rev-list-sparse.sh
+++ b/t/t6110-rev-list-sparse.sh
@@ -4,6 +4,7 @@ test_description='operations that cull histories in unusual ways'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 84d0f40d76a..dfa6b20f7a6 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -4,6 +4,7 @@ test_description='git send-email'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # May be altered later in the test
diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh
index 34f6c80dea3..d74d7b2de68 100755
--- a/t/t9116-git-svn-log.sh
+++ b/t/t9116-git-svn-log.sh
@@ -5,7 +5,6 @@
 
 test_description='git svn log tests'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup repository and import' '
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 23/27] revisions API: release "reflog_info" in release revisions()
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (21 preceding siblings ...)
  2022-03-23 20:32   ` [PATCH v2 22/27] revisions API: clear "boundary_commits" in release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:32   ` Ævar Arnfjörð Bjarmason
  2022-03-23 20:32   ` [PATCH v2 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release() Ævar Arnfjörð Bjarmason
                     ` (5 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:32 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Add a missing reflog_walk_info_release() to "reflog-walk.c" and use it
in release_revisions().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 reflog-walk.c            | 24 +++++++++++++++++++++++-
 reflog-walk.h            |  1 +
 revision.c               |  1 +
 t/t0100-previous.sh      |  1 +
 t/t1401-symbolic-ref.sh  |  2 ++
 t/t1411-reflog-show.sh   |  1 +
 t/t1412-reflog-loop.sh   |  2 ++
 t/t1415-worktree-refs.sh |  1 +
 8 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/reflog-walk.c b/reflog-walk.c
index 8ac4b284b6b..7aa6595a51f 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -8,7 +8,7 @@
 
 struct complete_reflogs {
 	char *ref;
-	const char *short_ref;
+	char *short_ref;
 	struct reflog_info {
 		struct object_id ooid, noid;
 		char *email;
@@ -51,9 +51,16 @@ static void free_complete_reflog(struct complete_reflogs *array)
 	}
 	free(array->items);
 	free(array->ref);
+	free(array->short_ref);
 	free(array);
 }
 
+static void complete_reflogs_clear(void *util, const char *str)
+{
+	struct complete_reflogs *array = util;
+	free_complete_reflog(array);
+}
+
 static struct complete_reflogs *read_complete_reflog(const char *ref)
 {
 	struct complete_reflogs *reflogs =
@@ -116,6 +123,21 @@ void init_reflog_walk(struct reflog_walk_info **info)
 	(*info)->complete_reflogs.strdup_strings = 1;
 }
 
+void reflog_walk_info_release(struct reflog_walk_info *info)
+{
+	size_t i;
+
+	if (!info)
+		return;
+
+	for (i = 0; i < info->nr; i++)
+		free(info->logs[i]);
+	string_list_clear_func(&info->complete_reflogs,
+			       complete_reflogs_clear);
+	free(info->logs);
+	free(info);
+}
+
 int add_reflog_for_walk(struct reflog_walk_info *info,
 		struct commit *commit, const char *name)
 {
diff --git a/reflog-walk.h b/reflog-walk.h
index e9e00ffd479..8076f10d9fb 100644
--- a/reflog-walk.h
+++ b/reflog-walk.h
@@ -8,6 +8,7 @@ struct reflog_walk_info;
 struct date_mode;
 
 void init_reflog_walk(struct reflog_walk_info **info);
+void reflog_walk_info_release(struct reflog_walk_info *info);
 int add_reflog_for_walk(struct reflog_walk_info *info,
 			struct commit *commit, const char *name);
 void show_reflog_message(struct reflog_walk_info *info, int,
diff --git a/revision.c b/revision.c
index 020f32f6eca..ce23590a219 100644
--- a/revision.c
+++ b/revision.c
@@ -2964,6 +2964,7 @@ void release_revisions(struct rev_info *revs)
 	clear_pathspec(&revs->prune_data);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
+	reflog_walk_info_release(revs->reflog_info);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh
index 69beb59f627..a16cc3d2983 100755
--- a/t/t0100-previous.sh
+++ b/t/t0100-previous.sh
@@ -5,6 +5,7 @@ test_description='previous branch syntax @{-n}'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'branch -d @{-1}' '
diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh
index 132a1b885ac..9fb0b90f252 100755
--- a/t/t1401-symbolic-ref.sh
+++ b/t/t1401-symbolic-ref.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='basic symbolic-ref tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # If the tests munging HEAD fail, they can break detection of
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
index 0bb319b944a..3770ceffafd 100755
--- a/t/t1411-reflog-show.sh
+++ b/t/t1411-reflog-show.sh
@@ -4,6 +4,7 @@ test_description='Test reflog display routines'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t1412-reflog-loop.sh b/t/t1412-reflog-loop.sh
index 977603f7f1c..ff30874f940 100755
--- a/t/t1412-reflog-loop.sh
+++ b/t/t1412-reflog-loop.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='reflog walk shows repeated commits again'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup commits' '
diff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh
index a3e6ea08088..3b531842dd4 100755
--- a/t/t1415-worktree-refs.sh
+++ b/t/t1415-worktree-refs.sh
@@ -2,6 +2,7 @@
 
 test_description='per-worktree refs'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release()
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (22 preceding siblings ...)
  2022-03-23 20:32   ` [PATCH v2 23/27] revisions API: release "reflog_info" in release revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:32   ` Ævar Arnfjörð Bjarmason
  2022-03-23 20:32   ` [PATCH v2 25/27] revisions API: have release_revisions() release "date_mode" Ævar Arnfjörð Bjarmason
                     ` (4 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:32 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Call diff_free() on the "pruning" member of "struct rev_info".  Doing
so makes several tests pass under SANITIZE=leak.

This was also the last missing piece that allows us to remove the
UNLEAK() in "cmd_diff" and "cmd_diff_index", which allows us to use
those commands as a canary for general leaks in the revisions API. See
[1] for further rationale, and 886e1084d78 (builtin/: add UNLEAKs,
2017-10-01) for the commit that added the UNLEAK() there.

1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/diff-index.c                | 1 -
 builtin/diff.c                      | 1 -
 revision.c                          | 1 +
 t/t1001-read-tree-m-2way.sh         | 1 +
 t/t1002-read-tree-m-u-2way.sh       | 1 +
 t/t2200-add-update.sh               | 1 +
 t/t4039-diff-assume-unchanged.sh    | 1 +
 t/t4206-log-follow-harder-copies.sh | 2 ++
 t/t6131-pathspec-icase.sh           | 2 ++
 9 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 3a83183c312..7d158af6b6d 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -70,7 +70,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
 		return -1;
 	}
 	result = run_diff_index(&rev, option);
-	UNLEAK(rev);
 	result = diff_result_code(&rev.diffopt, result);
 	release_revisions(&rev);
 	return result;
diff --git a/builtin/diff.c b/builtin/diff.c
index dd48336da56..f539132ac68 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -594,7 +594,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
 	result = diff_result_code(&rev.diffopt, result);
 	if (1 < rev.diffopt.skip_stat_unmatch)
 		refresh_index_quietly();
-	UNLEAK(rev);
 	release_revisions(&rev);
 	UNLEAK(ent);
 	UNLEAK(blob);
diff --git a/revision.c b/revision.c
index ce23590a219..32e4d10f27e 100644
--- a/revision.c
+++ b/revision.c
@@ -2964,6 +2964,7 @@ void release_revisions(struct rev_info *revs)
 	clear_pathspec(&revs->prune_data);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
+	diff_free(&revs->pruning);
 	reflog_walk_info_release(revs->reflog_info);
 }
 
diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
index 0710b1fb1e9..516a6112fdc 100755
--- a/t/t1001-read-tree-m-2way.sh
+++ b/t/t1001-read-tree-m-2way.sh
@@ -21,6 +21,7 @@ In the test, these paths are used:
 	yomin   - not in H or M
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 
diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh
index 46cbd5514a6..bd5313caec9 100755
--- a/t/t1002-read-tree-m-u-2way.sh
+++ b/t/t1002-read-tree-m-u-2way.sh
@@ -9,6 +9,7 @@ This is identical to t1001, but uses -u to update the work tree as well.
 
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index 0c38f8e3569..be394f1131a 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -14,6 +14,7 @@ only the updates to dir/sub.
 Also tested are "git add -u" without limiting, and "git add -u"
 without contents changes, and other conditions'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh
index 0eb0314a8b3..78090e6852d 100755
--- a/t/t4039-diff-assume-unchanged.sh
+++ b/t/t4039-diff-assume-unchanged.sh
@@ -2,6 +2,7 @@
 
 test_description='diff with assume-unchanged entries'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # external diff has been tested in t4020-diff-external.sh
diff --git a/t/t4206-log-follow-harder-copies.sh b/t/t4206-log-follow-harder-copies.sh
index 4871a5dc92f..33ecf82c7f9 100755
--- a/t/t4206-log-follow-harder-copies.sh
+++ b/t/t4206-log-follow-harder-copies.sh
@@ -6,6 +6,8 @@
 test_description='Test --follow should always find copies hard in git log.
 
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh
 
diff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh
index 39fc3f6769b..770cce026cb 100755
--- a/t/t6131-pathspec-icase.sh
+++ b/t/t6131-pathspec-icase.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test case insensitive pathspec limiting'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 if test_have_prereq CASE_INSENSITIVE_FS
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 25/27] revisions API: have release_revisions() release "date_mode"
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (23 preceding siblings ...)
  2022-03-23 20:32   ` [PATCH v2 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release() Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:32   ` Ævar Arnfjörð Bjarmason
  2022-03-23 20:32   ` [PATCH v2 26/27] revisions API: have release_revisions() release "topo_walk_info" Ævar Arnfjörð Bjarmason
                     ` (3 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:32 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"date_mode" in the "struct ref_info".

This uses the date_mode_release() function added in 974c919d36d (date
API: add and use a date_mode_release(), 2022-02-16). As that commit
notes "t7004-tag.sh" tests for the leaks that are being fixed
here. That test now fails "only" 44 tests, instead of the 46 it failed
before this change.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/revision.c b/revision.c
index 32e4d10f27e..62dddeeeab5 100644
--- a/revision.c
+++ b/revision.c
@@ -2962,6 +2962,7 @@ void release_revisions(struct rev_info *revs)
 	release_revisions_cmdline(&revs->cmdline);
 	list_objects_filter_release(&revs->filter);
 	clear_pathspec(&revs->prune_data);
+	date_mode_release(&revs->date_mode);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
 	diff_free(&revs->pruning);
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 26/27] revisions API: have release_revisions() release "topo_walk_info"
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (24 preceding siblings ...)
  2022-03-23 20:32   ` [PATCH v2 25/27] revisions API: have release_revisions() release "date_mode" Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:32   ` Ævar Arnfjörð Bjarmason
  2022-03-23 20:32   ` [PATCH v2 27/27] revisions API: add a TODO for diff_free(&revs->diffopt) Ævar Arnfjörð Bjarmason
                     ` (2 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:32 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Refactor the existing reset_topo_walk() into a thin wrapper for a
release_revisions_topo_walk_info() + resetting the member to "NULL",
and call release_revisions_topo_walk_info() from release_revisions().

This fixes memory leaks that have been with us ever since
"topo_walk_info" was added to revision.[ch] in
f0d9cc4196a (revision.c: begin refactoring --topo-order logic,
2018-11-01).

Due to various other leaks this makes no tests pass in their entirety,
but e.g. before this running this on git.git:

    ./git -P log --pretty=tformat:"%P   %H | %s" --parents --full-history --topo-order -3 -- README.md

Would report under SANITIZE=leak:

    SUMMARY: LeakSanitizer: 531064 byte(s) leaked in 6 allocation(s).

Now we'll free all of that memory.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/revision.c b/revision.c
index 62dddeeeab5..ab570ac754c 100644
--- a/revision.c
+++ b/revision.c
@@ -2952,6 +2952,8 @@ static void release_revisions_mailmap(struct string_list *mailmap)
 	free(mailmap);
 }
 
+static void release_revisions_topo_walk_info(struct topo_walk_info *info);
+
 void release_revisions(struct rev_info *revs)
 {
 	if (!revs)
@@ -2967,6 +2969,7 @@ void release_revisions(struct rev_info *revs)
 	free_grep_patterns(&revs->grep_filter);
 	diff_free(&revs->pruning);
 	reflog_walk_info_release(revs->reflog_info);
+	release_revisions_topo_walk_info(revs->topo_walk_info);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
@@ -3479,17 +3482,22 @@ static void compute_indegrees_to_depth(struct rev_info *revs,
 		indegree_walk_step(revs);
 }
 
-static void reset_topo_walk(struct rev_info *revs)
+static void release_revisions_topo_walk_info(struct topo_walk_info *info)
 {
-	struct topo_walk_info *info = revs->topo_walk_info;
-
+	if (!info)
+		return;
 	clear_prio_queue(&info->explore_queue);
 	clear_prio_queue(&info->indegree_queue);
 	clear_prio_queue(&info->topo_queue);
 	clear_indegree_slab(&info->indegree);
 	clear_author_date_slab(&info->author_date);
+	free(info);
+}
 
-	FREE_AND_NULL(revs->topo_walk_info);
+static void reset_topo_walk(struct rev_info *revs)
+{
+	release_revisions_topo_walk_info(revs->topo_walk_info);
+	revs->topo_walk_info = NULL;
 }
 
 static void init_topo_walk(struct rev_info *revs)
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v2 27/27] revisions API: add a TODO for diff_free(&revs->diffopt)
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (25 preceding siblings ...)
  2022-03-23 20:32   ` [PATCH v2 26/27] revisions API: have release_revisions() release "topo_walk_info" Ævar Arnfjörð Bjarmason
@ 2022-03-23 20:32   ` Ævar Arnfjörð Bjarmason
  2022-03-25  1:41   ` [PATCH v2 00/27] revision.[ch]: add and use release_revisions() Junio C Hamano
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-23 20:32 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Add a TODO comment indicating that we should release "diffopt" in
release_revisions(). In a preceding commit we started releasing the
"pruning" member of the same type, but handling "diffopt" will require
us to untangle the "no_free" conditions I added in e900d494dcf (diff:
add an API for deferred freeing, 2021-02-11).

Let's leave a TODO comment to that effect, and so that we don't forget
refactor code that was changed to use release_revisions() in earlier
commits to stop using the "diffopt" member after a call to
release_revisions(). This works currently, but would become a logic
error as soon as we started freeing "diffopt". Doing that change now
doesn't harm anything, and future-proofs us against a later change to
release_revisions().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 diff-lib.c  | 4 +++-
 revision.c  | 1 +
 wt-status.c | 6 ++++--
 3 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/diff-lib.c b/diff-lib.c
index 298265e5b54..7eb66a417aa 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -651,6 +651,7 @@ int index_differs_from(struct repository *r,
 {
 	struct rev_info rev;
 	struct setup_revision_opt opt;
+	unsigned has_changes;
 
 	repo_init_revisions(r, &rev, NULL);
 	memset(&opt, 0, sizeof(opt));
@@ -662,8 +663,9 @@ int index_differs_from(struct repository *r,
 		diff_flags_or(&rev.diffopt.flags, flags);
 	rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
 	run_diff_index(&rev, 1);
+	has_changes = rev.diffopt.flags.has_changes;
 	release_revisions(&rev);
-	return (rev.diffopt.flags.has_changes != 0);
+	return (has_changes != 0);
 }
 
 static struct strbuf *idiff_prefix_cb(struct diff_options *opt, void *data)
diff --git a/revision.c b/revision.c
index ab570ac754c..ebcb87acaa0 100644
--- a/revision.c
+++ b/revision.c
@@ -2967,6 +2967,7 @@ void release_revisions(struct rev_info *revs)
 	date_mode_release(&revs->date_mode);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
+	/* TODO (need to handle "no_free"): diff_free(&revs->diffopt) */
 	diff_free(&revs->pruning);
 	reflog_walk_info_release(revs->reflog_info);
 	release_revisions_topo_walk_info(revs->topo_walk_info);
diff --git a/wt-status.c b/wt-status.c
index 61e0c1022f5..102d904adcb 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -2545,8 +2545,9 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)
 	rev_info.diffopt.flags.quick = 1;
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_files(&rev_info, 0);
+	result = diff_result_code(&rev_info.diffopt, result);
 	release_revisions(&rev_info);
-	return diff_result_code(&rev_info.diffopt, result);
+	return result;
 }
 
 /**
@@ -2578,8 +2579,9 @@ int has_uncommitted_changes(struct repository *r,
 
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_index(&rev_info, 1);
+	result = diff_result_code(&rev_info.diffopt, result);
 	release_revisions(&rev_info);
-	return diff_result_code(&rev_info.diffopt, result);
+	return result;
 }
 
 /**
-- 
2.35.1.1452.ga7cfc89151f


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf"
  2022-03-23 20:31   ` [PATCH v2 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
@ 2022-03-24  4:13     ` Junio C Hamano
  2022-03-24 16:57       ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 252+ messages in thread
From: Junio C Hamano @ 2022-03-24  4:13 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> Fix a memory leak that's been with us since f9500261e0a (fast-rebase:
> write conflict state to working tree, index, and HEAD, 2021-05-20)
> changed this code to move these strbuf_release() into an if/else
> block.
>
> We'll also add to "reflog_msg" in the "else" arm of the "if" block
> being modified here, and we'll append to "branch_msg" in both
> cases. But after f9500261e0a only the "if" block would free these two
> "struct strbuf".
>
> This change is needed so that a subsequent addition of a "goto
> cleanup" pattern when adding release_revisions() doesn't solve this
> unrelated bug while it's at it.

With or without the goto-cleanup change, this fix alone is the right
fix for these leaks.  I would not have written the last paragraph if
I were working on this topic ;-)


> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  t/helper/test-fast-rebase.c | 5 +++--
>  1 file changed, 3 insertions(+), 2 deletions(-)
>
> diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c
> index fc2d4609043..993b90eaedd 100644
> --- a/t/helper/test-fast-rebase.c
> +++ b/t/helper/test-fast-rebase.c
> @@ -201,8 +201,6 @@ int cmd__fast_rebase(int argc, const char **argv)
>  		}
>  		if (create_symref("HEAD", branch_name.buf, reflog_msg.buf) < 0)
>  			die(_("unable to update HEAD"));
> -		strbuf_release(&reflog_msg);
> -		strbuf_release(&branch_name);
>  
>  		prime_cache_tree(the_repository, the_repository->index,
>  				 result.tree);
> @@ -221,5 +219,8 @@ int cmd__fast_rebase(int argc, const char **argv)
>  	if (write_locked_index(&the_index, &lock,
>  			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
>  		die(_("unable to write %s"), get_index_file());
> +
> +	strbuf_release(&reflog_msg);
> +	strbuf_release(&branch_name);
>  	return (result.clean == 0);
>  }

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 02/27] blame: use "goto cleanup" for cleanup_scoreboard()
  2022-03-23 20:31   ` [PATCH v2 02/27] blame: use "goto cleanup" for cleanup_scoreboard() Ævar Arnfjörð Bjarmason
@ 2022-03-24  4:23     ` Junio C Hamano
  0 siblings, 0 replies; 252+ messages in thread
From: Junio C Hamano @ 2022-03-24  4:23 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> Amend a freeing pattern added in 0906ac2b54b (blame: use changed-path
> Bloom filters, 2020-04-16) to use a "goto cleanup", so that we can be
> sure that we call cleanup_scoreboard().

Makes sense.

> This change is needed so that a subsequent addition of a
> release_revisions() doesn't solve this unrelated issue while it's at
> it.

Again, this is irrelevant.  This patch alone with or without
anything else is the right fix to plug the leakage of the
scoreboard.

> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>  builtin/blame.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
>
> diff --git a/builtin/blame.c b/builtin/blame.c
> index 8d15b68afc9..885b381ab83 100644
> --- a/builtin/blame.c
> +++ b/builtin/blame.c
> @@ -1167,7 +1167,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
>  	if (!incremental)
>  		setup_pager();
>  	else
> -		return 0;
> +		goto cleanup;
>  
>  	blame_sort_final(&sb);
>  
> @@ -1201,6 +1201,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
>  		printf("num commits: %d\n", sb.num_commits);
>  	}
>  
> +cleanup:
>  	cleanup_scoreboard(&sb);
>  	return 0;
>  }

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids"
  2022-03-23 20:31   ` [PATCH v2 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids" Ævar Arnfjörð Bjarmason
@ 2022-03-24  4:33     ` Junio C Hamano
  2022-03-24 16:59       ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 252+ messages in thread
From: Junio C Hamano @ 2022-03-24  4:33 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> Fix two memory leaks in "struct rev_info" by freeing that memory in
> cmd_format_patch(). These two are unusual special-cases in being in
> the "struct rev_info", but not being "owned" by the code in
> revision.c. I.e. they're members of the struct so that this code in
> "builtin/log.c" can pass information code in log-tree.c.

Hmph, if we are eventually adding a new API function to clear a
rev-info structure, shouldn't these members be released there
instead, I wonder.

This is the only user of this member, so it does not matter too much
either way, though.


> diff --git a/builtin/log.c b/builtin/log.c
> index 634dc782cce..6f9928fabfe 100644
> --- a/builtin/log.c
> +++ b/builtin/log.c
> @@ -1747,6 +1747,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
>  	struct commit *commit;
>  	struct commit **list = NULL;
>  	struct rev_info rev;
> +	char *to_free = NULL;
>  	struct setup_revision_opt s_r_opt;
>  	int nr = 0, total, i;
>  	int use_stdout = 0;
> @@ -1947,7 +1948,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
>  		strbuf_addch(&buf, '\n');
>  	}
>  
> -	rev.extra_headers = strbuf_detach(&buf, NULL);
> +	rev.extra_headers = to_free = strbuf_detach(&buf, NULL);
>  
>  	if (from) {
>  		if (split_ident_line(&rev.from_ident, from, strlen(from)))
> @@ -2284,6 +2285,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
>  	strbuf_release(&rdiff1);
>  	strbuf_release(&rdiff2);
>  	strbuf_release(&rdiff_title);
> +	free(to_free);
> +	if (rev.ref_message_ids)
> +		string_list_clear(rev.ref_message_ids, 0);
> +	free(rev.ref_message_ids);
>  	UNLEAK(rev);
>  	return 0;
>  }

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 05/27] revision.[ch]: split freeing of revs->commit into a function
  2022-03-23 20:31   ` [PATCH v2 05/27] revision.[ch]: split freeing of revs->commit into a function Ævar Arnfjörð Bjarmason
@ 2022-03-24  4:33     ` Junio C Hamano
  2022-03-24 17:01       ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 252+ messages in thread
From: Junio C Hamano @ 2022-03-24  4:33 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> +static void release_revisions_commit_list(struct rev_info *revs)
> +{
> +	struct commit_list *commits = revs->commits;
> +
> +	if (!commits)
> +		return;
> +	free_commit_list(commits);
> +	revs->commits = NULL;
> +}

It makes sense to have this as a separate helper, but the original
implementation this was lifted from is much easier to follow than
this version with an unnecessary rewrite, I would think.

> @@ -4080,10 +4090,7 @@ static void create_boundary_commit_list(struct rev_info *revs)
>  	 * boundary commits anyway.  (This is what the code has always
>  	 * done.)
>  	 */
> -	if (revs->commits) {
> -		free_commit_list(revs->commits);
> -		revs->commits = NULL;
> -	}
> +	release_revisions_commit_list(revs);

IOW

static void release_revisions_commit_list(struct rev_info *revs)
{
	if (revs->commits) {
		free_commit_list(revs->commits);
		revs->commits = NULL;
	}
}


^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 06/27] revision.[ch]: provide and start using a release_revisions()
  2022-03-23 20:31   ` [PATCH v2 06/27] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-24  4:43     ` Junio C Hamano
  2022-03-24 17:02       ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 252+ messages in thread
From: Junio C Hamano @ 2022-03-24  4:43 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> diff --git a/revision.c b/revision.c
> index 303d1188207..90bac9ada03 100644
> --- a/revision.c
> +++ b/revision.c
> @@ -2933,6 +2933,13 @@ static void release_revisions_commit_list(struct rev_info *revs)
>  	revs->commits = NULL;
>  }
>  
> +void release_revisions(struct rev_info *revs)
> +{
> +	if (!revs)
> +		return;
> +	object_array_clear(&revs->pending);
> +}

Yay.

It is unclear why we want to allow passing NULL to this, though.  Do
we even have any code paths that allocate on-heap rev-info?  Address
of an on-stack or global rev_info will never be NULL.

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 08/27] revisions API users: use release_revisions() needing "{ 0 }" init
  2022-03-23 20:31   ` [PATCH v2 08/27] revisions API users: use release_revisions() needing "{ 0 }" init Ævar Arnfjörð Bjarmason
@ 2022-03-24  4:53     ` Junio C Hamano
  2022-03-24 17:04       ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 252+ messages in thread
From: Junio C Hamano @ 2022-03-24  4:53 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> Use release_revisions() to various users of "struct rev_list" which
> need to have their "struct rev_info" zero-initialized before we can
> start using it. In all of these cases we might "goto cleanup" (or equivalent),

I didn't look at the bisect code, but the bundle one looks iffy from
the point of view of API cleanliness.  If we have not yet called
repo_init_revisions() on a revs, we should refrain from calling
release_revisions() on it in the first place, no?

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf"
  2022-03-24  4:13     ` Junio C Hamano
@ 2022-03-24 16:57       ` Ævar Arnfjörð Bjarmason
  2022-03-24 17:34         ` Junio C Hamano
  0 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-24 16:57 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson


On Wed, Mar 23 2022, Junio C Hamano wrote:

> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
>
>> Fix a memory leak that's been with us since f9500261e0a (fast-rebase:
>> write conflict state to working tree, index, and HEAD, 2021-05-20)
>> changed this code to move these strbuf_release() into an if/else
>> block.
>>
>> We'll also add to "reflog_msg" in the "else" arm of the "if" block
>> being modified here, and we'll append to "branch_msg" in both
>> cases. But after f9500261e0a only the "if" block would free these two
>> "struct strbuf".
>>
>> This change is needed so that a subsequent addition of a "goto
>> cleanup" pattern when adding release_revisions() doesn't solve this
>> unrelated bug while it's at it.
>
> With or without the goto-cleanup change, this fix alone is the right
> fix for these leaks.  I would not have written the last paragraph if
> I were working on this topic ;-)

To this and your 02/27 comment: Sure, I can remove that.

In general I think it's bad form for individual commits to discuss the
series they're in, they should be stand-alone.

But I'm pretty sure if I left this out I'd have gotten questions about
why these seemingly unrelated leak fixes are here as art of series
implementing release_revisions(), from either you or someone else :)

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids"
  2022-03-24  4:33     ` Junio C Hamano
@ 2022-03-24 16:59       ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-24 16:59 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson


On Wed, Mar 23 2022, Junio C Hamano wrote:

> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
>
>> Fix two memory leaks in "struct rev_info" by freeing that memory in
>> cmd_format_patch(). These two are unusual special-cases in being in
>> the "struct rev_info", but not being "owned" by the code in
>> revision.c. I.e. they're members of the struct so that this code in
>> "builtin/log.c" can pass information code in log-tree.c.
>
> Hmph, if we are eventually adding a new API function to clear a
> rev-info structure, shouldn't these members be released there
> instead, I wonder.
>
> This is the only user of this member, so it does not matter too much
> either way, though.

Ideally, but in practice "struct rev_info" is both before and after this
change a mixed bag of stuff it "owns" and stuff that others "hand over"
to it, sometimes permanently, and sometimes just to hold on for a bit.

So it can't really free() everything in the struct, at least witohut
some larger refactorings, which I'm trying to avoid here.

The "diffopt" at the end is another such case, which is left for another
follow-up.

But for now is it OK if we keep this as-is?

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 05/27] revision.[ch]: split freeing of revs->commit into a function
  2022-03-24  4:33     ` Junio C Hamano
@ 2022-03-24 17:01       ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-24 17:01 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson


On Wed, Mar 23 2022, Junio C Hamano wrote:

> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
>
>> +static void release_revisions_commit_list(struct rev_info *revs)
>> +{
>> +	struct commit_list *commits = revs->commits;
>> +
>> +	if (!commits)
>> +		return;
>> +	free_commit_list(commits);
>> +	revs->commits = NULL;
>> +}
>
> It makes sense to have this as a separate helper, but the original
> implementation this was lifted from is much easier to follow than
> this version with an unnecessary rewrite, I would think.
>
>> @@ -4080,10 +4090,7 @@ static void create_boundary_commit_list(struct rev_info *revs)
>>  	 * boundary commits anyway.  (This is what the code has always
>>  	 * done.)
>>  	 */
>> -	if (revs->commits) {
>> -		free_commit_list(revs->commits);
>> -		revs->commits = NULL;
>> -	}
>> +	release_revisions_commit_list(revs);
>
> IOW
>
> static void release_revisions_commit_list(struct rev_info *revs)
> {
> 	if (revs->commits) {
> 		free_commit_list(revs->commits);
> 		revs->commits = NULL;
> 	}
> }

Sure, will change it.

I think the pre-image is preferrable in context, i.e. to have them all
use the same early abort pattern, but not enough to argue the point :)




^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 06/27] revision.[ch]: provide and start using a release_revisions()
  2022-03-24  4:43     ` Junio C Hamano
@ 2022-03-24 17:02       ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-24 17:02 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson


On Wed, Mar 23 2022, Junio C Hamano wrote:

> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
>
>> diff --git a/revision.c b/revision.c
>> index 303d1188207..90bac9ada03 100644
>> --- a/revision.c
>> +++ b/revision.c
>> @@ -2933,6 +2933,13 @@ static void release_revisions_commit_list(struct rev_info *revs)
>>  	revs->commits = NULL;
>>  }
>>  
>> +void release_revisions(struct rev_info *revs)
>> +{
>> +	if (!revs)
>> +		return;
>> +	object_array_clear(&revs->pending);
>> +}
>
> Yay.
>
> It is unclear why we want to allow passing NULL to this, though.  Do
> we even have any code paths that allocate on-heap rev-info?  Address
> of an on-stack or global rev_info will never be NULL.

I'm almost certain it's boilerplate from early testing, and running the
tests with it removed just now passed.

I'll make a bit more sure sure it's OK (with SANITIZE=address et al) and
drop it in a v3.

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 08/27] revisions API users: use release_revisions() needing "{ 0 }" init
  2022-03-24  4:53     ` Junio C Hamano
@ 2022-03-24 17:04       ` Ævar Arnfjörð Bjarmason
  2022-03-24 17:39         ` Junio C Hamano
  0 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-24 17:04 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson


On Wed, Mar 23 2022, Junio C Hamano wrote:

> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
>
>> Use release_revisions() to various users of "struct rev_list" which
>> need to have their "struct rev_info" zero-initialized before we can
>> start using it. In all of these cases we might "goto cleanup" (or equivalent),
>
> I didn't look at the bisect code, but the bundle one looks iffy from
> the point of view of API cleanliness.  If we have not yet called
> repo_init_revisions() on a revs, we should refrain from calling
> release_revisions() on it in the first place, no?

It could be avoided, but I'd really prefer not to for this series.

repo_init_revisions() is a non-trivial function, and changing the
various bits in this series that can easily have a "goto" pattern
because we assume that { 0 }-init'd is safe to pass to
release_revisions() would be a larger change...

We assume that in a lot of other destructors throughout the codebase, I
figured we could leave this for later.

Is that OK with you?

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf"
  2022-03-24 16:57       ` Ævar Arnfjörð Bjarmason
@ 2022-03-24 17:34         ` Junio C Hamano
  0 siblings, 0 replies; 252+ messages in thread
From: Junio C Hamano @ 2022-03-24 17:34 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> But I'm pretty sure if I left this out I'd have gotten questions about
> why these seemingly unrelated leak fixes are here as art of series
> implementing release_revisions(), from either you or someone else :)

That is an indication that these changes are outside the scope of
the series, and should be done as a preliminary and separate series,
isn't it?

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 08/27] revisions API users: use release_revisions() needing "{ 0 }" init
  2022-03-24 17:04       ` Ævar Arnfjörð Bjarmason
@ 2022-03-24 17:39         ` Junio C Hamano
  2022-03-25  0:47           ` Junio C Hamano
  0 siblings, 1 reply; 252+ messages in thread
From: Junio C Hamano @ 2022-03-24 17:39 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> On Wed, Mar 23 2022, Junio C Hamano wrote:
>
>> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
>>
>>> Use release_revisions() to various users of "struct rev_list" which
>>> need to have their "struct rev_info" zero-initialized before we can
>>> start using it. In all of these cases we might "goto cleanup" (or equivalent),
>>
>> I didn't look at the bisect code, but the bundle one looks iffy from
>> the point of view of API cleanliness.  If we have not yet called
>> repo_init_revisions() on a revs, we should refrain from calling
>> release_revisions() on it in the first place, no?
>
> It could be avoided, but I'd really prefer not to for this series.
>
> repo_init_revisions() is a non-trivial function, and changing the
> various bits in this series that can easily have a "goto" pattern
> because we assume that { 0 }-init'd is safe to pass to
> release_revisions() would be a larger change...
>
> We assume that in a lot of other destructors throughout the codebase, I
> figured we could leave this for later.
>
> Is that OK with you?

Not really.  If you introduce "#define REV_INFO_INIT { 0 }",
perhaps, though.

Without such a mechanism to clearly say "here is what we initialize
a rev_info", the first call to repo_init_revisions() looks like the
place that initializes a rev_info, and call to release_revisions()
on a rev_info that did not go through repo_init_revisions() looks
like a call to free() of a pointer that hasn't been assigned the
result from an allocation from the heap.  That is where my "iffy
from the API cleanliness POV" comes from.


^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 08/27] revisions API users: use release_revisions() needing "{ 0 }" init
  2022-03-24 17:39         ` Junio C Hamano
@ 2022-03-25  0:47           ` Junio C Hamano
  0 siblings, 0 replies; 252+ messages in thread
From: Junio C Hamano @ 2022-03-25  0:47 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Junio C Hamano <gitster@pobox.com> writes:

> Without such a mechanism to clearly say "here is what we initialize

"what" -> "where and how".

> a rev_info", the first call to repo_init_revisions() looks like the
> place that initializes a rev_info, and call to release_revisions()
> on a rev_info that did not go through repo_init_revisions() looks
> like a call to free() of a pointer that hasn't been assigned the
> result from an allocation from the heap.  That is where my "iffy
> from the API cleanliness POV" comes from.

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 09/27] stash: always have the owner of "stash_info" free it
  2022-03-23 20:31   ` [PATCH v2 09/27] stash: always have the owner of "stash_info" free it Ævar Arnfjörð Bjarmason
@ 2022-03-25  0:51     ` Junio C Hamano
  0 siblings, 0 replies; 252+ messages in thread
From: Junio C Hamano @ 2022-03-25  0:51 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> Change the initialization of the "revision" member of "struct
> stash_info" to be initialized vi a macro, and more importantly that

"vi a" "via a"


^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 10/27] revisions API users: add "goto cleanup" for release_revisions()
  2022-03-23 20:32   ` [PATCH v2 10/27] revisions API users: add "goto cleanup" for release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-25  0:52     ` Junio C Hamano
  0 siblings, 0 replies; 252+ messages in thread
From: Junio C Hamano @ 2022-03-25  0:52 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> Add a release_revisions() to various users of "struct rev_list" which

"rev_list" -> "rev_info", no?

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 12/27] revisions API users: use release_revisions() in builtin/log.c
  2022-03-23 20:32   ` [PATCH v2 12/27] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
@ 2022-03-25  1:03     ` Junio C Hamano
  0 siblings, 0 replies; 252+ messages in thread
From: Junio C Hamano @ 2022-03-25  1:03 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> In preparation for having the "log" family of functions make wider use
> of release_revisions() let's have them call it just before
> exiting. This changes the "log", "whatchanged", "show",
> "format-patch", etc. commands, all of which live in this file.
>
> The release_revisions() API still only frees the "pending" member, but
> will learn to more members of "struct rev_info" in subsequent commits.

Learn to do what to more members?

> In the case of "format-patch" revert the addition of UNLEAK() in
> dee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16), and
> which will cause several tests that previously passed under
> "TEST_PASSES_SANITIZE_LEAK=true" to start failing.

", and which" -> ", which".

> diff --git a/builtin/log.c b/builtin/log.c
> index 6f9928fabfe..c40ebe1c3f4 100644
> --- a/builtin/log.c
> +++ b/builtin/log.c
> @@ -295,6 +295,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
>  	cmd_log_init_finish(argc, argv, prefix, rev, opt);
>  }
>  
> +static int cmd_log_deinit(int ret, struct rev_info *rev)
> +{
> +	release_revisions(rev);
> +	return ret;
> +}

Ugly but cute.

> @@ -732,8 +738,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
>  			ret = error(_("unknown type: %d"), o->type);
>  		}
>  	}
> -	free(objects);
> -	return ret;
> +	return cmd_log_deinit(ret, &rev);
>  }

This probably makes things worse tentatively for OBJ_COMMIT case,
where we reuse the rev structure to "(un)walk" the given commit
after clearing rev.pending, but hopefully you'll fix it up in a
later step or two?

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 14/27] revisions API users: use release_revisions() in submodule.c edge case
  2022-03-23 20:32   ` [PATCH v2 14/27] revisions API users: use release_revisions() in submodule.c edge case Ævar Arnfjörð Bjarmason
@ 2022-03-25  1:07     ` Junio C Hamano
  0 siblings, 0 replies; 252+ messages in thread
From: Junio C Hamano @ 2022-03-25  1:07 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> diff --git a/submodule.c b/submodule.c
> index 0510cb193b6..285c1896c9e 100644
> --- a/submodule.c
> +++ b/submodule.c
> @@ -638,7 +638,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,
>  		struct object_id *one, struct object_id *two,
>  		unsigned dirty_submodule)
>  {
> -	struct rev_info rev;
> +	struct rev_info rev = { 0 };

Exactly the same comment as an earlier step about bundle and bisect
applies here.

>  	struct commit *left = NULL, *right = NULL;
>  	struct commit_list *merge_bases = NULL;
>  	struct repository *sub;
> @@ -664,6 +664,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,
>  	print_submodule_diff_summary(sub, &rev, o);
>  
>  out:
> +	release_revisions(&rev);
>  	if (merge_bases)
>  		free_commit_list(merge_bases);
>  	clear_commit_marks(left, ~0);

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 17/27] revisions API: have release_revisions() release "mailmap"
  2022-03-23 20:32   ` [PATCH v2 17/27] revisions API: have release_revisions() release "mailmap" Ævar Arnfjörð Bjarmason
@ 2022-03-25  1:11     ` Junio C Hamano
  0 siblings, 0 replies; 252+ messages in thread
From: Junio C Hamano @ 2022-03-25  1:11 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> Extend the the release_revisions() function so that it frees the
> "prune_data" in the "struct ref_info".

Huh?  The patch text does make sense but it is all about mailmap as
far as I can see.

> diff --git a/builtin/commit.c b/builtin/commit.c
> index c7eda9bbb72..cd6cebcf8c8 100644
> --- a/builtin/commit.c
> +++ b/builtin/commit.c
> @@ -1100,7 +1100,6 @@ static const char *find_author_by_nickname(const char *name)
>  	struct rev_info revs;
>  	struct commit *commit;
>  	struct strbuf buf = STRBUF_INIT;
> -	struct string_list mailmap = STRING_LIST_INIT_NODUP;
>  	const char *av[20];
>  	int ac = 0;
>  
> @@ -1111,7 +1110,8 @@ static const char *find_author_by_nickname(const char *name)
>  	av[++ac] = buf.buf;
>  	av[++ac] = NULL;
>  	setup_revisions(ac, av, &revs, NULL);
> -	revs.mailmap = &mailmap;
> +	revs.mailmap = xmalloc(sizeof(struct string_list));
> +	string_list_init_nodup(revs.mailmap);
>  	read_mailmap(revs.mailmap);
>  
>  	if (prepare_revision_walk(&revs))
> @@ -1122,7 +1122,6 @@ static const char *find_author_by_nickname(const char *name)
>  		ctx.date_mode.type = DATE_NORMAL;
>  		strbuf_release(&buf);
>  		format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
> -		clear_mailmap(&mailmap);
>  		release_revisions(&revs);
>  		return strbuf_detach(&buf, NULL);
>  	}
> diff --git a/revision.c b/revision.c
> index 13b568aea76..d9e2b171f6d 100644
> --- a/revision.c
> +++ b/revision.c
> @@ -2933,12 +2933,21 @@ static void release_revisions_commit_list(struct rev_info *revs)
>  	revs->commits = NULL;
>  }
>  
> +static void release_revisions_mailmap(struct string_list *mailmap)
> +{
> +	if (!mailmap)
> +		return;
> +	clear_mailmap(mailmap);
> +	free(mailmap);
> +}
> +
>  void release_revisions(struct rev_info *revs)
>  {
>  	if (!revs)
>  		return;
>  	release_revisions_commit_list(revs);
>  	object_array_clear(&revs->pending);
> +	release_revisions_mailmap(revs->mailmap);
>  }
>  
>  static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
> diff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh
> index 2630e756dab..752aa8c9454 100755
> --- a/t/t0056-git-C.sh
> +++ b/t/t0056-git-C.sh
> @@ -2,6 +2,7 @@
>  
>  test_description='"-C <path>" option and its effects on other path-related options'
>  
> +TEST_PASSES_SANITIZE_LEAK=true
>  . ./test-lib.sh
>  
>  test_expect_success '"git -C <path>" runs git from the directory <path>' '
> diff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh
> index bb5fea02a03..d0c4d38b4d4 100755
> --- a/t/t3302-notes-index-expensive.sh
> +++ b/t/t3302-notes-index-expensive.sh
> @@ -8,6 +8,7 @@ test_description='Test commit notes index (expensive!)'
>  GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
>  export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
>  
> +TEST_PASSES_SANITIZE_LEAK=true
>  . ./test-lib.sh
>  
>  create_repo () {
> diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh
> index 741e0803c1a..73048d0a526 100755
> --- a/t/t4055-diff-context.sh
> +++ b/t/t4055-diff-context.sh
> @@ -5,6 +5,7 @@
>  
>  test_description='diff.context configuration'
>  
> +TEST_PASSES_SANITIZE_LEAK=true
>  . ./test-lib.sh
>  
>  test_expect_success 'setup' '
> diff --git a/t/t4066-diff-emit-delay.sh b/t/t4066-diff-emit-delay.sh
> index a1de63b77f8..0ecb3915412 100755
> --- a/t/t4066-diff-emit-delay.sh
> +++ b/t/t4066-diff-emit-delay.sh
> @@ -4,6 +4,7 @@ test_description='test combined/stat/moved interaction'
>  GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
>  export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
>  
> +TEST_PASSES_SANITIZE_LEAK=true
>  . ./test-lib.sh
>  
>  # This test covers a weird 3-way interaction between "--cc -p", which will run
> diff --git a/t/t7008-filter-branch-null-sha1.sh b/t/t7008-filter-branch-null-sha1.sh
> index 9ba9f24ad2f..93fbc92b8db 100755
> --- a/t/t7008-filter-branch-null-sha1.sh
> +++ b/t/t7008-filter-branch-null-sha1.sh
> @@ -1,6 +1,7 @@
>  #!/bin/sh
>  
>  test_description='filter-branch removal of trees with null sha1'
> +
>  . ./test-lib.sh
>  
>  test_expect_success 'setup: base commits' '

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 18/27] revisions API: have release_revisions() release "cmdline"
  2022-03-23 20:32   ` [PATCH v2 18/27] revisions API: have release_revisions() release "cmdline" Ævar Arnfjörð Bjarmason
@ 2022-03-25  1:16     ` Junio C Hamano
  0 siblings, 0 replies; 252+ messages in thread
From: Junio C Hamano @ 2022-03-25  1:16 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> in-flight semantic conflicts.
>
> 1. https://lore.kernel.org/git/YUj%2FgFRh6pwrZalY@carlos-mbp.lan/
> 2. https://lore.kernel.org/git/87o88obkb1.fsf@evledraar.gmail.com/
>
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
>
> fixup with bc0e0439040?

WTH is this line?

> diff --git a/revision.c b/revision.c
> index d9e2b171f6d..f44298299a4 100644
> --- a/revision.c
> +++ b/revision.c
> @@ -2933,6 +2933,17 @@ static void release_revisions_commit_list(struct rev_info *revs)
>  	revs->commits = NULL;
>  }
>  
> +static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)
> +{
> +	unsigned int i;
> +
> +	if (!cmdline)
> +		return;
> +	for (i = 0; i < cmdline->nr; i++)
> +		free((char *)cmdline->rev[i].name);
> +	FREE_AND_NULL(cmdline->rev);
> +}
> +
>  static void release_revisions_mailmap(struct string_list *mailmap)
>  {
>  	if (!mailmap)
> @@ -2947,6 +2958,7 @@ void release_revisions(struct rev_info *revs)
>  		return;
>  	release_revisions_commit_list(revs);
>  	object_array_clear(&revs->pending);
> +	release_revisions_cmdline(&revs->cmdline);
>  	release_revisions_mailmap(revs->mailmap);
>  }

That patch is quite unexpectedly small ;-)

When I created cmdline, I recall liberally stuffing borrowed strings
in the array, sort-of knowinly making it unnecessarily hard to
"free".

Quite pleasing.


^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 19/27] revisions API: have release_revisions() release "filter"
  2022-03-23 20:32   ` [PATCH v2 19/27] revisions API: have release_revisions() release "filter" Ævar Arnfjörð Bjarmason
@ 2022-03-25  1:17     ` Junio C Hamano
  0 siblings, 0 replies; 252+ messages in thread
From: Junio C Hamano @ 2022-03-25  1:17 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> Extend the the release_revisions() function so that it frees the
> "cmdline" in the "struct ref_info". This in combination with a
> preceding change to free "cmdline" means that we can mark another set
> of tests as passing under "TEST_PASSES_SANITIZE_LEAK=true".

Read that again, perhaps twice if needed, and you already know which
typo I was about to point out ;-).



^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 21/27] revisions API: have release_revisions() release "prune_data"
  2022-03-23 20:32   ` [PATCH v2 21/27] revisions API: have release_revisions() release "prune_data" Ævar Arnfjörð Bjarmason
@ 2022-03-25  1:20     ` Junio C Hamano
  0 siblings, 0 replies; 252+ messages in thread
From: Junio C Hamano @ 2022-03-25  1:20 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> Extend the the release_revisions() function so that it frees the
> "prune_data" in the "struct ref_info". This means that any code that

The same comment as the "cmdline" -> "filter" in a few patches ago
applies here, this time "rev_info".




^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 00/27] revision.[ch]: add and use release_revisions()
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (26 preceding siblings ...)
  2022-03-23 20:32   ` [PATCH v2 27/27] revisions API: add a TODO for diff_free(&revs->diffopt) Ævar Arnfjörð Bjarmason
@ 2022-03-25  1:41   ` Junio C Hamano
  2022-03-25  9:14     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
  28 siblings, 1 reply; 252+ messages in thread
From: Junio C Hamano @ 2022-03-25  1:41 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> This is a re-roll of the v1 of [1] now that 7391ecd338e (Merge branch
> 'ds/partial-bundles', 2022-03-21) has landed, which it had a conflict
> with. I believe this v2 addresses all the feedback brought up on v1.

Thanks.  It was mostly a pleasant read but with a huge caveat---I
have no confidence in the code that would not try to release what it
did not allocate (simply because I do not have time to audit all
allocations to various members of rev-info structure).  But at least
if we try to free something we borrowed from say command line, we'd
immediately get a crash so with enough cooking and guinea-pig testing,
such a bug would be easy to catch.

I suspect cmd_show() still is leaky when fed a few commits.

    int cmd_show(int argc, const char **argv, const char *prefix)
    {
            struct rev_info rev;
            struct object_array_entry *objects;
            struct setup_revision_opt opt;
            struct pathspec match_all;
            int i, count, ret = 0;

            init_log_defaults();
            git_config(git_log_config, NULL);

            memset(&match_all, 0, sizeof(match_all));
            repo_init_revisions(the_repository, &rev, prefix);
            ...
            cmd_log_init(argc, argv, prefix, &rev, &opt);

            if (!rev.no_walk)
                    return cmd_log_deinit(cmd_log_walk(&rev), &rev);

At this point, we only have positive objects in pending and rev.no_walk
is set.

            count = rev.pending.nr;
            objects = rev.pending.objects;

We grab .objects and we "show" each one of them.

            for (i = 0; i < count && !ret; i++) {
                    struct object *o = objects[i].item;
                    const char *name = objects[i].name;
                    switch (o->type) {
                    ...

There are ways to show different types of objects, but what we are
interested in is what happens to commits.

                    case OBJ_COMMIT:
                            rev.pending.nr = rev.pending.alloc = 0;
                            rev.pending.objects = NULL;

We clear pending.objects because we want to reuse the rev_info we
already have here (things like "git show -U8 master maint" is harder
to do if we do not allow the reuse), and push this single commit to
the pending array, and let log_walk() machinery to "show" it (note
that we do not "walk", as we have the .no_walk bit set).

                            add_object_array(o, name, &rev.pending);
                            ret = cmd_log_walk(&rev);

After we are done with this "traversal" of a single object, we
process another element in the original "objects" list, which was
taken from the original rev.pending.objects[] array, and we can keep
going, because nobody else has access to objects[] array.

I do not offhand recall what other members of rev_info
cmd_log_walk() touches, but we are not clearing them.  Not clearing
rev.pending.objects here means we can be leaking the newly created
one-element object array to hold this single object 'o', UNLESS it
is the last object in the original objects[] array.

                            break;
                    default:
                            ret = error(_("unknown type: %d"), o->type);
                    }
            }

And even worse, we used to free objects[] here, but we no longer do.
Instead we let cmd_log_deinit() clear rev.pending.objects[], which
is the same as objects[] if and only if there are NO commit objects
on the command line.  If there is even one commit object on the
command line, we would have cleared rev.pending.objects and created
a new one, which is what is cleared by the call to cmd_log_deinit().
The original one, pointed by the local variable objects[], is no
longer freed.

            return cmd_log_deinit(ret, &rev);
    }

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v2 00/27] revision.[ch]: add and use release_revisions()
  2022-03-25  1:41   ` [PATCH v2 00/27] revision.[ch]: add and use release_revisions() Junio C Hamano
@ 2022-03-25  9:14     ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25  9:14 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson


On Thu, Mar 24 2022, Junio C Hamano wrote:

> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
>
>> This is a re-roll of the v1 of [1] now that 7391ecd338e (Merge branch
>> 'ds/partial-bundles', 2022-03-21) has landed, which it had a conflict
>> with. I believe this v2 addresses all the feedback brought up on v1.
>
> Thanks.  It was mostly a pleasant read but with a huge caveat---I
> have no confidence in the code that would not try to release what it
> did not allocate (simply because I do not have time to audit all
> allocations to various members of rev-info structure).  But at least
> if we try to free something we borrowed from say command line, we'd
> immediately get a crash so with enough cooking and guinea-pig testing,
> such a bug would be easy to catch.

Yes, especially with SANITIZE=address and friends.

> I suspect cmd_show() still is leaky when fed a few commits.
> [...]
> There are ways to show different types of objects, but what we are
> interested in is what happens to commits.
>
>                     case OBJ_COMMIT:
>                             rev.pending.nr = rev.pending.alloc = 0;
>                             rev.pending.objects = NULL;

Yes, FWIW I know about that one, and will add some passing mention of it
to a commit message.

There's still a bunch of leaks related to the revisions.c code after
this series, which doesn't address all of them.

But since it's already getting up to ~30 patches I wanted to address the
most common ones, this and various other tricky edge cases have been
left for a follow-up.

^ permalink raw reply	[flat|nested] 252+ messages in thread

* [PATCH v3 00/27] revision.[ch]: add and use release_revisions()
  2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
                     ` (27 preceding siblings ...)
  2022-03-25  1:41   ` [PATCH v2 00/27] revision.[ch]: add and use release_revisions() Junio C Hamano
@ 2022-03-25 17:18   ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
                       ` (27 more replies)
  28 siblings, 28 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

= Brief summary, more in the v1 CL[1]:

This series adds a release_revisions() and works towards fixing those
memory leaks one at a time. As the diffstat to t/* shows we can mark a
*lot* of tests leak-free as a result, and those are only the the tests
that 100% pass with this series.

An approximate count of the total number of memory leaks in the test
suite (approximate because some are "hidden" due to SANITIZE=leak
causing "git" to die) is, before this series (running with "--tee" and
"--verbose-log"):

    $ grep -a "SUMMARY: LeakSanitizer.*leaked in .*allocation" test-results/*.out|wc -l
    12032

After:

    $ grep -a "SUMMARY: LeakSanitizer.*leaked in .*allocation" test-results/*.out|wc -l
    6652

[NOTE: These numbers are for v1, and may not be *exactly* those
anymore as "master" has moved, but are going to be in that ballpark].

= Changes since v2[2]

I think this addresses all the outstanding commments/feedback on the
v2. Thanks to Junio for the review!

This still has a similar textual conflict as v2[2], and will require
my [3] on top of ds/partial-bundle-more to fully pass the leak tests,
currently "seen" is failing due to that issue.

1. https://lore.kernel.org/git/cover-00.24-00000000000-20220309T123321Z-avarab@gmail.com/
2. https://lore.kernel.org/git/cover-v2-00.27-00000000000-20220323T203149Z-avarab@gmail.com/
3. https://lore.kernel.org/git/patch-1.1-193534b0f07-20220325T121715Z-avarab@gmail.com/

Ævar Arnfjörð Bjarmason (27):
  t/helper/test-fast-rebase.c: don't leak "struct strbuf"
  blame: use "goto cleanup" for cleanup_scoreboard()
  string_list API users: use string_list_init_{no,}dup
  format-patch: don't leak "extra_headers" or "ref_message_ids"
  revision.[ch]: split freeing of revs->commit into a function
  revision.[ch]: provide and start using a release_revisions()
  revisions API users: add straightforward release_revisions()
  revisions API users: add "goto cleanup" for "rev_info" early exit
  revisions API users: use release_revisions() in submodule.c edge case
  stash: always have the owner of "stash_info" free it
  revisions API users: add "goto cleanup" for release_revisions()
  revisions API users: use release_revisions() in http-push.c
  revisions API users: use release_revisions() in builtin/log.c
  revisions API users: use release_revisions() with UNLEAK()
  revisions API users: use release_revisions() for "prune_data" users
  revisions API: have release_revisions() release "commits"
  revisions API: have release_revisions() release "mailmap"
  revisions API: have release_revisions() release "cmdline"
  revisions API: have release_revisions() release "filter"
  revisions API: have release_revisions() release "grep_filter"
  revisions API: have release_revisions() release "prune_data"
  revisions API: clear "boundary_commits" in release_revisions()
  revisions API: release "reflog_info" in release revisions()
  revisions API: call diff_free(&revs->pruning) in revisions_release()
  revisions API: have release_revisions() release "date_mode"
  revisions API: have release_revisions() release "topo_walk_info"
  revisions API: add a TODO for diff_free(&revs->diffopt)

 add-interactive.c                          |  3 +-
 bisect.c                                   | 19 ++++--
 builtin/add.c                              |  3 +-
 builtin/am.c                               |  3 +
 builtin/bisect--helper.c                   |  2 +
 builtin/blame.c                            |  4 +-
 builtin/checkout.c                         |  3 +-
 builtin/commit.c                           |  6 +-
 builtin/describe.c                         |  2 +
 builtin/diff-files.c                       |  8 ++-
 builtin/diff-index.c                       |  5 +-
 builtin/diff.c                             |  2 +-
 builtin/fast-export.c                      |  1 +
 builtin/log.c                              | 36 ++++++----
 builtin/merge.c                            |  2 +
 builtin/pack-objects.c                     |  2 +
 builtin/prune.c                            |  1 +
 builtin/reflog.c                           |  1 +
 builtin/rev-list.c                         | 19 ++++--
 builtin/shortlog.c                         |  8 ++-
 builtin/stash.c                            | 77 +++++++++++-----------
 builtin/submodule--helper.c                | 17 +++--
 bundle.c                                   | 12 +++-
 diff-lib.c                                 |  8 ++-
 fmt-merge-msg.c                            |  1 +
 http-push.c                                |  3 +-
 merge-ort.c                                |  1 +
 merge-recursive.c                          |  5 +-
 midx.c                                     |  1 +
 pack-bitmap-write.c                        |  1 +
 range-diff.c                               |  2 +-
 ref-filter.c                               |  1 +
 reflog-walk.c                              | 24 ++++++-
 reflog-walk.h                              |  1 +
 remote.c                                   |  1 +
 revision.c                                 | 64 +++++++++++++++---
 revision.h                                 |  6 ++
 sequencer.c                                | 26 ++++++--
 shallow.c                                  |  1 +
 submodule.c                                | 43 ++++++------
 t/helper/test-fast-rebase.c                | 23 +++++--
 t/helper/test-revision-walking.c           |  1 +
 t/lib-git-svn.sh                           |  4 ++
 t/t0056-git-C.sh                           |  1 +
 t/t0062-revision-walking.sh                |  1 +
 t/t0100-previous.sh                        |  1 +
 t/t0101-at-syntax.sh                       |  2 +
 t/t1001-read-tree-m-2way.sh                |  1 +
 t/t1002-read-tree-m-u-2way.sh              |  1 +
 t/t1060-object-corruption.sh               |  2 +
 t/t1401-symbolic-ref.sh                    |  2 +
 t/t1411-reflog-show.sh                     |  1 +
 t/t1412-reflog-loop.sh                     |  2 +
 t/t1415-worktree-refs.sh                   |  1 +
 t/t2015-checkout-unborn.sh                 |  1 +
 t/t2200-add-update.sh                      |  1 +
 t/t3302-notes-index-expensive.sh           |  1 +
 t/t3303-notes-subtrees.sh                  |  1 +
 t/t3305-notes-fanout.sh                    |  1 +
 t/t3408-rebase-multi-line.sh               |  1 +
 t/t4021-format-patch-numbered.sh           |  1 +
 t/t4027-diff-submodule.sh                  |  1 +
 t/t4028-format-patch-mime-headers.sh       |  2 +
 t/t4036-format-patch-signer-mime.sh        |  1 +
 t/t4039-diff-assume-unchanged.sh           |  1 +
 t/t4055-diff-context.sh                    |  1 +
 t/t4066-diff-emit-delay.sh                 |  1 +
 t/t4122-apply-symlink-inside.sh            |  1 +
 t/t4126-apply-empty.sh                     |  1 -
 t/t4128-apply-root.sh                      |  1 +
 t/t4206-log-follow-harder-copies.sh        |  2 +
 t/t4207-log-decoration-colors.sh           |  1 +
 t/t4212-log-corrupt.sh                     |  1 +
 t/t5301-sliding-window.sh                  |  2 +
 t/t5313-pack-bounds-checks.sh              |  2 +
 t/t5316-pack-delta-depth.sh                |  2 +
 t/t5320-delta-islands.sh                   |  2 +
 t/t5322-pack-objects-sparse.sh             |  1 +
 t/t5506-remote-groups.sh                   |  1 +
 t/t5513-fetch-track.sh                     |  1 +
 t/t5515-fetch-merge-logic.sh               |  1 +
 t/t5518-fetch-exit-status.sh               |  1 +
 t/t5532-fetch-proxy.sh                     |  2 +
 t/t5600-clone-fail-cleanup.sh              |  1 +
 t/t5900-repo-selection.sh                  |  2 +
 t/t6002-rev-list-bisect.sh                 |  1 +
 t/t6003-rev-list-topo-order.sh             |  1 +
 t/t6005-rev-list-count.sh                  |  1 +
 t/t6018-rev-list-glob.sh                   |  1 +
 t/t6100-rev-list-in-order.sh               |  1 +
 t/t6101-rev-parse-parents.sh               |  1 +
 t/t6110-rev-list-sparse.sh                 |  1 +
 t/t6114-keep-packs.sh                      |  2 +
 t/t6131-pathspec-icase.sh                  |  2 +
 t/t7008-filter-branch-null-sha1.sh         |  1 +
 t/t7702-repack-cyclic-alternate.sh         |  2 +
 t/t9001-send-email.sh                      |  1 +
 t/t9100-git-svn-basic.sh                   |  1 +
 t/t9101-git-svn-props.sh                   |  2 +
 t/t9104-git-svn-follow-parent.sh           |  2 +
 t/t9106-git-svn-commit-diff-clobber.sh     |  2 +
 t/t9115-git-svn-dcommit-funky-renames.sh   |  1 +
 t/t9116-git-svn-log.sh                     |  1 +
 t/t9122-git-svn-author.sh                  |  2 +
 t/t9127-git-svn-partial-rebuild.sh         |  1 +
 t/t9129-git-svn-i18n-commitencoding.sh     |  1 +
 t/t9132-git-svn-broken-symlink.sh          |  1 +
 t/t9139-git-svn-non-utf8-commitencoding.sh |  1 +
 t/t9146-git-svn-empty-dirs.sh              |  2 +
 t/t9148-git-svn-propset.sh                 |  1 +
 t/t9160-git-svn-preserve-empty-dirs.sh     |  1 +
 t/t9162-git-svn-dcommit-interactive.sh     |  2 +
 t/t9164-git-svn-dcommit-concurrent.sh      |  2 +
 t/t9501-gitweb-standalone-http-status.sh   |  1 +
 wt-status.c                                | 15 +++--
 115 files changed, 416 insertions(+), 145 deletions(-)

Range-diff against v2:
 1:  8fbafe61053 !  1:  7c8298f564f t/helper/test-fast-rebase.c: don't leak "struct strbuf"
    @@ Commit message
         cases. But after f9500261e0a only the "if" block would free these two
         "struct strbuf".
     
    -    This change is needed so that a subsequent addition of a "goto
    -    cleanup" pattern when adding release_revisions() doesn't solve this
    -    unrelated bug while it's at it.
    -
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## t/helper/test-fast-rebase.c ##
 2:  77e7c2c321e !  2:  515bacc8f27 blame: use "goto cleanup" for cleanup_scoreboard()
    @@ Commit message
         Bloom filters, 2020-04-16) to use a "goto cleanup", so that we can be
         sure that we call cleanup_scoreboard().
     
    -    This change is needed so that a subsequent addition of a
    -    release_revisions() doesn't solve this unrelated issue while it's at
    -    it.
    -
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/blame.c ##
 3:  151d1175905 =  3:  58b59542c2b string_list API users: use string_list_init_{no,}dup
 4:  64a76629e23 =  4:  71495dfd073 format-patch: don't leak "extra_headers" or "ref_message_ids"
 5:  4c0718b43d7 !  5:  12fb45f02c1 revision.[ch]: split freeing of revs->commit into a function
    @@ revision.c: int setup_revisions(int argc, const char **argv, struct rev_info *re
      
     +static void release_revisions_commit_list(struct rev_info *revs)
     +{
    -+	struct commit_list *commits = revs->commits;
    -+
    -+	if (!commits)
    -+		return;
    -+	free_commit_list(commits);
    -+	revs->commits = NULL;
    ++	if (revs->commits) {
    ++		free_commit_list(revs->commits);
    ++		revs->commits = NULL;
    ++	}
     +}
     +
      static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
 6:  7194aac88e1 !  6:  1e93a60d81a revision.[ch]: provide and start using a release_revisions()
    @@ range-diff.c: int is_range_diff_range(const char *arg)
     
      ## revision.c ##
     @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)
    - 	revs->commits = NULL;
    + 	}
      }
      
     +void release_revisions(struct rev_info *revs)
     +{
    -+	if (!revs)
    -+		return;
     +	object_array_clear(&revs->pending);
     +}
     +
 7:  3cb36d358d6 =  7:  c4fc1c98e7b revisions API users: add straightforward release_revisions()
 8:  42ad1208934 !  8:  f8a9443fe6f revisions API users: use release_revisions() needing "{ 0 }" init
    @@ Metadata
     Author: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## Commit message ##
    -    revisions API users: use release_revisions() needing "{ 0 }" init
    +    revisions API users: add "goto cleanup" for "rev_info" early exit
     
    -    Use release_revisions() to various users of "struct rev_list" which
    -    need to have their "struct rev_info" zero-initialized before we can
    -    start using it. In all of these cases we might "goto cleanup" (or equivalent),
    +    Add release_revisions() in various users of "struct rev_info" that can
    +    mostly use a "goto cleanup" pattern, but also have an early "return"
    +    before we've called repo_init_revisions(). We need to avoid calling
    +    release_revisions() with uninitialized memory.
    +
    +    It would be a lot cleaner to be able to initialize "struct rev_info"
    +    with "{ 0 }" here, or if a "REV_INFO_INIT" existed, we'll hopefully
    +    get around to making the initialization easier in the future (now it
    +    can't be done via a macro).
    +
    +    Until then let's leave a "cleanup_no_rev[s]" in place to document the
    +    intention here. Only status_submodule() in builtin/submodule--helper.c
    +    strictly speaking needs this, the other ones could keep their "return"
    +    for the early exit. But let's have them also use the "goto
    +    cleanup[...]" for consistency, and for the eventual migration to
    +    simpler initialization.
     
         For the bundle.c code see the early exit case added in
         3bbbe467f29 (bundle verify: error out if called without an object
    @@ Commit message
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## bisect.c ##
    -@@ bisect.c: void read_bisect_terms(const char **read_bad, const char **read_good)
    -  */
    - enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
    - {
    --	struct rev_info revs;
    -+	struct rev_info revs = { 0 };
    - 	struct commit_list *tried;
    - 	int reaches = 0, all = 0, nr, steps;
    - 	enum bisect_error res = BISECT_OK;
     @@ bisect.c: enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
      
      	res = check_good_are_ancestors_of_bad(r, prefix, no_checkout);
      	if (res)
     -		return res;
    -+		goto cleanup;
    ++		goto cleanup_no_revs;
      
      	bisect_rev_setup(r, &revs, prefix, "%s", "^%s", 1);
      
    @@ bisect.c: enum bisect_error bisect_next_all(struct repository *r, const char *pr
     +	res = bisect_checkout(bisect_rev, no_checkout);
     +cleanup:
     +	release_revisions(&revs);
    ++cleanup_no_revs:
     +	return res;
      }
      
    @@ bisect.c: enum bisect_error bisect_next_all(struct repository *r, const char *pr
     
      ## builtin/submodule--helper.c ##
     @@ builtin/submodule--helper.c: static void status_submodule(const char *path, const struct object_id *ce_oid,
    - {
    - 	char *displaypath;
    - 	struct strvec diff_files_args = STRVEC_INIT;
    --	struct rev_info rev;
    -+	struct rev_info rev = { 0 };
    - 	int diff_files_result;
    - 	struct strbuf buf = STRBUF_INIT;
    - 	const char *git_dir;
    + 
    + 	if ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) {
    + 		print_status(flags, 'U', path, null_oid(), displaypath);
    +-		goto cleanup;
    ++		goto cleanup_no_rev;
    + 	}
    + 
    + 	strbuf_addf(&buf, "%s/.git", path);
     @@ builtin/submodule--helper.c: static void status_submodule(const char *path, const struct object_id *ce_oid,
    + 	    !is_git_directory(git_dir)) {
    + 		print_status(flags, '-', path, ce_oid, displaypath);
    + 		strbuf_release(&buf);
    +-		goto cleanup;
    ++		goto cleanup_no_rev;
    + 	}
    + 	strbuf_release(&buf);
    + 
    +@@ builtin/submodule--helper.c: static void status_submodule(const char *path, const struct object_id *ce_oid,
    + 	}
    + 
      cleanup:
    ++	release_revisions(&rev);
    ++cleanup_no_rev:
      	strvec_clear(&diff_files_args);
      	free(displaypath);
    -+	release_revisions(&rev);
      }
    - 
    - static void status_submodule_cb(const struct cache_entry *list_item,
     
      ## bundle.c ##
     @@ bundle.c: int verify_bundle(struct repository *r,
    - 	 * to be verbose about the errors
    - 	 */
    - 	struct string_list *p = &header->prerequisites;
    --	struct rev_info revs;
    -+	struct rev_info revs = { 0 };
    - 	const char *argv[] = {NULL, "--all", NULL};
    - 	struct commit *commit;
      	int i, ret = 0, req_nr;
      	const char *message = _("Repository lacks these prerequisite commits:");
      
    @@ bundle.c: int verify_bundle(struct repository *r,
     -		return error(_("need a repository to verify a bundle"));
     +	if (!r || !r->objects || !r->objects->odb) {
     +		ret = error(_("need a repository to verify a bundle"));
    -+		goto cleanup;
    ++		goto cleanup_no_revs;
     +	}
      
      	repo_init_revisions(r, &revs, NULL);
    @@ bundle.c: int verify_bundle(struct repository *r,
      			list_refs(r, 0, NULL);
      		}
      	}
    ++
     +cleanup:
     +	release_revisions(&revs);
    ++cleanup_no_revs:
      	return ret;
      }
      
14:  38f4f1c4ecb !  9:  5f5c0d26395 revisions API users: use release_revisions() in submodule.c edge case
    @@ Commit message
         revisions API users: use release_revisions() in submodule.c edge case
     
         Use release_revisions() on the the "struct rev_info" in
    -    show_submodule_diff_summary() where we need to pre-initialize with "{
    -    0 }" to make sure that we won't segfault when calling
    -    release_revisions().
    -
    -    Most functions that use "struct rev_info" will call
    -    repo_init_revisions() early, which will do an equivalent memset(). In
    -    this case we might "goto out" before doing the equivalent of that
    -    operation, see the "goto" on "(!left || !right || !sub)" added in
    +    show_submodule_diff_summary() where we'd otherwise need to do the
    +    equivalent of pre-initializing the "struct rev_info" with "{ 0 }" if
    +    we were going to add it to the cleanup being performed in the "out"
    +    part of the function, let's instead introduce an "out_no_rev" for the
    +    reasons discussed in the preceding commit.
    +
    +    Doing so for the "goto" on "(!left || !right || !sub)" added in
         8e6df65015f (submodule: refactor show_submodule_summary with helper
    -    function, 2016-08-31).
    +    function, 2016-08-31) would have been straightforward, as in the
    +    preceding commit.
    +
    +    But for the case of prepare_submodule_diff_summary() failing it's less
    +    straightforward. Reading the pre-image we could simply retain the
    +    "goto out" if it fails, because we can see that the first thing it
    +    does is call repo_init_revisions().
    +
    +    But having a hard reliance on that would be a bit nasty, as we'd
    +    potentially introduce a segfault if the function did some other
    +    initialization first, and early aborted if that failed.
    +
    +    Let's just fold that helper function away into
    +    show_submodule_diff_summary() itself, which was its only user. Now
    +    following the flow of initialization is more obvious, and it's
    +    immediately clear that the "goto out" if prepare_revision_walk()
    +    returns non-zero is safe.
     
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## submodule.c ##
    -@@ submodule.c: void show_submodule_diff_summary(struct diff_options *o, const char *path,
    - 		struct object_id *one, struct object_id *two,
    - 		unsigned dirty_submodule)
    +@@ submodule.c: void handle_ignore_submodules_arg(struct diff_options *diffopt,
    + 	 */
    + }
    + 
    +-static int prepare_submodule_diff_summary(struct repository *r, struct rev_info *rev,
    +-					  const char *path,
    +-					  struct commit *left, struct commit *right,
    +-					  struct commit_list *merge_bases)
    +-{
    +-	struct commit_list *list;
    +-
    +-	repo_init_revisions(r, rev, NULL);
    +-	setup_revisions(0, NULL, rev, NULL);
    +-	rev->left_right = 1;
    +-	rev->first_parent_only = 1;
    +-	left->object.flags |= SYMMETRIC_LEFT;
    +-	add_pending_object(rev, &left->object, path);
    +-	add_pending_object(rev, &right->object, path);
    +-	for (list = merge_bases; list; list = list->next) {
    +-		list->item->object.flags |= UNINTERESTING;
    +-		add_pending_object(rev, &list->item->object,
    +-			oid_to_hex(&list->item->object.oid));
    +-	}
    +-	return prepare_revision_walk(rev);
    +-}
    +-
    + static void print_submodule_diff_summary(struct repository *r, struct rev_info *rev, struct diff_options *o)
      {
    --	struct rev_info rev;
    -+	struct rev_info rev = { 0 };
    + 	static const char format[] = "  %m %s";
    +@@ submodule.c: void show_submodule_diff_summary(struct diff_options *o, const char *path,
      	struct commit *left = NULL, *right = NULL;
      	struct commit_list *merge_bases = NULL;
      	struct repository *sub;
    ++	struct commit_list *list;
    + 
    + 	sub = open_submodule(path);
    + 	show_submodule_header(o, path, one, two, dirty_submodule,
    +@@ submodule.c: void show_submodule_diff_summary(struct diff_options *o, const char *path,
    + 	 * all the information the user needs.
    + 	 */
    + 	if (!left || !right || !sub)
    +-		goto out;
    ++		goto out_no_rev;
    + 
    ++	repo_init_revisions(sub, &rev, NULL);
    ++	setup_revisions(0, NULL, &rev, NULL);
    ++	rev.left_right = 1;
    ++	rev.first_parent_only = 1;
    ++	left->object.flags |= SYMMETRIC_LEFT;
    ++	add_pending_object(&rev, &left->object, path);
    ++	add_pending_object(&rev, &right->object, path);
    ++	for (list = merge_bases; list; list = list->next) {
    ++		list->item->object.flags |= UNINTERESTING;
    ++		add_pending_object(&rev, &list->item->object,
    ++			oid_to_hex(&list->item->object.oid));
    ++	}
    + 	/* Treat revision walker failure the same as missing commits */
    +-	if (prepare_submodule_diff_summary(sub, &rev, path, left, right, merge_bases)) {
    ++	if (prepare_revision_walk(&rev)) {
    + 		diff_emit_submodule_error(o, "(revision walker failed)\n");
    + 		goto out;
    + 	}
     @@ submodule.c: void show_submodule_diff_summary(struct diff_options *o, const char *path,
      	print_submodule_diff_summary(sub, &rev, o);
      
      out:
     +	release_revisions(&rev);
    ++out_no_rev:
      	if (merge_bases)
      		free_commit_list(merge_bases);
      	clear_commit_marks(left, ~0);
 9:  88289157533 ! 10:  9da5c5f5f5d stash: always have the owner of "stash_info" free it
    @@ Commit message
     
         Change the initialization of the "revision" member of "struct
         stash_info" to be initialized vi a macro, and more importantly that
    -    that initializing function be tasked to free it, usually via "goto
    +    that initializing function be tasked to free it, usually via a "goto
         cleanup" pattern.
     
         Despite the "revision" name (and the topic of the series containing
10:  eafe57663fc ! 11:  da4fcd04345 revisions API users: add "goto cleanup" for release_revisions()
    @@ Metadata
      ## Commit message ##
         revisions API users: add "goto cleanup" for release_revisions()
     
    -    Add a release_revisions() to various users of "struct rev_list" which
    +    Add a release_revisions() to various users of "struct rev_info" which
         requires a minor refactoring to a "goto cleanup" pattern to use that
         function.
     
11:  6fb2caf4d17 = 12:  b6acc61fb61 revisions API users: use release_revisions() in http-push.c
12:  74818bc372f ! 13:  2eae0e809e5 revisions API users: use release_revisions() in builtin/log.c
    @@ Commit message
         "format-patch", etc. commands, all of which live in this file.
     
         The release_revisions() API still only frees the "pending" member, but
    -    will learn to more members of "struct rev_info" in subsequent commits.
    +    will learn to release more members of "struct rev_info" in subsequent
    +    commits.
     
         In the case of "format-patch" revert the addition of UNLEAK() in
    -    dee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16), and
    +    dee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16),
         which will cause several tests that previously passed under
         "TEST_PASSES_SANITIZE_LEAK=true" to start failing.
     
    @@ Commit message
         incremental progress in that direction. See [1] for further details
         about this approach.
     
    +    Note that the release_revisions() will not be sufficient to deal with
    +    the code in cmd_show() added in 5d7eeee2ac6 (git-show: grok blobs,
    +    trees and tags, too, 2006-12-14) which clobbers the "pending" array in
    +    the case of "OBJ_COMMIT". That will need to be dealt with by some
    +    future follow-up work.
    +
         1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/
     
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
13:  8717e6e8471 = 14:  216eb3b41f7 revisions API users: use release_revisions() with UNLEAK()
15:  566b739ac6c = 15:  f8e0eb52957 revisions API users: use release_revisions() for "prune_data" users
16:  bc36810e56e ! 16:  0a5662a9f67 revisions API: have release_revisions() release "commits"
    @@ Commit message
         revisions API: have release_revisions() release "commits"
     
         Extend the the release_revisions() function so that it frees the
    -    "commits" in the "struct ref_info".
    +    "commits" in the "struct rev_info".
     
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## revision.c ##
    -@@ revision.c: void release_revisions(struct rev_info *revs)
    +@@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)
    + 
    + void release_revisions(struct rev_info *revs)
      {
    - 	if (!revs)
    - 		return;
     +	release_revisions_commit_list(revs);
      	object_array_clear(&revs->pending);
      }
17:  a93251edf85 ! 17:  65803961ee2 revisions API: have release_revisions() release "mailmap"
    @@ Commit message
         revisions API: have release_revisions() release "mailmap"
     
         Extend the the release_revisions() function so that it frees the
    -    "prune_data" in the "struct ref_info".
    +    "mailmap" in the "struct rev_info".
     
         The log family of functions now calls the clear_mailmap() function
         added in fa8afd18e5a (revisions API: provide and use a
    @@ builtin/commit.c: static const char *find_author_by_nickname(const char *name)
     
      ## revision.c ##
     @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)
    - 	revs->commits = NULL;
    + 	}
      }
      
     +static void release_revisions_mailmap(struct string_list *mailmap)
    @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)
     +
      void release_revisions(struct rev_info *revs)
      {
    - 	if (!revs)
    - 		return;
      	release_revisions_commit_list(revs);
      	object_array_clear(&revs->pending);
     +	release_revisions_mailmap(revs->mailmap);
18:  424fac7ab95 ! 18:  ac68630dbf6 revisions API: have release_revisions() release "cmdline"
    @@ Commit message
         revisions API: have release_revisions() release "cmdline"
     
         Extend the the release_revisions() function so that it frees the
    -    "cmdline" in the "struct ref_info". This in combination with a
    +    "cmdline" in the "struct rev_info". This in combination with a
         preceding change to free "commits" and "mailmap" means that we can
         whitelist another test under "TEST_PASSES_SANITIZE_LEAK=true".
     
    @@ Commit message
     
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
    -    fixup with bc0e0439040?
    -
      ## revision.c ##
     @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)
    - 	revs->commits = NULL;
    + 	}
      }
      
     +static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)
    @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)
      {
      	if (!mailmap)
     @@ revision.c: void release_revisions(struct rev_info *revs)
    - 		return;
    + {
      	release_revisions_commit_list(revs);
      	object_array_clear(&revs->pending);
     +	release_revisions_cmdline(&revs->cmdline);
19:  d5b7a8a690e ! 19:  740abb80c57 revisions API: have release_revisions() release "filter"
    @@ Commit message
         revisions API: have release_revisions() release "filter"
     
         Extend the the release_revisions() function so that it frees the
    -    "cmdline" in the "struct ref_info". This in combination with a
    +    "filter" in the "struct rev_info". This in combination with a
         preceding change to free "cmdline" means that we can mark another set
         of tests as passing under "TEST_PASSES_SANITIZE_LEAK=true".
     
20:  857880bb4b6 ! 20:  26163e5e754 revisions API: have release_revisions() release "grep_filter"
    @@ Commit message
         revisions API: have release_revisions() release "grep_filter"
     
         Extend the the release_revisions() function so that it frees the
    -    "grep_filter" in the "struct ref_info".This allows us to mark a test
    +    "grep_filter" in the "struct rev_info".This allows us to mark a test
         as passing under "TEST_PASSES_SANITIZE_LEAK=true".
     
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
21:  f98d473f08e ! 21:  842faebddc4 revisions API: have release_revisions() release "prune_data"
    @@ Commit message
         revisions API: have release_revisions() release "prune_data"
     
         Extend the the release_revisions() function so that it frees the
    -    "prune_data" in the "struct ref_info". This means that any code that
    +    "prune_data" in the "struct rev_info". This means that any code that
         calls "release_revisions()" already can get rid of adjacent calls to
         clear_pathspec().
     
22:  827430315fa ! 22:  bf93197993c revisions API: clear "boundary_commits" in release_revisions()
    @@ Commit message
     
      ## revision.c ##
     @@ revision.c: void release_revisions(struct rev_info *revs)
    - 		return;
    + {
      	release_revisions_commit_list(revs);
      	object_array_clear(&revs->pending);
     +	object_array_clear(&revs->boundary_commits);
23:  c559b7d538b = 23:  af874c17fbd revisions API: release "reflog_info" in release revisions()
24:  c3a22f5b474 = 24:  006fa1fcc34 revisions API: call diff_free(&revs->pruning) in revisions_release()
25:  dabbb430ece = 25:  0eacbd66711 revisions API: have release_revisions() release "date_mode"
26:  644d1b54afe ! 26:  5efc0ae58ed revisions API: have release_revisions() release "topo_walk_info"
    @@ revision.c: static void release_revisions_mailmap(struct string_list *mailmap)
     +
      void release_revisions(struct rev_info *revs)
      {
    - 	if (!revs)
    + 	release_revisions_commit_list(revs);
     @@ revision.c: void release_revisions(struct rev_info *revs)
      	free_grep_patterns(&revs->grep_filter);
      	diff_free(&revs->pruning);
27:  2a4d50a0ec8 = 27:  6c6fdf696af revisions API: add a TODO for diff_free(&revs->diffopt)
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply	[flat|nested] 252+ messages in thread

* [PATCH v3 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf"
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 02/27] blame: use "goto cleanup" for cleanup_scoreboard() Ævar Arnfjörð Bjarmason
                       ` (26 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Fix a memory leak that's been with us since f9500261e0a (fast-rebase:
write conflict state to working tree, index, and HEAD, 2021-05-20)
changed this code to move these strbuf_release() into an if/else
block.

We'll also add to "reflog_msg" in the "else" arm of the "if" block
being modified here, and we'll append to "branch_msg" in both
cases. But after f9500261e0a only the "if" block would free these two
"struct strbuf".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 t/helper/test-fast-rebase.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c
index fc2d4609043..993b90eaedd 100644
--- a/t/helper/test-fast-rebase.c
+++ b/t/helper/test-fast-rebase.c
@@ -201,8 +201,6 @@ int cmd__fast_rebase(int argc, const char **argv)
 		}
 		if (create_symref("HEAD", branch_name.buf, reflog_msg.buf) < 0)
 			die(_("unable to update HEAD"));
-		strbuf_release(&reflog_msg);
-		strbuf_release(&branch_name);
 
 		prime_cache_tree(the_repository, the_repository->index,
 				 result.tree);
@@ -221,5 +219,8 @@ int cmd__fast_rebase(int argc, const char **argv)
 	if (write_locked_index(&the_index, &lock,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("unable to write %s"), get_index_file());
+
+	strbuf_release(&reflog_msg);
+	strbuf_release(&branch_name);
 	return (result.clean == 0);
 }
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 02/27] blame: use "goto cleanup" for cleanup_scoreboard()
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 03/27] string_list API users: use string_list_init_{no,}dup Ævar Arnfjörð Bjarmason
                       ` (25 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Amend a freeing pattern added in 0906ac2b54b (blame: use changed-path
Bloom filters, 2020-04-16) to use a "goto cleanup", so that we can be
sure that we call cleanup_scoreboard().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/blame.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/builtin/blame.c b/builtin/blame.c
index 8d15b68afc9..885b381ab83 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1167,7 +1167,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 	if (!incremental)
 		setup_pager();
 	else
-		return 0;
+		goto cleanup;
 
 	blame_sort_final(&sb);
 
@@ -1201,6 +1201,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 		printf("num commits: %d\n", sb.num_commits);
 	}
 
+cleanup:
 	cleanup_scoreboard(&sb);
 	return 0;
 }
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 03/27] string_list API users: use string_list_init_{no,}dup
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 02/27] blame: use "goto cleanup" for cleanup_scoreboard() Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids" Ævar Arnfjörð Bjarmason
                       ` (24 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Follow-up on the introduction of string_list_init_nodup() and
string_list_init_dup() in the series merged in bd4232fac33 (Merge
branch 'ab/struct-init', 2021-07-16) and convert code that implicitly
relied on xcalloc() being equivalent to the initializer to use
xmalloc() and string_list_init_{no,}dup() instead.

In the case of get_unmerged() in merge-recursive.c we used the
combination of xcalloc() and assigning "1" to "strdup_strings" to get
what we'd get via string_list_init_dup(), let's use that instead.

Adjacent code in cmd_format_patch() will be changed in a subsequent
commit, since we're changing that let's change the other in-tree
patterns that do the same. Let's also convert a "x == NULL" to "!x"
per our CodingGuidelines, as we need to change the "if" line anyway.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/log.c      | 9 ++++++---
 builtin/shortlog.c | 6 ++++--
 merge-recursive.c  | 4 ++--
 3 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/builtin/log.c b/builtin/log.c
index c211d66d1d0..634dc782cce 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -231,7 +231,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
 	}
 
 	if (mailmap) {
-		rev->mailmap = xcalloc(1, sizeof(struct string_list));
+		rev->mailmap = xmalloc(sizeof(struct string_list));
+		string_list_init_nodup(rev->mailmap);
 		read_mailmap(rev->mailmap);
 	}
 
@@ -2173,8 +2174,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		prepare_bases(&bases, base, list, nr);
 	}
 
-	if (in_reply_to || thread || cover_letter)
-		rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
+	if (in_reply_to || thread || cover_letter) {
+		rev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));
+		string_list_init_nodup(rev.ref_message_ids);
+	}
 	if (in_reply_to) {
 		const char *msgid = clean_message_id(in_reply_to);
 		string_list_append(rev.ref_message_ids, msgid);
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 26c5c0cf935..fcde07c9367 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -81,8 +81,10 @@ static void insert_one_record(struct shortlog *log,
 		format_subject(&subject, oneline, " ");
 		buffer = strbuf_detach(&subject, NULL);
 
-		if (item->util == NULL)
-			item->util = xcalloc(1, sizeof(struct string_list));
+		if (!item->util) {
+			item->util = xmalloc(sizeof(struct string_list));
+			string_list_init_nodup(item->util);
+		}
 		string_list_append(item->util, buffer);
 	}
 }
diff --git a/merge-recursive.c b/merge-recursive.c
index 1ee6364e8b1..32bbba5fbb1 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -522,10 +522,10 @@ static struct stage_data *insert_stage_data(struct repository *r,
  */
 static struct string_list *get_unmerged(struct index_state *istate)
 {
-	struct string_list *unmerged = xcalloc(1, sizeof(struct string_list));
+	struct string_list *unmerged = xmalloc(sizeof(struct string_list));
 	int i;
 
-	unmerged->strdup_strings = 1;
+	string_list_init_dup(unmerged);
 
 	/* TODO: audit for interaction with sparse-index. */
 	ensure_full_index(istate);
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids"
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (2 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 03/27] string_list API users: use string_list_init_{no,}dup Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 05/27] revision.[ch]: split freeing of revs->commit into a function Ævar Arnfjörð Bjarmason
                       ` (23 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Fix two memory leaks in "struct rev_info" by freeing that memory in
cmd_format_patch(). These two are unusual special-cases in being in
the "struct rev_info", but not being "owned" by the code in
revision.c. I.e. they're members of the struct so that this code in
"builtin/log.c" can pass information code in log-tree.c.

See 20ff06805c6 (format-patch: resurrect extra headers from config,
2006-06-02) and d1566f7883f (git-format-patch: Make the second and
subsequent mails replies to the first, 2006-07-14) for the initial
introduction of "extra_headers" and "ref_message_ids".

We can count on repo_init_revisions() memset()-ing this data to 0
however, so we can count on it being either NULL or something we
allocated. In the case of "extra_headers" let's add a local "char *"
variable to hold it, to avoid the eventual cast from "const char *"
when we free() it.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/log.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/builtin/log.c b/builtin/log.c
index 634dc782cce..6f9928fabfe 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1747,6 +1747,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	struct commit *commit;
 	struct commit **list = NULL;
 	struct rev_info rev;
+	char *to_free = NULL;
 	struct setup_revision_opt s_r_opt;
 	int nr = 0, total, i;
 	int use_stdout = 0;
@@ -1947,7 +1948,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		strbuf_addch(&buf, '\n');
 	}
 
-	rev.extra_headers = strbuf_detach(&buf, NULL);
+	rev.extra_headers = to_free = strbuf_detach(&buf, NULL);
 
 	if (from) {
 		if (split_ident_line(&rev.from_ident, from, strlen(from)))
@@ -2284,6 +2285,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	strbuf_release(&rdiff1);
 	strbuf_release(&rdiff2);
 	strbuf_release(&rdiff_title);
+	free(to_free);
+	if (rev.ref_message_ids)
+		string_list_clear(rev.ref_message_ids, 0);
+	free(rev.ref_message_ids);
 	UNLEAK(rev);
 	return 0;
 }
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 05/27] revision.[ch]: split freeing of revs->commit into a function
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (3 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids" Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 06/27] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
                       ` (22 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Move the existing code for invoking free_commit_list() and setting
revs->commits to NULL into a new release_revisions_commit_list()
function. This will be used as part of a general free()-ing mechanism
for "struct rev_info".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/revision.c b/revision.c
index 2646b78990e..ef45762bf21 100644
--- a/revision.c
+++ b/revision.c
@@ -2923,6 +2923,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 	return left;
 }
 
+static void release_revisions_commit_list(struct rev_info *revs)
+{
+	if (revs->commits) {
+		free_commit_list(revs->commits);
+		revs->commits = NULL;
+	}
+}
+
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
 {
 	struct commit_list *l = xcalloc(1, sizeof(*l));
@@ -4080,10 +4088,7 @@ static void create_boundary_commit_list(struct rev_info *revs)
 	 * boundary commits anyway.  (This is what the code has always
 	 * done.)
 	 */
-	if (revs->commits) {
-		free_commit_list(revs->commits);
-		revs->commits = NULL;
-	}
+	release_revisions_commit_list(revs);
 
 	/*
 	 * Put all of the actual boundary commits from revs->boundary_commits
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 06/27] revision.[ch]: provide and start using a release_revisions()
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (4 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 05/27] revision.[ch]: split freeing of revs->commit into a function Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 07/27] revisions API users: add straightforward release_revisions() Ævar Arnfjörð Bjarmason
                       ` (21 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

The users of the revision.[ch] API's "struct rev_info" are a major
source of memory leaks in the test suite under SANITIZE=leak, which in
turn adds a lot of noise when trying to mark up tests with
"TEST_PASSES_SANITIZE_LEAK=true".

The users of that API are largely one-shot, e.g. "git log", so
arguably freeing the memory is a waste of time, but in many cases
they've actually been trying to free the memory, and just doing that
in a buggy manner.

Let's provide a release_revisions() function for these users, and
start migrating them over per the plan outlined in [1]. Right now this
only handles the "pending" member of the struct, but more will be
added in subsequent commits.

Even though we only clear the "pending" member now, let's not leave a
trap in code like the pre-image of index_differs_from(), where we'd
start doing the wrong thing as soon as the release_revisions() learned
to clear its "diffopt". I.e. we need to call release_revisions() after
we've inspected any state in "struct rev_info".

This leaves in place e.g. clear_pathspec(&rev.prune_data) in
stash_working_tree() in builtin/stash.c, subsequent commits will teach
release_revisions() to free "prune_data" and other members that in
some cases are individually cleared by users of "struct rev_info" by
reaching into its members. Those subsequent commits will remove the
relevant calls to e.g. clear_pathspec().

We avoid amending code in index_differs_from() in diff-lib.c as well
as wt_status_collect_changes_index(), has_unstaged_changes() and
has_uncommitted_changes() in wt-status.c in a way that assumes that we
are already clearing the "diffopt" member. That will be handled in a
subsequent commit.

1. https://lore.kernel.org/git/87a6k8daeu.fsf@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/checkout.c | 2 +-
 builtin/stash.c    | 3 +--
 diff-lib.c         | 2 +-
 range-diff.c       | 2 +-
 revision.c         | 5 +++++
 revision.h         | 6 ++++++
 wt-status.c        | 5 +++--
 7 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 797681481d1..4d9e0bd3ac1 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -629,7 +629,7 @@ static void show_local_changes(struct object *head,
 	diff_setup_done(&rev.diffopt);
 	add_pending_object(&rev, head, NULL);
 	run_diff_index(&rev, 0);
-	object_array_clear(&rev.pending);
+	release_revisions(&rev);
 }
 
 static void describe_detached_head(const char *msg, struct commit *commit)
diff --git a/builtin/stash.c b/builtin/stash.c
index ccdfdab44be..ad74624c2f7 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1266,9 +1266,8 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
 
 done:
 	discard_index(&istate);
-	UNLEAK(rev);
-	object_array_clear(&rev.pending);
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 	strbuf_release(&diff_output);
 	remove_path(stash_index_path.buf);
 	return ret;
diff --git a/diff-lib.c b/diff-lib.c
index ca085a03efc..d6800274bd5 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -662,7 +662,7 @@ int index_differs_from(struct repository *r,
 		diff_flags_or(&rev.diffopt.flags, flags);
 	rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
 	run_diff_index(&rev, 1);
-	object_array_clear(&rev.pending);
+	release_revisions(&rev);
 	return (rev.diffopt.flags.has_changes != 0);
 }
 
diff --git a/range-diff.c b/range-diff.c
index b72eb9fdbee..39cc010c628 100644
--- a/range-diff.c
+++ b/range-diff.c
@@ -596,6 +596,6 @@ int is_range_diff_range(const char *arg)
 	}
 
 	free(copy);
-	object_array_clear(&revs.pending);
+	release_revisions(&revs);
 	return negative > 0 && positive > 0;
 }
diff --git a/revision.c b/revision.c
index ef45762bf21..895bba9ae57 100644
--- a/revision.c
+++ b/revision.c
@@ -2931,6 +2931,11 @@ static void release_revisions_commit_list(struct rev_info *revs)
 	}
 }
 
+void release_revisions(struct rev_info *revs)
+{
+	object_array_clear(&revs->pending);
+}
+
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
 {
 	struct commit_list *l = xcalloc(1, sizeof(*l));
diff --git a/revision.h b/revision.h
index 5bc59c7bfe1..ad325a30453 100644
--- a/revision.h
+++ b/revision.h
@@ -377,6 +377,12 @@ void repo_init_revisions(struct repository *r,
 int setup_revisions(int argc, const char **argv, struct rev_info *revs,
 		    struct setup_revision_opt *);
 
+/**
+ * Free data allocated in a "struct rev_info" after it's been setup
+ * with repo_init_revisions() and setup_revisions().
+ */
+void release_revisions(struct rev_info *revs);
+
 void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
 			const struct option *options,
 			const char * const usagestr[]);
diff --git a/wt-status.c b/wt-status.c
index d33f9272b72..922cf787f95 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -662,7 +662,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)
 
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_index(&rev, 1);
-	object_array_clear(&rev.pending);
+	release_revisions(&rev);
 	clear_pathspec(&rev.prune_data);
 }
 
@@ -2545,6 +2545,7 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)
 	rev_info.diffopt.flags.quick = 1;
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_files(&rev_info, 0);
+	release_revisions(&rev_info);
 	return diff_result_code(&rev_info.diffopt, result);
 }
 
@@ -2577,7 +2578,7 @@ int has_uncommitted_changes(struct repository *r,
 
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_index(&rev_info, 1);
-	object_array_clear(&rev_info.pending);
+	release_revisions(&rev_info);
 	return diff_result_code(&rev_info.diffopt, result);
 }
 
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 07/27] revisions API users: add straightforward release_revisions()
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (5 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 06/27] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 08/27] revisions API users: add "goto cleanup" for "rev_info" early exit Ævar Arnfjörð Bjarmason
                       ` (20 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Add a release_revisions() to various users of "struct rev_list" in
those straightforward cases where we only need to add the
release_revisions() call to the end of a block, and don't need to
e.g. refactor anything to use a "goto cleanup" pattern.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 add-interactive.c                | 1 +
 bisect.c                         | 2 ++
 builtin/add.c                    | 1 +
 builtin/am.c                     | 3 +++
 builtin/bisect--helper.c         | 2 ++
 builtin/blame.c                  | 1 +
 builtin/checkout.c               | 1 +
 builtin/commit.c                 | 1 +
 builtin/describe.c               | 2 ++
 builtin/fast-export.c            | 1 +
 builtin/merge.c                  | 2 ++
 builtin/pack-objects.c           | 2 ++
 builtin/prune.c                  | 1 +
 builtin/reflog.c                 | 1 +
 builtin/shortlog.c               | 2 ++
 builtin/submodule--helper.c      | 1 +
 fmt-merge-msg.c                  | 1 +
 merge-ort.c                      | 1 +
 merge-recursive.c                | 1 +
 midx.c                           | 1 +
 pack-bitmap-write.c              | 1 +
 ref-filter.c                     | 1 +
 remote.c                         | 1 +
 sequencer.c                      | 3 +++
 shallow.c                        | 1 +
 submodule.c                      | 2 ++
 t/helper/test-revision-walking.c | 1 +
 wt-status.c                      | 1 +
 28 files changed, 39 insertions(+)

diff --git a/add-interactive.c b/add-interactive.c
index 72472103017..54cdfc82017 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -570,6 +570,7 @@ static int get_modified_files(struct repository *r,
 
 		if (ps)
 			clear_pathspec(&rev.prune_data);
+		release_revisions(&rev);
 	}
 	hashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);
 	if (unmerged_count)
diff --git a/bisect.c b/bisect.c
index 9e6a2b7f201..cc6b8b6230d 100644
--- a/bisect.c
+++ b/bisect.c
@@ -884,6 +884,7 @@ static int check_ancestors(struct repository *r, int rev_nr,
 	/* Clean up objects used, as they will be reused. */
 	clear_commit_marks_many(rev_nr, rev, ALL_REV_FLAGS);
 
+	release_revisions(&revs);
 	return res;
 }
 
@@ -964,6 +965,7 @@ static void show_diff_tree(struct repository *r,
 
 	setup_revisions(ARRAY_SIZE(argv) - 1, argv, &opt, NULL);
 	log_tree_commit(&opt, commit);
+	release_revisions(&opt);
 }
 
 /*
diff --git a/builtin/add.c b/builtin/add.c
index 3ffb86a4338..f507d2191cd 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -340,6 +340,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
 
 	unlink(file);
 	free(file);
+	release_revisions(&rev);
 	return 0;
 }
 
diff --git a/builtin/am.c b/builtin/am.c
index 0f4111bafa0..93bec62afa9 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1397,6 +1397,7 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm
 	add_pending_object(&rev_info, &commit->object, "");
 	diff_setup_done(&rev_info.diffopt);
 	log_tree_commit(&rev_info, commit);
+	release_revisions(&rev_info);
 }
 
 /**
@@ -1429,6 +1430,7 @@ static void write_index_patch(const struct am_state *state)
 	add_pending_object(&rev_info, &tree->object, "");
 	diff_setup_done(&rev_info.diffopt);
 	run_diff_index(&rev_info, 1);
+	release_revisions(&rev_info);
 }
 
 /**
@@ -1582,6 +1584,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
 		add_pending_oid(&rev_info, "HEAD", &our_tree, 0);
 		diff_setup_done(&rev_info.diffopt);
 		run_diff_index(&rev_info, 1);
+		release_revisions(&rev_info);
 	}
 
 	if (run_apply(state, index_path))
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 8b2b259ff0d..e4d7b6779ae 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -555,6 +555,7 @@ static int bisect_skipped_commits(struct bisect_terms *terms)
 	reset_revision_walk();
 
 	strbuf_release(&commit_name);
+	release_revisions(&revs);
 	fclose(fp);
 	return 0;
 }
@@ -1041,6 +1042,7 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar
 						oid_to_hex(&commit->object.oid));
 
 			reset_revision_walk();
+			release_revisions(&revs);
 		} else {
 			strvec_push(&argv_state, argv[i]);
 		}
diff --git a/builtin/blame.c b/builtin/blame.c
index 885b381ab83..24bac822c56 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1203,5 +1203,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 
 cleanup:
 	cleanup_scoreboard(&sb);
+	release_revisions(&revs);
 	return 0;
 }
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 4d9e0bd3ac1..7ad4a7113c9 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1082,6 +1082,7 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne
 
 	/* Clean up objects used, as they will be reused. */
 	repo_clear_commit_marks(the_repository, ALL_REV_FLAGS);
+	release_revisions(&revs);
 }
 
 static int switch_branches(const struct checkout_opts *opts,
diff --git a/builtin/commit.c b/builtin/commit.c
index 009a1de0a3d..c7eda9bbb72 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1123,6 +1123,7 @@ static const char *find_author_by_nickname(const char *name)
 		strbuf_release(&buf);
 		format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
 		clear_mailmap(&mailmap);
+		release_revisions(&revs);
 		return strbuf_detach(&buf, NULL);
 	}
 	die(_("--author '%s' is not 'Name <email>' and matches no existing author"), name);
diff --git a/builtin/describe.c b/builtin/describe.c
index 42159cd26bd..a76f1a1a7a7 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -517,6 +517,7 @@ static void describe_blob(struct object_id oid, struct strbuf *dst)
 
 	traverse_commit_list(&revs, process_commit, process_object, &pcd);
 	reset_revision_walk();
+	release_revisions(&revs);
 }
 
 static void describe(const char *arg, int last_one)
@@ -667,6 +668,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
 				suffix = NULL;
 			else
 				suffix = dirty;
+			release_revisions(&revs);
 		}
 		describe("HEAD", 1);
 	} else if (dirty) {
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index a7d72697fba..f34ae451ee3 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -1275,6 +1275,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
 		printf("done\n");
 
 	refspec_clear(&refspecs);
+	release_revisions(&revs);
 
 	return 0;
 }
diff --git a/builtin/merge.c b/builtin/merge.c
index f178f5a3ee1..d9784d4891c 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -443,6 +443,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
 	}
 	write_file_buf(git_path_squash_msg(the_repository), out.buf, out.len);
 	strbuf_release(&out);
+	release_revisions(&rev);
 }
 
 static void finish(struct commit *head_commit,
@@ -998,6 +999,7 @@ static int evaluate_result(void)
 	 */
 	cnt += count_unmerged_entries();
 
+	release_revisions(&rev);
 	return cnt;
 }
 
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 829ca359cf9..0aa194f68dc 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -3384,6 +3384,7 @@ static void read_packs_list_from_stdin(void)
 	strbuf_release(&buf);
 	string_list_clear(&include_packs, 0);
 	string_list_clear(&exclude_packs, 0);
+	release_revisions(&revs);
 }
 
 static void read_object_list_from_stdin(void)
@@ -3800,6 +3801,7 @@ static void get_object_list(int ac, const char **av)
 	if (unpack_unreachable)
 		loosen_unused_packed_objects();
 
+	release_revisions(&revs);
 	oid_array_clear(&recent_objects);
 }
 
diff --git a/builtin/prune.c b/builtin/prune.c
index c2bcdc07db4..df376b2ed1e 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -196,5 +196,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
 		prune_shallow(show_only ? PRUNE_SHOW_ONLY : 0);
 	}
 
+	release_revisions(&revs);
 	return 0;
 }
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 9407f835cb6..592d5d33442 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -248,6 +248,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 		if (verbose)
 			printf(_("Marking reachable objects..."));
 		mark_reachable_objects(&revs, 0, 0, NULL);
+		release_revisions(&revs);
 		if (verbose)
 			putchar('\n');
 	}
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index fcde07c9367..35825f075e3 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -422,6 +422,8 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 	else
 		get_from_rev(&rev, &log);
 
+	release_revisions(&rev);
+
 	shortlog_output(&log);
 	if (log.file != stdout)
 		fclose(log.file);
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 5301612d24b..24980863f68 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1269,6 +1269,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 		run_diff_files(&rev, 0);
 	prepare_submodule_summary(info, &list);
 	strvec_clear(&diff_args);
+	release_revisions(&rev);
 	return 0;
 }
 
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index baca57d5b64..f48f44f9cd1 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -699,6 +699,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 			shortlog(origins.items[i].string,
 				 origins.items[i].util,
 				 head, &rev, opts, out);
+		release_revisions(&rev);
 	}
 
 	strbuf_complete_line(out);
diff --git a/merge-ort.c b/merge-ort.c
index 8545354dafd..4c0d4eba19b 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -1594,6 +1594,7 @@ static int find_first_merges(struct repository *repo,
 	}
 
 	object_array_clear(&merges);
+	release_revisions(&revs);
 	return result->nr;
 }
 
diff --git a/merge-recursive.c b/merge-recursive.c
index 32bbba5fbb1..acd13b2b069 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -1160,6 +1160,7 @@ static int find_first_merges(struct repository *repo,
 	}
 
 	object_array_clear(&merges);
+	release_revisions(&revs);
 	return result->nr;
 }
 
diff --git a/midx.c b/midx.c
index 865170bad05..702c8a9b178 100644
--- a/midx.c
+++ b/midx.c
@@ -1061,6 +1061,7 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr
 	if (indexed_commits_nr_p)
 		*indexed_commits_nr_p = cb.commits_nr;
 
+	release_revisions(&revs);
 	return cb.commits;
 }
 
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index cab3eaa2acd..ea8e0b51cdf 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -326,6 +326,7 @@ static void bitmap_builder_init(struct bitmap_builder *bb,
 	trace2_data_intmax("pack-bitmap-write", the_repository,
 			   "num_maximal_commits", num_maximal);
 
+	release_revisions(&revs);
 	free_commit_list(reusable);
 }
 
diff --git a/ref-filter.c b/ref-filter.c
index 7838bd22b8d..a91688bbf17 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2392,6 +2392,7 @@ static void reach_filter(struct ref_array *array,
 		clear_commit_marks(merge_commit, ALL_REV_FLAGS);
 	}
 
+	release_revisions(&revs);
 	free(to_clear);
 }
 
diff --git a/remote.c b/remote.c
index 42a4e7106e1..fa3152a5d52 100644
--- a/remote.c
+++ b/remote.c
@@ -2172,6 +2172,7 @@ static int stat_branch_pair(const char *branch_name, const char *base,
 	clear_commit_marks(theirs, ALL_REV_FLAGS);
 
 	strvec_clear(&argv);
+	release_revisions(&revs);
 	return 1;
 }
 
diff --git a/sequencer.c b/sequencer.c
index a1bb39383db..f9d7acd1065 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1347,6 +1347,7 @@ void print_commit_summary(struct repository *r,
 		log_tree_commit(&rev, commit);
 	}
 
+	release_revisions(&rev);
 	strbuf_release(&format);
 }
 
@@ -3415,6 +3416,7 @@ static int make_patch(struct repository *r,
 		unuse_commit_buffer(commit, commit_buffer);
 	}
 	strbuf_release(&buf);
+	release_revisions(&log_tree_opt);
 
 	return res;
 }
@@ -4525,6 +4527,7 @@ static int pick_commits(struct repository *r,
 					      &log_tree_opt.diffopt);
 				log_tree_diff_flush(&log_tree_opt);
 			}
+			release_revisions(&log_tree_opt);
 		}
 		flush_rewritten_pending();
 		if (!stat(rebase_path_rewritten_list(), &st) &&
diff --git a/shallow.c b/shallow.c
index 71e5876f377..2552f139f61 100644
--- a/shallow.c
+++ b/shallow.c
@@ -261,6 +261,7 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,
 		if ((o->flags & both_flags) == both_flags)
 			o->flags &= ~not_shallow_flag;
 	}
+	release_revisions(&revs);
 	return result;
 }
 
diff --git a/submodule.c b/submodule.c
index 5ace18a7d94..0510cb193b6 100644
--- a/submodule.c
+++ b/submodule.c
@@ -902,9 +902,11 @@ static void collect_changed_submodules(struct repository *r,
 		diff_rev.diffopt.format_callback_data = &data;
 		diff_rev.dense_combined_merges = 1;
 		diff_tree_combined_merge(commit, &diff_rev);
+		release_revisions(&diff_rev);
 	}
 
 	reset_revision_walk();
+	release_revisions(&rev);
 }
 
 static void free_submodules_oids(struct string_list *submodules)
diff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c
index 625b2dbf822..4a45d5bac2a 100644
--- a/t/helper/test-revision-walking.c
+++ b/t/helper/test-revision-walking.c
@@ -43,6 +43,7 @@ static int run_revision_walk(void)
 	}
 
 	reset_revision_walk();
+	release_revisions(&rev);
 	return got_revision;
 }
 
diff --git a/wt-status.c b/wt-status.c
index 922cf787f95..f9100621375 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1152,6 +1152,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
 		rev.diffopt.b_prefix = "w/";
 		run_diff_files(&rev, 0);
 	}
+	release_revisions(&rev);
 }
 
 static void wt_longstatus_print_tracking(struct wt_status *s)
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 08/27] revisions API users: add "goto cleanup" for "rev_info" early exit
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (6 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 07/27] revisions API users: add straightforward release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 20:30       ` Junio C Hamano
  2022-03-25 17:18     ` [PATCH v3 09/27] revisions API users: use release_revisions() in submodule.c edge case Ævar Arnfjörð Bjarmason
                       ` (19 subsequent siblings)
  27 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Add release_revisions() in various users of "struct rev_info" that can
mostly use a "goto cleanup" pattern, but also have an early "return"
before we've called repo_init_revisions(). We need to avoid calling
release_revisions() with uninitialized memory.

It would be a lot cleaner to be able to initialize "struct rev_info"
with "{ 0 }" here, or if a "REV_INFO_INIT" existed, we'll hopefully
get around to making the initialization easier in the future (now it
can't be done via a macro).

Until then let's leave a "cleanup_no_rev[s]" in place to document the
intention here. Only status_submodule() in builtin/submodule--helper.c
strictly speaking needs this, the other ones could keep their "return"
for the early exit. But let's have them also use the "goto
cleanup[...]" for consistency, and for the eventual migration to
simpler initialization.

For the bundle.c code see the early exit case added in
3bbbe467f29 (bundle verify: error out if called without an object
database, 2019-05-27).

For the relevant bisect.c code see 45b6370812c (bisect: libify
`check_good_are_ancestors_of_bad` and its dependents, 2020-02-17).

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 bisect.c                    | 17 ++++++++++++-----
 builtin/submodule--helper.c |  6 ++++--
 bundle.c                    | 12 +++++++++---
 3 files changed, 25 insertions(+), 10 deletions(-)

diff --git a/bisect.c b/bisect.c
index cc6b8b6230d..c2e9f6ca9f6 100644
--- a/bisect.c
+++ b/bisect.c
@@ -1035,7 +1035,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 
 	res = check_good_are_ancestors_of_bad(r, prefix, no_checkout);
 	if (res)
-		return res;
+		goto cleanup_no_revs;
 
 	bisect_rev_setup(r, &revs, prefix, "%s", "^%s", 1);
 
@@ -1060,14 +1060,16 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 		       term_good,
 		       term_bad);
 
-		return BISECT_FAILED;
+		res = BISECT_FAILED;
+		goto cleanup;
 	}
 
 	if (!all) {
 		fprintf(stderr, _("No testable commit found.\n"
 			"Maybe you started with bad path arguments?\n"));
 
-		return BISECT_NO_TESTABLE_COMMIT;
+		res = BISECT_NO_TESTABLE_COMMIT;
+		goto cleanup;
 	}
 
 	bisect_rev = &revs.commits->item->object.oid;
@@ -1087,7 +1089,8 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 		 * for negative return values for early returns up
 		 * until the cmd_bisect__helper() caller.
 		 */
-		return BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;
+		res = BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;
+		goto cleanup;
 	}
 
 	nr = all - reaches - 1;
@@ -1106,7 +1109,11 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 	/* Clean up objects used, as they will be reused. */
 	repo_clear_commit_marks(r, ALL_REV_FLAGS);
 
-	return bisect_checkout(bisect_rev, no_checkout);
+	res = bisect_checkout(bisect_rev, no_checkout);
+cleanup:
+	release_revisions(&revs);
+cleanup_no_revs:
+	return res;
 }
 
 static inline int log2i(int n)
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 24980863f68..d1b656c0909 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -779,7 +779,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
 
 	if ((CE_STAGEMASK & ce_flags) >> CE_STAGESHIFT) {
 		print_status(flags, 'U', path, null_oid(), displaypath);
-		goto cleanup;
+		goto cleanup_no_rev;
 	}
 
 	strbuf_addf(&buf, "%s/.git", path);
@@ -791,7 +791,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
 	    !is_git_directory(git_dir)) {
 		print_status(flags, '-', path, ce_oid, displaypath);
 		strbuf_release(&buf);
-		goto cleanup;
+		goto cleanup_no_rev;
 	}
 	strbuf_release(&buf);
 
@@ -851,6 +851,8 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
 	}
 
 cleanup:
+	release_revisions(&rev);
+cleanup_no_rev:
 	strvec_clear(&diff_files_args);
 	free(displaypath);
 }
diff --git a/bundle.c b/bundle.c
index e359370cfcd..9b7c0bc55c4 100644
--- a/bundle.c
+++ b/bundle.c
@@ -202,8 +202,10 @@ int verify_bundle(struct repository *r,
 	int i, ret = 0, req_nr;
 	const char *message = _("Repository lacks these prerequisite commits:");
 
-	if (!r || !r->objects || !r->objects->odb)
-		return error(_("need a repository to verify a bundle"));
+	if (!r || !r->objects || !r->objects->odb) {
+		ret = error(_("need a repository to verify a bundle"));
+		goto cleanup_no_revs;
+	}
 
 	repo_init_revisions(r, &revs, NULL);
 	for (i = 0; i < p->nr; i++) {
@@ -221,7 +223,7 @@ int verify_bundle(struct repository *r,
 		error("%s %s", oid_to_hex(oid), name);
 	}
 	if (revs.pending.nr != p->nr)
-		return ret;
+		goto cleanup;
 	req_nr = revs.pending.nr;
 	setup_revisions(2, argv, &revs, NULL);
 
@@ -283,6 +285,10 @@ int verify_bundle(struct repository *r,
 			list_refs(r, 0, NULL);
 		}
 	}
+
+cleanup:
+	release_revisions(&revs);
+cleanup_no_revs:
 	return ret;
 }
 
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 09/27] revisions API users: use release_revisions() in submodule.c edge case
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (7 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 08/27] revisions API users: add "goto cleanup" for "rev_info" early exit Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 10/27] stash: always have the owner of "stash_info" free it Ævar Arnfjörð Bjarmason
                       ` (18 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Use release_revisions() on the the "struct rev_info" in
show_submodule_diff_summary() where we'd otherwise need to do the
equivalent of pre-initializing the "struct rev_info" with "{ 0 }" if
we were going to add it to the cleanup being performed in the "out"
part of the function, let's instead introduce an "out_no_rev" for the
reasons discussed in the preceding commit.

Doing so for the "goto" on "(!left || !right || !sub)" added in
8e6df65015f (submodule: refactor show_submodule_summary with helper
function, 2016-08-31) would have been straightforward, as in the
preceding commit.

But for the case of prepare_submodule_diff_summary() failing it's less
straightforward. Reading the pre-image we could simply retain the
"goto out" if it fails, because we can see that the first thing it
does is call repo_init_revisions().

But having a hard reliance on that would be a bit nasty, as we'd
potentially introduce a segfault if the function did some other
initialization first, and early aborted if that failed.

Let's just fold that helper function away into
show_submodule_diff_summary() itself, which was its only user. Now
following the flow of initialization is more obvious, and it's
immediately clear that the "goto out" if prepare_revision_walk()
returns non-zero is safe.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 submodule.c | 41 +++++++++++++++++------------------------
 1 file changed, 17 insertions(+), 24 deletions(-)

diff --git a/submodule.c b/submodule.c
index 0510cb193b6..1dd61476307 100644
--- a/submodule.c
+++ b/submodule.c
@@ -477,28 +477,6 @@ void handle_ignore_submodules_arg(struct diff_options *diffopt,
 	 */
 }
 
-static int prepare_submodule_diff_summary(struct repository *r, struct rev_info *rev,
-					  const char *path,
-					  struct commit *left, struct commit *right,
-					  struct commit_list *merge_bases)
-{
-	struct commit_list *list;
-
-	repo_init_revisions(r, rev, NULL);
-	setup_revisions(0, NULL, rev, NULL);
-	rev->left_right = 1;
-	rev->first_parent_only = 1;
-	left->object.flags |= SYMMETRIC_LEFT;
-	add_pending_object(rev, &left->object, path);
-	add_pending_object(rev, &right->object, path);
-	for (list = merge_bases; list; list = list->next) {
-		list->item->object.flags |= UNINTERESTING;
-		add_pending_object(rev, &list->item->object,
-			oid_to_hex(&list->item->object.oid));
-	}
-	return prepare_revision_walk(rev);
-}
-
 static void print_submodule_diff_summary(struct repository *r, struct rev_info *rev, struct diff_options *o)
 {
 	static const char format[] = "  %m %s";
@@ -642,6 +620,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,
 	struct commit *left = NULL, *right = NULL;
 	struct commit_list *merge_bases = NULL;
 	struct repository *sub;
+	struct commit_list *list;
 
 	sub = open_submodule(path);
 	show_submodule_header(o, path, one, two, dirty_submodule,
@@ -653,10 +632,22 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,
 	 * all the information the user needs.
 	 */
 	if (!left || !right || !sub)
-		goto out;
+		goto out_no_rev;
 
+	repo_init_revisions(sub, &rev, NULL);
+	setup_revisions(0, NULL, &rev, NULL);
+	rev.left_right = 1;
+	rev.first_parent_only = 1;
+	left->object.flags |= SYMMETRIC_LEFT;
+	add_pending_object(&rev, &left->object, path);
+	add_pending_object(&rev, &right->object, path);
+	for (list = merge_bases; list; list = list->next) {
+		list->item->object.flags |= UNINTERESTING;
+		add_pending_object(&rev, &list->item->object,
+			oid_to_hex(&list->item->object.oid));
+	}
 	/* Treat revision walker failure the same as missing commits */
-	if (prepare_submodule_diff_summary(sub, &rev, path, left, right, merge_bases)) {
+	if (prepare_revision_walk(&rev)) {
 		diff_emit_submodule_error(o, "(revision walker failed)\n");
 		goto out;
 	}
@@ -664,6 +655,8 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,
 	print_submodule_diff_summary(sub, &rev, o);
 
 out:
+	release_revisions(&rev);
+out_no_rev:
 	if (merge_bases)
 		free_commit_list(merge_bases);
 	clear_commit_marks(left, ~0);
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 10/27] stash: always have the owner of "stash_info" free it
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (8 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 09/27] revisions API users: use release_revisions() in submodule.c edge case Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 11/27] revisions API users: add "goto cleanup" for release_revisions() Ævar Arnfjörð Bjarmason
                       ` (17 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Change the initialization of the "revision" member of "struct
stash_info" to be initialized vi a macro, and more importantly that
that initializing function be tasked to free it, usually via a "goto
cleanup" pattern.

Despite the "revision" name (and the topic of the series containing
this commit) the "stash info" has nothing to do with the "struct
rev_info". I'm making this change because in the subsequent commit
when we do want to free the "struct rev_info" via a "goto cleanup"
pattern we'd otherwise free() uninitialized memory in some cases, as
we only strbuf_init() the string in get_stash_info().

So while it's the smallest possible change, let's convert all users of
this pattern in the file while we're at it.

A good follow-up to this change would be to change all the "ret = -1;
goto done;" in this file to instead use a "goto cleanup", and
initialize "int ret = -1" at the start of the relevant functions. That
would allow us to drop a lot of needless brace verbosity on two-line
"if" statements, but let's leave that alone for now.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/stash.c | 70 ++++++++++++++++++++++++++-----------------------
 1 file changed, 37 insertions(+), 33 deletions(-)

diff --git a/builtin/stash.c b/builtin/stash.c
index ad74624c2f7..891572d807c 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -116,6 +116,10 @@ struct stash_info {
 	int has_u;
 };
 
+#define STASH_INFO_INIT { \
+	.revision = STRBUF_INIT, \
+}
+
 static void free_stash_info(struct stash_info *info)
 {
 	strbuf_release(&info->revision);
@@ -157,10 +161,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
 	if (argc == 1)
 		commit = argv[0];
 
-	strbuf_init(&info->revision, 0);
 	if (!commit) {
 		if (!ref_exists(ref_stash)) {
-			free_stash_info(info);
 			fprintf_ln(stderr, _("No stash entries found."));
 			return -1;
 		}
@@ -174,11 +176,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
 
 	revision = info->revision.buf;
 
-	if (get_oid(revision, &info->w_commit)) {
-		error(_("%s is not a valid reference"), revision);
-		free_stash_info(info);
-		return -1;
-	}
+	if (get_oid(revision, &info->w_commit))
+		return error(_("%s is not a valid reference"), revision);
 
 	assert_stash_like(info, revision);
 
@@ -197,7 +196,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
 		info->is_stash_ref = !strcmp(expanded_ref, ref_stash);
 		break;
 	default: /* Invalid or ambiguous */
-		free_stash_info(info);
+		break;
 	}
 
 	free(expanded_ref);
@@ -598,10 +597,10 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
 
 static int apply_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret;
+	int ret = -1;
 	int quiet = 0;
 	int index = 0;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_BOOL(0, "index", &index,
@@ -613,9 +612,10 @@ static int apply_stash(int argc, const char **argv, const char *prefix)
 			     git_stash_apply_usage, 0);
 
 	if (get_stash_info(&info, argc, argv))
-		return -1;
+		goto cleanup;
 
 	ret = do_apply_stash(prefix, &info, index, quiet);
+cleanup:
 	free_stash_info(&info);
 	return ret;
 }
@@ -662,9 +662,9 @@ static void assert_stash_ref(struct stash_info *info)
 
 static int drop_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret;
+	int ret = -1;
 	int quiet = 0;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_END()
@@ -674,21 +674,22 @@ static int drop_stash(int argc, const char **argv, const char *prefix)
 			     git_stash_drop_usage, 0);
 
 	if (get_stash_info(&info, argc, argv))
-		return -1;
+		goto cleanup;
 
 	assert_stash_ref(&info);
 
 	ret = do_drop_stash(&info, quiet);
+cleanup:
 	free_stash_info(&info);
 	return ret;
 }
 
 static int pop_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret;
+	int ret = -1;
 	int index = 0;
 	int quiet = 0;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_BOOL(0, "index", &index,
@@ -700,7 +701,7 @@ static int pop_stash(int argc, const char **argv, const char *prefix)
 			     git_stash_pop_usage, 0);
 
 	if (get_stash_info(&info, argc, argv))
-		return -1;
+		goto cleanup;
 
 	assert_stash_ref(&info);
 	if ((ret = do_apply_stash(prefix, &info, index, quiet)))
@@ -709,15 +710,16 @@ static int pop_stash(int argc, const char **argv, const char *prefix)
 	else
 		ret = do_drop_stash(&info, quiet);
 
+cleanup:
 	free_stash_info(&info);
 	return ret;
 }
 
 static int branch_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret;
+	int ret = -1;
 	const char *branch = NULL;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct child_process cp = CHILD_PROCESS_INIT;
 	struct option options[] = {
 		OPT_END()
@@ -734,7 +736,7 @@ static int branch_stash(int argc, const char **argv, const char *prefix)
 	branch = argv[0];
 
 	if (get_stash_info(&info, argc - 1, argv + 1))
-		return -1;
+		goto cleanup;
 
 	cp.git_cmd = 1;
 	strvec_pushl(&cp.args, "checkout", "-b", NULL);
@@ -746,8 +748,8 @@ static int branch_stash(int argc, const char **argv, const char *prefix)
 	if (!ret && info.is_stash_ref)
 		ret = do_drop_stash(&info, 0);
 
+cleanup:
 	free_stash_info(&info);
-
 	return ret;
 }
 
@@ -825,8 +827,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op
 static int show_stash(int argc, const char **argv, const char *prefix)
 {
 	int i;
-	int ret = 0;
-	struct stash_info info;
+	int ret = -1;
+	struct stash_info info = STASH_INFO_INIT;
 	struct rev_info rev;
 	struct strvec stash_args = STRVEC_INIT;
 	struct strvec revision_args = STRVEC_INIT;
@@ -861,10 +863,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 			strvec_push(&revision_args, argv[i]);
 	}
 
-	ret = get_stash_info(&info, stash_args.nr, stash_args.v);
-	strvec_clear(&stash_args);
-	if (ret)
-		return -1;
+	if (get_stash_info(&info, stash_args.nr, stash_args.v))
+		goto cleanup;
 
 	/*
 	 * The config settings are applied only if there are not passed
@@ -878,8 +878,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 			rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
 
 		if (!show_stat && !show_patch) {
-			free_stash_info(&info);
-			return 0;
+			ret = 0;
+			goto cleanup;
 		}
 	}
 
@@ -912,8 +912,11 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 	}
 	log_tree_diff_flush(&rev);
 
+	ret = diff_result_code(&rev.diffopt, 0);;
+cleanup:
+	strvec_clear(&stash_args);
 	free_stash_info(&info);
-	return diff_result_code(&rev.diffopt, 0);
+	return ret;
 }
 
 static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,
@@ -1409,9 +1412,9 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
 
 static int create_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret = 0;
+	int ret;
 	struct strbuf stash_msg_buf = STRBUF_INIT;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct pathspec ps;
 
 	/* Starting with argv[1], since argv[0] is "create" */
@@ -1426,6 +1429,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)
 	if (!ret)
 		printf_ln("%s", oid_to_hex(&info.w_commit));
 
+	free_stash_info(&info);
 	strbuf_release(&stash_msg_buf);
 	return ret;
 }
@@ -1434,7 +1438,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
 			 int keep_index, int patch_mode, int include_untracked, int only_staged)
 {
 	int ret = 0;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct strbuf patch = STRBUF_INIT;
 	struct strbuf stash_msg_buf = STRBUF_INIT;
 	struct strbuf untracked_files = STRBUF_INIT;
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 11/27] revisions API users: add "goto cleanup" for release_revisions()
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (9 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 10/27] stash: always have the owner of "stash_info" free it Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 12/27] revisions API users: use release_revisions() in http-push.c Ævar Arnfjörð Bjarmason
                       ` (16 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Add a release_revisions() to various users of "struct rev_info" which
requires a minor refactoring to a "goto cleanup" pattern to use that
function.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/diff-files.c        |  8 ++++++--
 builtin/rev-list.c          | 19 ++++++++++++-------
 builtin/stash.c             |  1 +
 builtin/submodule--helper.c | 10 +++++++---
 sequencer.c                 | 23 ++++++++++++++++-------
 t/helper/test-fast-rebase.c | 18 +++++++++++++-----
 6 files changed, 55 insertions(+), 24 deletions(-)

diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index 70103c40952..2bfaf9ba7ae 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -77,8 +77,12 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
 
 	if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
 		perror("read_cache_preload");
-		return -1;
+		result = -1;
+		goto cleanup;
 	}
+cleanup:
 	result = run_diff_files(&rev, options);
-	return diff_result_code(&rev.diffopt, result);
+	result = diff_result_code(&rev.diffopt, result);
+	release_revisions(&rev);
+	return result;
 }
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 572da1472e5..2f0b6c759e4 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -502,6 +502,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 	int use_bitmap_index = 0;
 	int filter_provided_objects = 0;
 	const char *show_progress = NULL;
+	int ret = 0;
 
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage(rev_list_usage);
@@ -585,7 +586,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 		}
 		if (!strcmp(arg, "--test-bitmap")) {
 			test_bitmap_walk(&revs);
-			return 0;
+			goto cleanup;
 		}
 		if (skip_prefix(arg, "--progress=", &arg)) {
 			show_progress = arg;
@@ -674,11 +675,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 
 	if (use_bitmap_index) {
 		if (!try_bitmap_count(&revs, filter_provided_objects))
-			return 0;
+			goto cleanup;
 		if (!try_bitmap_disk_usage(&revs, filter_provided_objects))
-			return 0;
+			goto cleanup;
 		if (!try_bitmap_traversal(&revs, filter_provided_objects))
-			return 0;
+			goto cleanup;
 	}
 
 	if (prepare_revision_walk(&revs))
@@ -698,8 +699,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 
 		find_bisection(&revs.commits, &reaches, &all, bisect_flags);
 
-		if (bisect_show_vars)
-			return show_bisect_vars(&info, reaches, all);
+		if (bisect_show_vars) {
+			ret = show_bisect_vars(&info, reaches, all);
+			goto cleanup;
+		}
 	}
 
 	if (filter_provided_objects) {
@@ -754,5 +757,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 	if (show_disk_usage)
 		printf("%"PRIuMAX"\n", (uintmax_t)total_disk_usage);
 
-	return 0;
+cleanup:
+	release_revisions(&revs);
+	return ret;
 }
diff --git a/builtin/stash.c b/builtin/stash.c
index 891572d807c..76d162387e2 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -916,6 +916,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 cleanup:
 	strvec_clear(&stash_args);
 	free_stash_info(&info);
+	release_revisions(&rev);
 	return ret;
 }
 
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index d1b656c0909..5d4c0b62ba9 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1233,6 +1233,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 	struct strvec diff_args = STRVEC_INIT;
 	struct rev_info rev;
 	struct module_cb_list list = MODULE_CB_LIST_INIT;
+	int ret = 0;
 
 	strvec_push(&diff_args, get_diff_cmd(diff_cmd));
 	if (info->cached)
@@ -1258,11 +1259,13 @@ static int compute_summary_module_list(struct object_id *head_oid,
 			setup_work_tree();
 		if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
 			perror("read_cache_preload");
-			return -1;
+			ret = -1;
+			goto cleanup;
 		}
 	} else if (read_cache() < 0) {
 		perror("read_cache");
-		return -1;
+		ret = -1;
+		goto cleanup;
 	}
 
 	if (diff_cmd == DIFF_INDEX)
@@ -1270,9 +1273,10 @@ static int compute_summary_module_list(struct object_id *head_oid,
 	else
 		run_diff_files(&rev, 0);
 	prepare_submodule_summary(info, &list);
+cleanup:
 	strvec_clear(&diff_args);
 	release_revisions(&rev);
-	return 0;
+	return ret;
 }
 
 static int module_summary(int argc, const char **argv, const char *prefix)
diff --git a/sequencer.c b/sequencer.c
index f9d7acd1065..41ae5e25278 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -5354,6 +5354,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
 	int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
 	int reapply_cherry_picks = flags & TODO_LIST_REAPPLY_CHERRY_PICKS;
 	int skipped_commit = 0;
+	int ret = 0;
 
 	repo_init_revisions(r, &revs, NULL);
 	revs.verbose_header = 1;
@@ -5377,14 +5378,20 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
 	pp.fmt = revs.commit_format;
 	pp.output_encoding = get_log_output_encoding();
 
-	if (setup_revisions(argc, argv, &revs, NULL) > 1)
-		return error(_("make_script: unhandled options"));
+	if (setup_revisions(argc, argv, &revs, NULL) > 1) {
+		ret = error(_("make_script: unhandled options"));
+		goto cleanup;
+	}
 
-	if (prepare_revision_walk(&revs) < 0)
-		return error(_("make_script: error preparing revisions"));
+	if (prepare_revision_walk(&revs) < 0) {
+		ret = error(_("make_script: error preparing revisions"));
+		goto cleanup;
+	}
 
-	if (rebase_merges)
-		return make_script_with_merges(&pp, &revs, out, flags);
+	if (rebase_merges) {
+		ret = make_script_with_merges(&pp, &revs, out, flags);
+		goto cleanup;
+	}
 
 	while ((commit = get_revision(&revs))) {
 		int is_empty = is_original_commit_empty(commit);
@@ -5408,7 +5415,9 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
 	if (skipped_commit)
 		advise_if_enabled(ADVICE_SKIPPED_CHERRY_PICKS,
 				  _("use --reapply-cherry-picks to include skipped commits"));
-	return 0;
+cleanup:
+	release_revisions(&revs);
+	return ret;
 }
 
 /*
diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c
index 993b90eaedd..4e5553e2024 100644
--- a/t/helper/test-fast-rebase.c
+++ b/t/helper/test-fast-rebase.c
@@ -99,6 +99,7 @@ int cmd__fast_rebase(int argc, const char **argv)
 	struct merge_result result;
 	struct strbuf reflog_msg = STRBUF_INIT;
 	struct strbuf branch_name = STRBUF_INIT;
+	int ret = 0;
 
 	/*
 	 * test-tool stuff doesn't set up the git directory by default; need to
@@ -137,13 +138,17 @@ int cmd__fast_rebase(int argc, const char **argv)
 	revs.topo_order = 1;
 	strvec_pushl(&rev_walk_args, "", argv[4], "--not", argv[3], NULL);
 
-	if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1)
-		return error(_("unhandled options"));
+	if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) {
+		ret = error(_("unhandled options"));
+		goto cleanup;
+	}
 
 	strvec_clear(&rev_walk_args);
 
-	if (prepare_revision_walk(&revs) < 0)
-		return error(_("error preparing revisions"));
+	if (prepare_revision_walk(&revs) < 0) {
+		ret = error(_("error preparing revisions"));
+		goto cleanup;
+	}
 
 	init_merge_options(&merge_opt, the_repository);
 	memset(&result, 0, sizeof(result));
@@ -220,7 +225,10 @@ int cmd__fast_rebase(int argc, const char **argv)
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("unable to write %s"), get_index_file());
 
+	ret = (result.clean == 0);
+cleanup:
 	strbuf_release(&reflog_msg);
 	strbuf_release(&branch_name);
-	return (result.clean == 0);
+	release_revisions(&revs);
+	return ret;
 }
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 12/27] revisions API users: use release_revisions() in http-push.c
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (10 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 11/27] revisions API users: add "goto cleanup" for release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 13/27] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
                       ` (15 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

In the case of cmd_main() in http-push.c we need to move the
deceleration of the "struct rev-list" into the loop over the
"remote_refs" when adding a release_revisions().

We'd previously set up the "revs" for each remote, but would
potentially leak memory on each one.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 http-push.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/http-push.c b/http-push.c
index f0c044dcf76..01e7c2ac5c8 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1689,7 +1689,6 @@ int cmd_main(int argc, const char **argv)
 	struct refspec rs = REFSPEC_INIT_PUSH;
 	struct remote_lock *ref_lock = NULL;
 	struct remote_lock *info_ref_lock = NULL;
-	struct rev_info revs;
 	int delete_branch = 0;
 	int force_delete = 0;
 	int objects_to_send;
@@ -1825,6 +1824,7 @@ int cmd_main(int argc, const char **argv)
 
 	new_refs = 0;
 	for (ref = remote_refs; ref; ref = ref->next) {
+		struct rev_info revs;
 		struct strvec commit_argv = STRVEC_INIT;
 
 		if (!ref->peer_ref)
@@ -1941,6 +1941,7 @@ int cmd_main(int argc, const char **argv)
 		unlock_remote(ref_lock);
 		check_locks();
 		strvec_clear(&commit_argv);
+		release_revisions(&revs);
 	}
 
 	/* Update remote server info if appropriate */
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 13/27] revisions API users: use release_revisions() in builtin/log.c
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (11 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 12/27] revisions API users: use release_revisions() in http-push.c Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 14/27] revisions API users: use release_revisions() with UNLEAK() Ævar Arnfjörð Bjarmason
                       ` (14 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

In preparation for having the "log" family of functions make wider use
of release_revisions() let's have them call it just before
exiting. This changes the "log", "whatchanged", "show",
"format-patch", etc. commands, all of which live in this file.

The release_revisions() API still only frees the "pending" member, but
will learn to release more members of "struct rev_info" in subsequent
commits.

In the case of "format-patch" revert the addition of UNLEAK() in
dee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16),
which will cause several tests that previously passed under
"TEST_PASSES_SANITIZE_LEAK=true" to start failing.

In subsequent commits we'll now be able to use those tests to check
whether that part of the API is really leaking memory, and will fix
all of those memory leaks. Removing the UNLEAK() allows us to make
incremental progress in that direction. See [1] for further details
about this approach.

Note that the release_revisions() will not be sufficient to deal with
the code in cmd_show() added in 5d7eeee2ac6 (git-show: grok blobs,
trees and tags, too, 2006-12-14) which clobbers the "pending" array in
the case of "OBJ_COMMIT". That will need to be dealt with by some
future follow-up work.

1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/log.c          | 20 ++++++++++++--------
 t/t4126-apply-empty.sh |  2 --
 2 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/builtin/log.c b/builtin/log.c
index 6f9928fabfe..c40ebe1c3f4 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -295,6 +295,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
 	cmd_log_init_finish(argc, argv, prefix, rev, opt);
 }
 
+static int cmd_log_deinit(int ret, struct rev_info *rev)
+{
+	release_revisions(rev);
+	return ret;
+}
+
 /*
  * This gives a rough estimate for how many commits we
  * will print out in the list.
@@ -558,7 +564,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
 	cmd_log_init(argc, argv, prefix, &rev, &opt);
 	if (!rev.diffopt.output_format)
 		rev.diffopt.output_format = DIFF_FORMAT_RAW;
-	return cmd_log_walk(&rev);
+	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 }
 
 static void show_tagger(const char *buf, struct rev_info *rev)
@@ -677,7 +683,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
 	cmd_log_init(argc, argv, prefix, &rev, &opt);
 
 	if (!rev.no_walk)
-		return cmd_log_walk(&rev);
+		return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 
 	count = rev.pending.nr;
 	objects = rev.pending.objects;
@@ -732,8 +738,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
 			ret = error(_("unknown type: %d"), o->type);
 		}
 	}
-	free(objects);
-	return ret;
+	return cmd_log_deinit(ret, &rev);
 }
 
 /*
@@ -761,7 +766,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
 	rev.always_show_header = 1;
 	cmd_log_init_finish(argc, argv, prefix, &rev, &opt);
 
-	return cmd_log_walk(&rev);
+	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 }
 
 static void log_setup_revisions_tweak(struct rev_info *rev,
@@ -792,7 +797,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)
 	opt.revarg_opt = REVARG_COMMITTISH;
 	opt.tweak = log_setup_revisions_tweak;
 	cmd_log_init(argc, argv, prefix, &rev, &opt);
-	return cmd_log_walk(&rev);
+	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 }
 
 /* format-patch */
@@ -2289,8 +2294,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	if (rev.ref_message_ids)
 		string_list_clear(rev.ref_message_ids, 0);
 	free(rev.ref_message_ids);
-	UNLEAK(rev);
-	return 0;
+	return cmd_log_deinit(0, &rev);
 }
 
 static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
diff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh
index 33860d38290..66a7ba8ab8f 100755
--- a/t/t4126-apply-empty.sh
+++ b/t/t4126-apply-empty.sh
@@ -2,8 +2,6 @@
 
 test_description='apply empty'
 
-
-TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 14/27] revisions API users: use release_revisions() with UNLEAK()
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (12 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 13/27] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 15/27] revisions API users: use release_revisions() for "prune_data" users Ævar Arnfjörð Bjarmason
                       ` (13 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Use a release_revisions() with those "struct rev_list" users which
already "UNLEAK" the struct. It may seem odd to simultaneously attempt
to free() memory, but also to explicitly ignore whether we have memory
leaks in the same.

As explained in preceding commits this is being done to use the
built-in commands as a guinea pig for whether the release_revisions()
function works as expected, we'd like to test e.g. whether we segfault
as we change it. In subsequent commits we'll then remove these
UNLEAK() as the function is made to free the memory that caused us to
add them in the first place.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/diff-index.c | 4 +++-
 builtin/diff.c       | 1 +
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 5fd23ab5b6c..3a83183c312 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -71,5 +71,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
 	}
 	result = run_diff_index(&rev, option);
 	UNLEAK(rev);
-	return diff_result_code(&rev.diffopt, result);
+	result = diff_result_code(&rev.diffopt, result);
+	release_revisions(&rev);
+	return result;
 }
diff --git a/builtin/diff.c b/builtin/diff.c
index bb7fafca618..dd48336da56 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -595,6 +595,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
 	if (1 < rev.diffopt.skip_stat_unmatch)
 		refresh_index_quietly();
 	UNLEAK(rev);
+	release_revisions(&rev);
 	UNLEAK(ent);
 	UNLEAK(blob);
 	return result;
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 15/27] revisions API users: use release_revisions() for "prune_data" users
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (13 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 14/27] revisions API users: use release_revisions() with UNLEAK() Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 16/27] revisions API: have release_revisions() release "commits" Ævar Arnfjörð Bjarmason
                       ` (12 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Use release_revisions() for users of "struct rev_list" that reach into
the "struct rev_info" and clear the "prune_data" already.

In a subsequent commit we'll teach release_revisions() to clear this
itself, but in the meantime let's invoke release_revisions() here to
clear anything else we may have missed, and for reasons of having
consistent boilerplate.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/add.c   | 1 +
 builtin/stash.c | 1 +
 diff-lib.c      | 1 +
 wt-status.c     | 1 +
 4 files changed, 4 insertions(+)

diff --git a/builtin/add.c b/builtin/add.c
index f507d2191cd..115a26ea633 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -143,6 +143,7 @@ int add_files_to_cache(const char *prefix,
 	rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
 	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 	return !!data.add_errors;
 }
 
diff --git a/builtin/stash.c b/builtin/stash.c
index 76d162387e2..a6ee030d4bd 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1060,6 +1060,7 @@ static int check_changes_tracked_files(const struct pathspec *ps)
 
 done:
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 	return ret;
 }
 
diff --git a/diff-lib.c b/diff-lib.c
index d6800274bd5..0f16281253f 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -642,6 +642,7 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
 	if (diff_cache(&revs, tree_oid, NULL, 1))
 		exit(128);
 	clear_pathspec(&revs.prune_data);
+	release_revisions(&revs);
 	return 0;
 }
 
diff --git a/wt-status.c b/wt-status.c
index f9100621375..a14fad1e03a 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -617,6 +617,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_files(&rev, 0);
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 }
 
 static void wt_status_collect_changes_index(struct wt_status *s)
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 16/27] revisions API: have release_revisions() release "commits"
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (14 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 15/27] revisions API users: use release_revisions() for "prune_data" users Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 17/27] revisions API: have release_revisions() release "mailmap" Ævar Arnfjörð Bjarmason
                       ` (11 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"commits" in the "struct rev_info".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/revision.c b/revision.c
index 895bba9ae57..84b47552a3e 100644
--- a/revision.c
+++ b/revision.c
@@ -2933,6 +2933,7 @@ static void release_revisions_commit_list(struct rev_info *revs)
 
 void release_revisions(struct rev_info *revs)
 {
+	release_revisions_commit_list(revs);
 	object_array_clear(&revs->pending);
 }
 
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 17/27] revisions API: have release_revisions() release "mailmap"
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (15 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 16/27] revisions API: have release_revisions() release "commits" Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 18/27] revisions API: have release_revisions() release "cmdline" Ævar Arnfjörð Bjarmason
                       ` (10 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"mailmap" in the "struct rev_info".

The log family of functions now calls the clear_mailmap() function
added in fa8afd18e5a (revisions API: provide and use a
release_revisions(), 2021-09-19), allowing us to whitelist some tests
with "TEST_PASSES_SANITIZE_LEAK=true".

Unfortunately having a pointer to a mailmap in "struct rev_info"
instead of an embedded member that we "own" get a bit messy, as can be
seen in the change to builtin/commit.c.

When we free() this data we won't be able to tell apart a pointer to a
"mailmap" on the heap from one on the stack. As seen in
ea57bc0d41b (log: add --use-mailmap option, 2013-01-05) the "log"
family allocates it on the heap, but in the find_author_by_nickname()
code added in ea16794e430 (commit: search author pattern against
mailmap, 2013-08-23) we allocated it on the stack instead.

Ideally we'd simply change that member to a "struct string_list
mailmap" and never free() the "mailmap" itself, but that would be a
much larger change to the revisions API.

We have code that needs to hand an existing "mailmap" to a "struct
rev_info", while we could change all of that, let's not go there
now.

The complexity isn't in the ownership of the "mailmap" per-se, but
that various things assume a "rev_info.mailmap == NULL" means "doesn't
want mailmap", if we changed that to an init'd "struct string_list
we'd need to carefully refactor things to change those assumptions.

Let's instead always free() it, and simply declare that if you add
such a "mailmap" it must be allocated on the heap. Any modern libc
will correctly panic if we free() a stack variable, so this should be
safe going forward.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/commit.c                   | 5 ++---
 revision.c                         | 9 +++++++++
 t/t0056-git-C.sh                   | 1 +
 t/t3302-notes-index-expensive.sh   | 1 +
 t/t4055-diff-context.sh            | 1 +
 t/t4066-diff-emit-delay.sh         | 1 +
 t/t7008-filter-branch-null-sha1.sh | 1 +
 7 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index c7eda9bbb72..cd6cebcf8c8 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1100,7 +1100,6 @@ static const char *find_author_by_nickname(const char *name)
 	struct rev_info revs;
 	struct commit *commit;
 	struct strbuf buf = STRBUF_INIT;
-	struct string_list mailmap = STRING_LIST_INIT_NODUP;
 	const char *av[20];
 	int ac = 0;
 
@@ -1111,7 +1110,8 @@ static const char *find_author_by_nickname(const char *name)
 	av[++ac] = buf.buf;
 	av[++ac] = NULL;
 	setup_revisions(ac, av, &revs, NULL);
-	revs.mailmap = &mailmap;
+	revs.mailmap = xmalloc(sizeof(struct string_list));
+	string_list_init_nodup(revs.mailmap);
 	read_mailmap(revs.mailmap);
 
 	if (prepare_revision_walk(&revs))
@@ -1122,7 +1122,6 @@ static const char *find_author_by_nickname(const char *name)
 		ctx.date_mode.type = DATE_NORMAL;
 		strbuf_release(&buf);
 		format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
-		clear_mailmap(&mailmap);
 		release_revisions(&revs);
 		return strbuf_detach(&buf, NULL);
 	}
diff --git a/revision.c b/revision.c
index 84b47552a3e..8520823d503 100644
--- a/revision.c
+++ b/revision.c
@@ -2931,10 +2931,19 @@ static void release_revisions_commit_list(struct rev_info *revs)
 	}
 }
 
+static void release_revisions_mailmap(struct string_list *mailmap)
+{
+	if (!mailmap)
+		return;
+	clear_mailmap(mailmap);
+	free(mailmap);
+}
+
 void release_revisions(struct rev_info *revs)
 {
 	release_revisions_commit_list(revs);
 	object_array_clear(&revs->pending);
+	release_revisions_mailmap(revs->mailmap);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
diff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh
index 2630e756dab..752aa8c9454 100755
--- a/t/t0056-git-C.sh
+++ b/t/t0056-git-C.sh
@@ -2,6 +2,7 @@
 
 test_description='"-C <path>" option and its effects on other path-related options'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success '"git -C <path>" runs git from the directory <path>' '
diff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh
index bb5fea02a03..d0c4d38b4d4 100755
--- a/t/t3302-notes-index-expensive.sh
+++ b/t/t3302-notes-index-expensive.sh
@@ -8,6 +8,7 @@ test_description='Test commit notes index (expensive!)'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 create_repo () {
diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh
index 741e0803c1a..73048d0a526 100755
--- a/t/t4055-diff-context.sh
+++ b/t/t4055-diff-context.sh
@@ -5,6 +5,7 @@
 
 test_description='diff.context configuration'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t4066-diff-emit-delay.sh b/t/t4066-diff-emit-delay.sh
index a1de63b77f8..0ecb3915412 100755
--- a/t/t4066-diff-emit-delay.sh
+++ b/t/t4066-diff-emit-delay.sh
@@ -4,6 +4,7 @@ test_description='test combined/stat/moved interaction'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # This test covers a weird 3-way interaction between "--cc -p", which will run
diff --git a/t/t7008-filter-branch-null-sha1.sh b/t/t7008-filter-branch-null-sha1.sh
index 9ba9f24ad2f..93fbc92b8db 100755
--- a/t/t7008-filter-branch-null-sha1.sh
+++ b/t/t7008-filter-branch-null-sha1.sh
@@ -1,6 +1,7 @@
 #!/bin/sh
 
 test_description='filter-branch removal of trees with null sha1'
+
 . ./test-lib.sh
 
 test_expect_success 'setup: base commits' '
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 18/27] revisions API: have release_revisions() release "cmdline"
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (16 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 17/27] revisions API: have release_revisions() release "mailmap" Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 19/27] revisions API: have release_revisions() release "filter" Ævar Arnfjörð Bjarmason
                       ` (9 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"cmdline" in the "struct rev_info". This in combination with a
preceding change to free "commits" and "mailmap" means that we can
whitelist another test under "TEST_PASSES_SANITIZE_LEAK=true".

There was a proposal in [1] to do away with xstrdup()-ing this
add_rev_cmdline(), perhaps that would be worthwhile, but for now let's
just free() it.

We could also make that a "char *" in "struct rev_cmdline_entry"
itself, but since we own it let's expose it as a constant to outside
callers. I proposed that in [2] but have since changed my mind. See
14d30cdfc04 (ref-filter: fix memory leak in `free_array_item()`,
2019-07-10), c514c62a4fd (checkout: fix leak of non-existent branch
names, 2020-08-14) and other log history hits for "free((char *)" for
prior art.

This includes the tests we had false-positive passes on before my
6798b08e848 (perl Git.pm: don't ignore signalled failure in
_cmd_close(), 2022-02-01), now they pass for real.

Since there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to
list those that don't pass than to touch most of those 66. So let's
introduce a "TEST_FAILS_SANITIZE_LEAK=true", which if set in the tests
won't cause lib-git-svn.sh to set "TEST_PASSES_SANITIZE_LEAK=true.

This change also marks all the tests that we removed
"TEST_FAILS_SANITIZE_LEAK=true" from in an earlier commit due to
removing the UNLEAK() from cmd_format_patch(), we can now assert that
its API use doesn't leak any "struct rev_info" memory.

This change also made commit "t5503-tagfollow.sh" pass on current
master, but that would regress when combined with
ps/fetch-atomic-fixup's de004e848a9 (t5503: simplify setup of test
which exercises failure of backfill, 2022-03-03) (through no fault of
that topic, that change started using "git clone" in the test, which
has an outstanding leak). Let's leave that test out for now to avoid
in-flight semantic conflicts.

1. https://lore.kernel.org/git/YUj%2FgFRh6pwrZalY@carlos-mbp.lan/
2. https://lore.kernel.org/git/87o88obkb1.fsf@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c                                 | 12 ++++++++++++
 t/lib-git-svn.sh                           |  4 ++++
 t/t0062-revision-walking.sh                |  1 +
 t/t0101-at-syntax.sh                       |  2 ++
 t/t1060-object-corruption.sh               |  1 +
 t/t3303-notes-subtrees.sh                  |  1 +
 t/t3305-notes-fanout.sh                    |  1 +
 t/t3408-rebase-multi-line.sh               |  1 +
 t/t4027-diff-submodule.sh                  |  1 +
 t/t4128-apply-root.sh                      |  1 +
 t/t4212-log-corrupt.sh                     |  1 +
 t/t5515-fetch-merge-logic.sh               |  1 +
 t/t5518-fetch-exit-status.sh               |  1 +
 t/t6002-rev-list-bisect.sh                 |  1 +
 t/t6003-rev-list-topo-order.sh             |  1 +
 t/t6005-rev-list-count.sh                  |  1 +
 t/t6018-rev-list-glob.sh                   |  1 +
 t/t6100-rev-list-in-order.sh               |  1 +
 t/t9100-git-svn-basic.sh                   |  1 +
 t/t9101-git-svn-props.sh                   |  2 ++
 t/t9104-git-svn-follow-parent.sh           |  2 ++
 t/t9106-git-svn-commit-diff-clobber.sh     |  2 ++
 t/t9115-git-svn-dcommit-funky-renames.sh   |  1 +
 t/t9116-git-svn-log.sh                     |  2 ++
 t/t9122-git-svn-author.sh                  |  2 ++
 t/t9127-git-svn-partial-rebuild.sh         |  2 ++
 t/t9129-git-svn-i18n-commitencoding.sh     |  1 +
 t/t9132-git-svn-broken-symlink.sh          |  1 +
 t/t9139-git-svn-non-utf8-commitencoding.sh |  1 +
 t/t9146-git-svn-empty-dirs.sh              |  2 ++
 t/t9148-git-svn-propset.sh                 |  1 +
 t/t9151-svn-mergeinfo.sh                   |  1 +
 t/t9160-git-svn-preserve-empty-dirs.sh     |  1 +
 t/t9162-git-svn-dcommit-interactive.sh     |  2 ++
 t/t9164-git-svn-dcommit-concurrent.sh      |  2 ++
 t/t9501-gitweb-standalone-http-status.sh   |  1 +
 36 files changed, 60 insertions(+)

diff --git a/revision.c b/revision.c
index 8520823d503..d864b9e2064 100644
--- a/revision.c
+++ b/revision.c
@@ -2931,6 +2931,17 @@ static void release_revisions_commit_list(struct rev_info *revs)
 	}
 }
 
+static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)
+{
+	unsigned int i;
+
+	if (!cmdline)
+		return;
+	for (i = 0; i < cmdline->nr; i++)
+		free((char *)cmdline->rev[i].name);
+	FREE_AND_NULL(cmdline->rev);
+}
+
 static void release_revisions_mailmap(struct string_list *mailmap)
 {
 	if (!mailmap)
@@ -2943,6 +2954,7 @@ void release_revisions(struct rev_info *revs)
 {
 	release_revisions_commit_list(revs);
 	object_array_clear(&revs->pending);
+	release_revisions_cmdline(&revs->cmdline);
 	release_revisions_mailmap(revs->mailmap);
 }
 
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 2fde2353fd3..ea28971e8ee 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -1,3 +1,7 @@
+if test -z "$TEST_FAILS_SANITIZE_LEAK"
+then
+	TEST_PASSES_SANITIZE_LEAK=true
+fi
 . ./test-lib.sh
 
 if test -n "$NO_SVN_TESTS"
diff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh
index 8e215867b8c..b9480c81781 100755
--- a/t/t0062-revision-walking.sh
+++ b/t/t0062-revision-walking.sh
@@ -5,6 +5,7 @@
 
 test_description='Test revision walking api'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 cat >run_twice_expected <<-EOF
diff --git a/t/t0101-at-syntax.sh b/t/t0101-at-syntax.sh
index a1998b558f9..878aadd64c9 100755
--- a/t/t0101-at-syntax.sh
+++ b/t/t0101-at-syntax.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='various @{whatever} syntax tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh
index bc89371f534..e8a58b15897 100755
--- a/t/t1060-object-corruption.sh
+++ b/t/t1060-object-corruption.sh
@@ -1,6 +1,7 @@
 #!/bin/sh
 
 test_description='see how we handle various forms of corruption'
+
 . ./test-lib.sh
 
 # convert "1234abcd" to ".git/objects/12/34abcd"
diff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh
index eac193757bf..bc9b791d1b9 100755
--- a/t/t3303-notes-subtrees.sh
+++ b/t/t3303-notes-subtrees.sh
@@ -5,6 +5,7 @@ test_description='Test commit notes organized in subtrees'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 number_of_commits=100
diff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh
index 9976d787f47..64a9915761a 100755
--- a/t/t3305-notes-fanout.sh
+++ b/t/t3305-notes-fanout.sh
@@ -2,6 +2,7 @@
 
 test_description='Test that adding/removing many notes triggers automatic fanout restructuring'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 path_has_fanout() {
diff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh
index cde3562e3a6..7b4607d72f2 100755
--- a/t/t3408-rebase-multi-line.sh
+++ b/t/t3408-rebase-multi-line.sh
@@ -5,6 +5,7 @@ test_description='rebasing a commit with multi-line first paragraph.'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
index 295da987cce..40164ae07d2 100755
--- a/t/t4027-diff-submodule.sh
+++ b/t/t4027-diff-submodule.sh
@@ -2,6 +2,7 @@
 
 test_description='difference in submodules'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh
 
diff --git a/t/t4128-apply-root.sh b/t/t4128-apply-root.sh
index f6db5a79dd9..ed94c90204e 100755
--- a/t/t4128-apply-root.sh
+++ b/t/t4128-apply-root.sh
@@ -2,6 +2,7 @@
 
 test_description='apply same filename'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh
index 0244888a5a7..30a219894bb 100755
--- a/t/t4212-log-corrupt.sh
+++ b/t/t4212-log-corrupt.sh
@@ -2,6 +2,7 @@
 
 test_description='git log with invalid commit headers'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh
index 320d26796d2..c100a809c5e 100755
--- a/t/t5515-fetch-merge-logic.sh
+++ b/t/t5515-fetch-merge-logic.sh
@@ -14,6 +14,7 @@ export GIT_TEST_PROTOCOL_VERSION
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 build_script () {
diff --git a/t/t5518-fetch-exit-status.sh b/t/t5518-fetch-exit-status.sh
index 5c4ac2556e7..c13120088fa 100755
--- a/t/t5518-fetch-exit-status.sh
+++ b/t/t5518-fetch-exit-status.sh
@@ -8,6 +8,7 @@ test_description='fetch exit status test'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index b95a0212adf..162cf50778d 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -4,6 +4,7 @@
 #
 test_description='Tests git rev-list --bisect functionality'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
 
diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh
index 24d1836f417..1f7d7dd20c1 100755
--- a/t/t6003-rev-list-topo-order.sh
+++ b/t/t6003-rev-list-topo-order.sh
@@ -5,6 +5,7 @@
 
 test_description='Tests git rev-list --topo-order functionality'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
 
diff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh
index e960049f647..0729f800c3c 100755
--- a/t/t6005-rev-list-count.sh
+++ b/t/t6005-rev-list-count.sh
@@ -2,6 +2,7 @@
 
 test_description='git rev-list --max-count and --skip test'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
index 24b34add833..e1abc5c2b32 100755
--- a/t/t6018-rev-list-glob.sh
+++ b/t/t6018-rev-list-glob.sh
@@ -5,6 +5,7 @@ test_description='rev-list/rev-parse --glob'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 commit () {
diff --git a/t/t6100-rev-list-in-order.sh b/t/t6100-rev-list-in-order.sh
index e934bc239c5..88ed7bd75a7 100755
--- a/t/t6100-rev-list-in-order.sh
+++ b/t/t6100-rev-list-in-order.sh
@@ -2,6 +2,7 @@
 
 test_description='rev-list testing in-commit-order'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a commit history with trees, blobs' '
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index fea41b3c360..7c5b847f584 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -8,6 +8,7 @@ test_description='git svn basic tests'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 prepare_utf8_locale
diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
index 8b5681dd68a..d043e80fc34 100755
--- a/t/t9101-git-svn-props.sh
+++ b/t/t9101-git-svn-props.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn property tests'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 mkdir import
diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
index c7d8e0bf00f..5cf2ef4b8b0 100755
--- a/t/t9104-git-svn-follow-parent.sh
+++ b/t/t9104-git-svn-follow-parent.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn fetching'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
index aec45bca3b7..3cab0b9720a 100755
--- a/t/t9106-git-svn-commit-diff-clobber.sh
+++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -2,6 +2,8 @@
 #
 # Copyright (c) 2006 Eric Wong
 test_description='git svn commit-diff clobber'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh
index 743fbe1fe46..419f055721d 100755
--- a/t/t9115-git-svn-dcommit-funky-renames.sh
+++ b/t/t9115-git-svn-dcommit-funky-renames.sh
@@ -5,6 +5,7 @@
 
 test_description='git svn dcommit can commit renames of files with ugly names'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'load repository with strange names' '
diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh
index 0a9f1ef366d..34f6c80dea3 100755
--- a/t/t9116-git-svn-log.sh
+++ b/t/t9116-git-svn-log.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn log tests'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup repository and import' '
diff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh
index 9e8fe38e7ef..527ba3d2932 100755
--- a/t/t9122-git-svn-author.sh
+++ b/t/t9122-git-svn-author.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='git svn authorship'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup svn repository' '
diff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh
index 2e4789d061f..90b1b30dde5 100755
--- a/t/t9127-git-svn-partial-rebuild.sh
+++ b/t/t9127-git-svn-partial-rebuild.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn partial-rebuild tests'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize svnrepo' '
diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh
index 01e1e8a8f76..185248a4cd7 100755
--- a/t/t9129-git-svn-i18n-commitencoding.sh
+++ b/t/t9129-git-svn-i18n-commitencoding.sh
@@ -4,6 +4,7 @@
 
 test_description='git svn honors i18n.commitEncoding in config'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 compare_git_head_with () {
diff --git a/t/t9132-git-svn-broken-symlink.sh b/t/t9132-git-svn-broken-symlink.sh
index aeceffaf7b0..4d8d0584b79 100755
--- a/t/t9132-git-svn-broken-symlink.sh
+++ b/t/t9132-git-svn-broken-symlink.sh
@@ -2,6 +2,7 @@
 
 test_description='test that git handles an svn repository with empty symlinks'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 test_expect_success 'load svn dumpfile' '
 	svnadmin load "$rawsvnrepo" <<EOF
diff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh
index 22d80b0be2b..b7f756b2b7f 100755
--- a/t/t9139-git-svn-non-utf8-commitencoding.sh
+++ b/t/t9139-git-svn-non-utf8-commitencoding.sh
@@ -4,6 +4,7 @@
 
 test_description='git svn refuses to dcommit non-UTF8 messages'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 # ISO-2022-JP can pass for valid UTF-8, so skipping that in this test
diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh
index 80cb55fee70..79c26ed69c1 100755
--- a/t/t9146-git-svn-empty-dirs.sh
+++ b/t/t9146-git-svn-empty-dirs.sh
@@ -3,6 +3,8 @@
 # Copyright (c) 2009 Eric Wong
 
 test_description='git svn creates empty directories'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9148-git-svn-propset.sh b/t/t9148-git-svn-propset.sh
index aebb28995e5..6cc76a07b39 100755
--- a/t/t9148-git-svn-propset.sh
+++ b/t/t9148-git-svn-propset.sh
@@ -5,6 +5,7 @@
 
 test_description='git svn propset tests'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup propset via import' '
diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh
index c93a5beab25..85221d439bd 100755
--- a/t/t9151-svn-mergeinfo.sh
+++ b/t/t9151-svn-mergeinfo.sh
@@ -5,6 +5,7 @@
 
 test_description='git-svn svn mergeinfo properties'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'load svn dump' "
diff --git a/t/t9160-git-svn-preserve-empty-dirs.sh b/t/t9160-git-svn-preserve-empty-dirs.sh
index 36c6b1a12ff..9cf7a1427ab 100755
--- a/t/t9160-git-svn-preserve-empty-dirs.sh
+++ b/t/t9160-git-svn-preserve-empty-dirs.sh
@@ -9,6 +9,7 @@ This test uses git to clone a Subversion repository that contains empty
 directories, and checks that corresponding directories are created in the
 local Git repository with placeholder files.'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 GIT_REPO=git-svn-repo
diff --git a/t/t9162-git-svn-dcommit-interactive.sh b/t/t9162-git-svn-dcommit-interactive.sh
index e38d9fa37b5..e2aa8ed88a9 100755
--- a/t/t9162-git-svn-dcommit-interactive.sh
+++ b/t/t9162-git-svn-dcommit-interactive.sh
@@ -3,6 +3,8 @@
 # Copyright (c) 2011 Frédéric Heitzmann
 
 test_description='git svn dcommit --interactive series'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9164-git-svn-dcommit-concurrent.sh b/t/t9164-git-svn-dcommit-concurrent.sh
index 8466269bf50..1465156072e 100755
--- a/t/t9164-git-svn-dcommit-concurrent.sh
+++ b/t/t9164-git-svn-dcommit-concurrent.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='concurrent git svn dcommit'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 
diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh
index 32814e75df5..c900231079c 100755
--- a/t/t9501-gitweb-standalone-http-status.sh
+++ b/t/t9501-gitweb-standalone-http-status.sh
@@ -13,6 +13,7 @@ code and message.'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./lib-gitweb.sh
 
 #
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 19/27] revisions API: have release_revisions() release "filter"
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (17 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 18/27] revisions API: have release_revisions() release "cmdline" Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 20/27] revisions API: have release_revisions() release "grep_filter" Ævar Arnfjörð Bjarmason
                       ` (8 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"filter" in the "struct rev_info". This in combination with a
preceding change to free "cmdline" means that we can mark another set
of tests as passing under "TEST_PASSES_SANITIZE_LEAK=true".

The "filter" member was added recently in ffaa137f646 (revision: put
object filter into struct rev_info, 2022-03-09), and this fixes leaks
intruded in the subsequent leak 7940941de1f (pack-objects: use
rev.filter when possible, 2022-03-09) and 105c6f14ad3 (bundle: parse
filter capability, 2022-03-09).

The "builtin/pack-objects.c" leak in 7940941de1f was effectively with
us already, but the variable was referred to by a "static" file-scoped
variable. The "bundle.c " leak in 105c6f14ad3 was newly introduced
with the new "filter" feature for bundles.

The "t5600-clone-fail-cleanup.sh" change here to add
"TEST_PASSES_SANITIZE_LEAK=true" is one of the cases where
run-command.c in not carrying the abort() exit code upwards would have
had that test passing before, but now it *actually* passes[1]. We
should fix the lack of 1=1 mapping of SANITIZE=leak testing to actual
leaks some other time, but it's an existing edge case, let's just mark
the really-passing test as passing for now.

1. https://lore.kernel.org/git/220303.86fsnz5o9w.gmgdl@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c                         | 1 +
 t/t1060-object-corruption.sh       | 1 +
 t/t2015-checkout-unborn.sh         | 1 +
 t/t4207-log-decoration-colors.sh   | 1 +
 t/t5301-sliding-window.sh          | 2 ++
 t/t5313-pack-bounds-checks.sh      | 2 ++
 t/t5316-pack-delta-depth.sh        | 2 ++
 t/t5320-delta-islands.sh           | 2 ++
 t/t5322-pack-objects-sparse.sh     | 1 +
 t/t5506-remote-groups.sh           | 1 +
 t/t5513-fetch-track.sh             | 1 +
 t/t5532-fetch-proxy.sh             | 2 ++
 t/t5600-clone-fail-cleanup.sh      | 1 +
 t/t5900-repo-selection.sh          | 2 ++
 t/t6101-rev-parse-parents.sh       | 1 +
 t/t6114-keep-packs.sh              | 2 ++
 t/t7702-repack-cyclic-alternate.sh | 2 ++
 t/t9127-git-svn-partial-rebuild.sh | 1 -
 18 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/revision.c b/revision.c
index d864b9e2064..55f2f4291f2 100644
--- a/revision.c
+++ b/revision.c
@@ -2955,6 +2955,7 @@ void release_revisions(struct rev_info *revs)
 	release_revisions_commit_list(revs);
 	object_array_clear(&revs->pending);
 	release_revisions_cmdline(&revs->cmdline);
+	list_objects_filter_release(&revs->filter);
 	release_revisions_mailmap(revs->mailmap);
 }
 
diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh
index e8a58b15897..5b8e47e346c 100755
--- a/t/t1060-object-corruption.sh
+++ b/t/t1060-object-corruption.sh
@@ -2,6 +2,7 @@
 
 test_description='see how we handle various forms of corruption'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # convert "1234abcd" to ".git/objects/12/34abcd"
diff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh
index a9721215fab..9425aae6395 100755
--- a/t/t2015-checkout-unborn.sh
+++ b/t/t2015-checkout-unborn.sh
@@ -4,6 +4,7 @@ test_description='checkout from unborn branch'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh
index b8709424981..36ac6aff1e4 100755
--- a/t/t4207-log-decoration-colors.sh
+++ b/t/t4207-log-decoration-colors.sh
@@ -8,6 +8,7 @@ test_description='Test for "git log --decorate" colors'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t5301-sliding-window.sh b/t/t5301-sliding-window.sh
index 76f9798ab95..3ccaaeb3977 100755
--- a/t/t5301-sliding-window.sh
+++ b/t/t5301-sliding-window.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='mmap sliding window tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success \
diff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh
index 535313e4dc8..cc4cfaa9d37 100755
--- a/t/t5313-pack-bounds-checks.sh
+++ b/t/t5313-pack-bounds-checks.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='bounds-checking of access to mmapped on-disk file formats'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 clear_base () {
diff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh
index e9045009a11..eb4ef3dda4d 100755
--- a/t/t5316-pack-delta-depth.sh
+++ b/t/t5316-pack-delta-depth.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='pack-objects breaks long cross-pack delta chains'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # This mirrors a repeated push setup:
diff --git a/t/t5320-delta-islands.sh b/t/t5320-delta-islands.sh
index fea92a5777f..124d47603df 100755
--- a/t/t5320-delta-islands.sh
+++ b/t/t5320-delta-islands.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='exercise delta islands'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # returns true iff $1 is a delta based on $2
diff --git a/t/t5322-pack-objects-sparse.sh b/t/t5322-pack-objects-sparse.sh
index d39958c066d..770695c9278 100755
--- a/t/t5322-pack-objects-sparse.sh
+++ b/t/t5322-pack-objects-sparse.sh
@@ -4,6 +4,7 @@ test_description='pack-objects object selection using sparse algorithm'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup repo' '
diff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh
index 8f150c0793e..5bac03ede81 100755
--- a/t/t5506-remote-groups.sh
+++ b/t/t5506-remote-groups.sh
@@ -4,6 +4,7 @@ test_description='git remote group handling'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 mark() {
diff --git a/t/t5513-fetch-track.sh b/t/t5513-fetch-track.sh
index 65d1e05bd62..c46c4dbaefc 100755
--- a/t/t5513-fetch-track.sh
+++ b/t/t5513-fetch-track.sh
@@ -2,6 +2,7 @@
 
 test_description='fetch follows remote-tracking branches correctly'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh
index 9c2798603b4..d664912799b 100755
--- a/t/t5532-fetch-proxy.sh
+++ b/t/t5532-fetch-proxy.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='fetching via git:// using core.gitproxy'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup remote repo' '
diff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh
index 34b3df40275..c814afa5656 100755
--- a/t/t5600-clone-fail-cleanup.sh
+++ b/t/t5600-clone-fail-cleanup.sh
@@ -13,6 +13,7 @@ Unless the directory already exists, in which case we clean up only what we
 wrote.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 corrupt_repo () {
diff --git a/t/t5900-repo-selection.sh b/t/t5900-repo-selection.sh
index 14e59c5b3e4..a84faac242d 100755
--- a/t/t5900-repo-selection.sh
+++ b/t/t5900-repo-selection.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='selecting remote repo in ambiguous cases'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 reset() {
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
index c571fa51797..a3a41c7a3e4 100755
--- a/t/t6101-rev-parse-parents.sh
+++ b/t/t6101-rev-parse-parents.sh
@@ -8,6 +8,7 @@ test_description='Test git rev-parse with different parent options'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_cmp_rev_output () {
diff --git a/t/t6114-keep-packs.sh b/t/t6114-keep-packs.sh
index 9239d8aa46d..44246f8a63e 100755
--- a/t/t6114-keep-packs.sh
+++ b/t/t6114-keep-packs.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='rev-list with .keep packs'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t7702-repack-cyclic-alternate.sh b/t/t7702-repack-cyclic-alternate.sh
index 93b74867ac8..f3cdb98eec2 100755
--- a/t/t7702-repack-cyclic-alternate.sh
+++ b/t/t7702-repack-cyclic-alternate.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='repack involving cyclic alternate'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh
index 90b1b30dde5..97f495bd49b 100755
--- a/t/t9127-git-svn-partial-rebuild.sh
+++ b/t/t9127-git-svn-partial-rebuild.sh
@@ -5,7 +5,6 @@
 
 test_description='git svn partial-rebuild tests'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize svnrepo' '
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 20/27] revisions API: have release_revisions() release "grep_filter"
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (18 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 19/27] revisions API: have release_revisions() release "filter" Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 21/27] revisions API: have release_revisions() release "prune_data" Ævar Arnfjörð Bjarmason
                       ` (7 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"grep_filter" in the "struct rev_info".This allows us to mark a test
as passing under "TEST_PASSES_SANITIZE_LEAK=true".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c               | 1 +
 t/t9151-svn-mergeinfo.sh | 1 -
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/revision.c b/revision.c
index 55f2f4291f2..da7e6356e68 100644
--- a/revision.c
+++ b/revision.c
@@ -2957,6 +2957,7 @@ void release_revisions(struct rev_info *revs)
 	release_revisions_cmdline(&revs->cmdline);
 	list_objects_filter_release(&revs->filter);
 	release_revisions_mailmap(revs->mailmap);
+	free_grep_patterns(&revs->grep_filter);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh
index 85221d439bd..c93a5beab25 100755
--- a/t/t9151-svn-mergeinfo.sh
+++ b/t/t9151-svn-mergeinfo.sh
@@ -5,7 +5,6 @@
 
 test_description='git-svn svn mergeinfo properties'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'load svn dump' "
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 21/27] revisions API: have release_revisions() release "prune_data"
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (19 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 20/27] revisions API: have release_revisions() release "grep_filter" Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 22/27] revisions API: clear "boundary_commits" in release_revisions() Ævar Arnfjörð Bjarmason
                       ` (6 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"prune_data" in the "struct rev_info". This means that any code that
calls "release_revisions()" already can get rid of adjacent calls to
clear_pathspec().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 add-interactive.c | 2 --
 builtin/add.c     | 1 -
 builtin/stash.c   | 2 --
 diff-lib.c        | 1 -
 revision.c        | 1 +
 wt-status.c       | 2 --
 6 files changed, 1 insertion(+), 8 deletions(-)

diff --git a/add-interactive.c b/add-interactive.c
index 54cdfc82017..6047e8f6489 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -568,8 +568,6 @@ static int get_modified_files(struct repository *r,
 			run_diff_files(&rev, 0);
 		}
 
-		if (ps)
-			clear_pathspec(&rev.prune_data);
 		release_revisions(&rev);
 	}
 	hashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);
diff --git a/builtin/add.c b/builtin/add.c
index 115a26ea633..fc729e14c17 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -142,7 +142,6 @@ int add_files_to_cache(const char *prefix,
 	rev.diffopt.flags.override_submodule_config = 1;
 	rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
 	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 	return !!data.add_errors;
 }
diff --git a/builtin/stash.c b/builtin/stash.c
index a6ee030d4bd..5237d0d061a 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1059,7 +1059,6 @@ static int check_changes_tracked_files(const struct pathspec *ps)
 	}
 
 done:
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 	return ret;
 }
@@ -1271,7 +1270,6 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
 
 done:
 	discard_index(&istate);
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 	strbuf_release(&diff_output);
 	remove_path(stash_index_path.buf);
diff --git a/diff-lib.c b/diff-lib.c
index 0f16281253f..298265e5b54 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -641,7 +641,6 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
 
 	if (diff_cache(&revs, tree_oid, NULL, 1))
 		exit(128);
-	clear_pathspec(&revs.prune_data);
 	release_revisions(&revs);
 	return 0;
 }
diff --git a/revision.c b/revision.c
index da7e6356e68..6fd582819fe 100644
--- a/revision.c
+++ b/revision.c
@@ -2956,6 +2956,7 @@ void release_revisions(struct rev_info *revs)
 	object_array_clear(&revs->pending);
 	release_revisions_cmdline(&revs->cmdline);
 	list_objects_filter_release(&revs->filter);
+	clear_pathspec(&revs->prune_data);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
 }
diff --git a/wt-status.c b/wt-status.c
index a14fad1e03a..61e0c1022f5 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -616,7 +616,6 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
 	rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_files(&rev, 0);
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 }
 
@@ -664,7 +663,6 @@ static void wt_status_collect_changes_index(struct wt_status *s)
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_index(&rev, 1);
 	release_revisions(&rev);
-	clear_pathspec(&rev.prune_data);
 }
 
 static int add_file_to_list(const struct object_id *oid,
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 22/27] revisions API: clear "boundary_commits" in release_revisions()
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (20 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 21/27] revisions API: have release_revisions() release "prune_data" Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 23/27] revisions API: release "reflog_info" in release revisions() Ævar Arnfjörð Bjarmason
                       ` (5 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Clear the "boundary_commits" object_array in release_revisions(). This
makes a few more tests pass under SANITIZE=leak, including
"t/t4126-apply-empty.sh" which started failed as an UNLEAK() in
cmd_format_patch() was removed in a preceding commit.

This also re-marks the various tests relying on "git format-patch" as
passing under "SANITIZE=leak", in the preceding "revisions API users:
use release_revisions() in builtin/log.c" commit those were marked as
failing as we removed the UNLEAK(rev) from cmd_format_patch() in
"builtin/log.c".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c                           | 1 +
 t/t4021-format-patch-numbered.sh     | 1 +
 t/t4028-format-patch-mime-headers.sh | 2 ++
 t/t4036-format-patch-signer-mime.sh  | 1 +
 t/t4122-apply-symlink-inside.sh      | 1 +
 t/t4126-apply-empty.sh               | 1 +
 t/t6110-rev-list-sparse.sh           | 1 +
 t/t9001-send-email.sh                | 1 +
 t/t9116-git-svn-log.sh               | 1 -
 9 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/revision.c b/revision.c
index 6fd582819fe..53b3ab6dc2d 100644
--- a/revision.c
+++ b/revision.c
@@ -2954,6 +2954,7 @@ void release_revisions(struct rev_info *revs)
 {
 	release_revisions_commit_list(revs);
 	object_array_clear(&revs->pending);
+	object_array_clear(&revs->boundary_commits);
 	release_revisions_cmdline(&revs->cmdline);
 	list_objects_filter_release(&revs->filter);
 	clear_pathspec(&revs->prune_data);
diff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh
index 9be65fd4440..1219aa226dc 100755
--- a/t/t4021-format-patch-numbered.sh
+++ b/t/t4021-format-patch-numbered.sh
@@ -5,6 +5,7 @@
 
 test_description='Format-patch numbering options'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4028-format-patch-mime-headers.sh b/t/t4028-format-patch-mime-headers.sh
index 204ba673cb5..60cb819c42e 100755
--- a/t/t4028-format-patch-mime-headers.sh
+++ b/t/t4028-format-patch-mime-headers.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='format-patch mime headers and extra headers do not conflict'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create commit with utf-8 body' '
diff --git a/t/t4036-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh
index 98d9713d8b2..48655bcc789 100755
--- a/t/t4036-format-patch-signer-mime.sh
+++ b/t/t4036-format-patch-signer-mime.sh
@@ -2,6 +2,7 @@
 
 test_description='format-patch -s should force MIME encoding as needed'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh
index aa52de401b9..96965373036 100755
--- a/t/t4122-apply-symlink-inside.sh
+++ b/t/t4122-apply-symlink-inside.sh
@@ -4,6 +4,7 @@ test_description='apply to deeper directory without getting fooled with symlink'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh
index 66a7ba8ab8f..ece9fae207d 100755
--- a/t/t4126-apply-empty.sh
+++ b/t/t4126-apply-empty.sh
@@ -2,6 +2,7 @@
 
 test_description='apply empty'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t6110-rev-list-sparse.sh b/t/t6110-rev-list-sparse.sh
index 13c1da53528..ddefc7f24ee 100755
--- a/t/t6110-rev-list-sparse.sh
+++ b/t/t6110-rev-list-sparse.sh
@@ -4,6 +4,7 @@ test_description='operations that cull histories in unusual ways'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 84d0f40d76a..dfa6b20f7a6 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -4,6 +4,7 @@ test_description='git send-email'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # May be altered later in the test
diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh
index 34f6c80dea3..d74d7b2de68 100755
--- a/t/t9116-git-svn-log.sh
+++ b/t/t9116-git-svn-log.sh
@@ -5,7 +5,6 @@
 
 test_description='git svn log tests'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup repository and import' '
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 23/27] revisions API: release "reflog_info" in release revisions()
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (21 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 22/27] revisions API: clear "boundary_commits" in release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release() Ævar Arnfjörð Bjarmason
                       ` (4 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Add a missing reflog_walk_info_release() to "reflog-walk.c" and use it
in release_revisions().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 reflog-walk.c            | 24 +++++++++++++++++++++++-
 reflog-walk.h            |  1 +
 revision.c               |  1 +
 t/t0100-previous.sh      |  1 +
 t/t1401-symbolic-ref.sh  |  2 ++
 t/t1411-reflog-show.sh   |  1 +
 t/t1412-reflog-loop.sh   |  2 ++
 t/t1415-worktree-refs.sh |  1 +
 8 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/reflog-walk.c b/reflog-walk.c
index 8ac4b284b6b..7aa6595a51f 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -8,7 +8,7 @@
 
 struct complete_reflogs {
 	char *ref;
-	const char *short_ref;
+	char *short_ref;
 	struct reflog_info {
 		struct object_id ooid, noid;
 		char *email;
@@ -51,9 +51,16 @@ static void free_complete_reflog(struct complete_reflogs *array)
 	}
 	free(array->items);
 	free(array->ref);
+	free(array->short_ref);
 	free(array);
 }
 
+static void complete_reflogs_clear(void *util, const char *str)
+{
+	struct complete_reflogs *array = util;
+	free_complete_reflog(array);
+}
+
 static struct complete_reflogs *read_complete_reflog(const char *ref)
 {
 	struct complete_reflogs *reflogs =
@@ -116,6 +123,21 @@ void init_reflog_walk(struct reflog_walk_info **info)
 	(*info)->complete_reflogs.strdup_strings = 1;
 }
 
+void reflog_walk_info_release(struct reflog_walk_info *info)
+{
+	size_t i;
+
+	if (!info)
+		return;
+
+	for (i = 0; i < info->nr; i++)
+		free(info->logs[i]);
+	string_list_clear_func(&info->complete_reflogs,
+			       complete_reflogs_clear);
+	free(info->logs);
+	free(info);
+}
+
 int add_reflog_for_walk(struct reflog_walk_info *info,
 		struct commit *commit, const char *name)
 {
diff --git a/reflog-walk.h b/reflog-walk.h
index e9e00ffd479..8076f10d9fb 100644
--- a/reflog-walk.h
+++ b/reflog-walk.h
@@ -8,6 +8,7 @@ struct reflog_walk_info;
 struct date_mode;
 
 void init_reflog_walk(struct reflog_walk_info **info);
+void reflog_walk_info_release(struct reflog_walk_info *info);
 int add_reflog_for_walk(struct reflog_walk_info *info,
 			struct commit *commit, const char *name);
 void show_reflog_message(struct reflog_walk_info *info, int,
diff --git a/revision.c b/revision.c
index 53b3ab6dc2d..380e6d9fe43 100644
--- a/revision.c
+++ b/revision.c
@@ -2960,6 +2960,7 @@ void release_revisions(struct rev_info *revs)
 	clear_pathspec(&revs->prune_data);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
+	reflog_walk_info_release(revs->reflog_info);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh
index 69beb59f627..a16cc3d2983 100755
--- a/t/t0100-previous.sh
+++ b/t/t0100-previous.sh
@@ -5,6 +5,7 @@ test_description='previous branch syntax @{-n}'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'branch -d @{-1}' '
diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh
index 132a1b885ac..9fb0b90f252 100755
--- a/t/t1401-symbolic-ref.sh
+++ b/t/t1401-symbolic-ref.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='basic symbolic-ref tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # If the tests munging HEAD fail, they can break detection of
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
index 0bb319b944a..3770ceffafd 100755
--- a/t/t1411-reflog-show.sh
+++ b/t/t1411-reflog-show.sh
@@ -4,6 +4,7 @@ test_description='Test reflog display routines'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t1412-reflog-loop.sh b/t/t1412-reflog-loop.sh
index 977603f7f1c..ff30874f940 100755
--- a/t/t1412-reflog-loop.sh
+++ b/t/t1412-reflog-loop.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='reflog walk shows repeated commits again'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup commits' '
diff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh
index a3e6ea08088..3b531842dd4 100755
--- a/t/t1415-worktree-refs.sh
+++ b/t/t1415-worktree-refs.sh
@@ -2,6 +2,7 @@
 
 test_description='per-worktree refs'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release()
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (22 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 23/27] revisions API: release "reflog_info" in release revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 25/27] revisions API: have release_revisions() release "date_mode" Ævar Arnfjörð Bjarmason
                       ` (3 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Call diff_free() on the "pruning" member of "struct rev_info".  Doing
so makes several tests pass under SANITIZE=leak.

This was also the last missing piece that allows us to remove the
UNLEAK() in "cmd_diff" and "cmd_diff_index", which allows us to use
those commands as a canary for general leaks in the revisions API. See
[1] for further rationale, and 886e1084d78 (builtin/: add UNLEAKs,
2017-10-01) for the commit that added the UNLEAK() there.

1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/diff-index.c                | 1 -
 builtin/diff.c                      | 1 -
 revision.c                          | 1 +
 t/t1001-read-tree-m-2way.sh         | 1 +
 t/t1002-read-tree-m-u-2way.sh       | 1 +
 t/t2200-add-update.sh               | 1 +
 t/t4039-diff-assume-unchanged.sh    | 1 +
 t/t4206-log-follow-harder-copies.sh | 2 ++
 t/t6131-pathspec-icase.sh           | 2 ++
 9 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 3a83183c312..7d158af6b6d 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -70,7 +70,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
 		return -1;
 	}
 	result = run_diff_index(&rev, option);
-	UNLEAK(rev);
 	result = diff_result_code(&rev.diffopt, result);
 	release_revisions(&rev);
 	return result;
diff --git a/builtin/diff.c b/builtin/diff.c
index dd48336da56..f539132ac68 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -594,7 +594,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
 	result = diff_result_code(&rev.diffopt, result);
 	if (1 < rev.diffopt.skip_stat_unmatch)
 		refresh_index_quietly();
-	UNLEAK(rev);
 	release_revisions(&rev);
 	UNLEAK(ent);
 	UNLEAK(blob);
diff --git a/revision.c b/revision.c
index 380e6d9fe43..9ac06e4d285 100644
--- a/revision.c
+++ b/revision.c
@@ -2960,6 +2960,7 @@ void release_revisions(struct rev_info *revs)
 	clear_pathspec(&revs->prune_data);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
+	diff_free(&revs->pruning);
 	reflog_walk_info_release(revs->reflog_info);
 }
 
diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
index 0710b1fb1e9..516a6112fdc 100755
--- a/t/t1001-read-tree-m-2way.sh
+++ b/t/t1001-read-tree-m-2way.sh
@@ -21,6 +21,7 @@ In the test, these paths are used:
 	yomin   - not in H or M
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 
diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh
index 46cbd5514a6..bd5313caec9 100755
--- a/t/t1002-read-tree-m-u-2way.sh
+++ b/t/t1002-read-tree-m-u-2way.sh
@@ -9,6 +9,7 @@ This is identical to t1001, but uses -u to update the work tree as well.
 
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index 0c38f8e3569..be394f1131a 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -14,6 +14,7 @@ only the updates to dir/sub.
 Also tested are "git add -u" without limiting, and "git add -u"
 without contents changes, and other conditions'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh
index 0eb0314a8b3..78090e6852d 100755
--- a/t/t4039-diff-assume-unchanged.sh
+++ b/t/t4039-diff-assume-unchanged.sh
@@ -2,6 +2,7 @@
 
 test_description='diff with assume-unchanged entries'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # external diff has been tested in t4020-diff-external.sh
diff --git a/t/t4206-log-follow-harder-copies.sh b/t/t4206-log-follow-harder-copies.sh
index 4871a5dc92f..33ecf82c7f9 100755
--- a/t/t4206-log-follow-harder-copies.sh
+++ b/t/t4206-log-follow-harder-copies.sh
@@ -6,6 +6,8 @@
 test_description='Test --follow should always find copies hard in git log.
 
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh
 
diff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh
index 39fc3f6769b..770cce026cb 100755
--- a/t/t6131-pathspec-icase.sh
+++ b/t/t6131-pathspec-icase.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test case insensitive pathspec limiting'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 if test_have_prereq CASE_INSENSITIVE_FS
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 25/27] revisions API: have release_revisions() release "date_mode"
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (23 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release() Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 26/27] revisions API: have release_revisions() release "topo_walk_info" Ævar Arnfjörð Bjarmason
                       ` (2 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"date_mode" in the "struct ref_info".

This uses the date_mode_release() function added in 974c919d36d (date
API: add and use a date_mode_release(), 2022-02-16). As that commit
notes "t7004-tag.sh" tests for the leaks that are being fixed
here. That test now fails "only" 44 tests, instead of the 46 it failed
before this change.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/revision.c b/revision.c
index 9ac06e4d285..49668012ed1 100644
--- a/revision.c
+++ b/revision.c
@@ -2958,6 +2958,7 @@ void release_revisions(struct rev_info *revs)
 	release_revisions_cmdline(&revs->cmdline);
 	list_objects_filter_release(&revs->filter);
 	clear_pathspec(&revs->prune_data);
+	date_mode_release(&revs->date_mode);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
 	diff_free(&revs->pruning);
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 26/27] revisions API: have release_revisions() release "topo_walk_info"
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (24 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 25/27] revisions API: have release_revisions() release "date_mode" Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-25 17:18     ` [PATCH v3 27/27] revisions API: add a TODO for diff_free(&revs->diffopt) Ævar Arnfjörð Bjarmason
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Refactor the existing reset_topo_walk() into a thin wrapper for a
release_revisions_topo_walk_info() + resetting the member to "NULL",
and call release_revisions_topo_walk_info() from release_revisions().

This fixes memory leaks that have been with us ever since
"topo_walk_info" was added to revision.[ch] in
f0d9cc4196a (revision.c: begin refactoring --topo-order logic,
2018-11-01).

Due to various other leaks this makes no tests pass in their entirety,
but e.g. before this running this on git.git:

    ./git -P log --pretty=tformat:"%P   %H | %s" --parents --full-history --topo-order -3 -- README.md

Would report under SANITIZE=leak:

    SUMMARY: LeakSanitizer: 531064 byte(s) leaked in 6 allocation(s).

Now we'll free all of that memory.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/revision.c b/revision.c
index 49668012ed1..cff1c2c6021 100644
--- a/revision.c
+++ b/revision.c
@@ -2950,6 +2950,8 @@ static void release_revisions_mailmap(struct string_list *mailmap)
 	free(mailmap);
 }
 
+static void release_revisions_topo_walk_info(struct topo_walk_info *info);
+
 void release_revisions(struct rev_info *revs)
 {
 	release_revisions_commit_list(revs);
@@ -2963,6 +2965,7 @@ void release_revisions(struct rev_info *revs)
 	free_grep_patterns(&revs->grep_filter);
 	diff_free(&revs->pruning);
 	reflog_walk_info_release(revs->reflog_info);
+	release_revisions_topo_walk_info(revs->topo_walk_info);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
@@ -3475,17 +3478,22 @@ static void compute_indegrees_to_depth(struct rev_info *revs,
 		indegree_walk_step(revs);
 }
 
-static void reset_topo_walk(struct rev_info *revs)
+static void release_revisions_topo_walk_info(struct topo_walk_info *info)
 {
-	struct topo_walk_info *info = revs->topo_walk_info;
-
+	if (!info)
+		return;
 	clear_prio_queue(&info->explore_queue);
 	clear_prio_queue(&info->indegree_queue);
 	clear_prio_queue(&info->topo_queue);
 	clear_indegree_slab(&info->indegree);
 	clear_author_date_slab(&info->author_date);
+	free(info);
+}
 
-	FREE_AND_NULL(revs->topo_walk_info);
+static void reset_topo_walk(struct rev_info *revs)
+{
+	release_revisions_topo_walk_info(revs->topo_walk_info);
+	revs->topo_walk_info = NULL;
 }
 
 static void init_topo_walk(struct rev_info *revs)
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v3 27/27] revisions API: add a TODO for diff_free(&revs->diffopt)
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (25 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 26/27] revisions API: have release_revisions() release "topo_walk_info" Ævar Arnfjörð Bjarmason
@ 2022-03-25 17:18     ` Ævar Arnfjörð Bjarmason
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-25 17:18 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Add a TODO comment indicating that we should release "diffopt" in
release_revisions(). In a preceding commit we started releasing the
"pruning" member of the same type, but handling "diffopt" will require
us to untangle the "no_free" conditions I added in e900d494dcf (diff:
add an API for deferred freeing, 2021-02-11).

Let's leave a TODO comment to that effect, and so that we don't forget
refactor code that was changed to use release_revisions() in earlier
commits to stop using the "diffopt" member after a call to
release_revisions(). This works currently, but would become a logic
error as soon as we started freeing "diffopt". Doing that change now
doesn't harm anything, and future-proofs us against a later change to
release_revisions().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 diff-lib.c  | 4 +++-
 revision.c  | 1 +
 wt-status.c | 6 ++++--
 3 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/diff-lib.c b/diff-lib.c
index 298265e5b54..7eb66a417aa 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -651,6 +651,7 @@ int index_differs_from(struct repository *r,
 {
 	struct rev_info rev;
 	struct setup_revision_opt opt;
+	unsigned has_changes;
 
 	repo_init_revisions(r, &rev, NULL);
 	memset(&opt, 0, sizeof(opt));
@@ -662,8 +663,9 @@ int index_differs_from(struct repository *r,
 		diff_flags_or(&rev.diffopt.flags, flags);
 	rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
 	run_diff_index(&rev, 1);
+	has_changes = rev.diffopt.flags.has_changes;
 	release_revisions(&rev);
-	return (rev.diffopt.flags.has_changes != 0);
+	return (has_changes != 0);
 }
 
 static struct strbuf *idiff_prefix_cb(struct diff_options *opt, void *data)
diff --git a/revision.c b/revision.c
index cff1c2c6021..fa7036e4d5a 100644
--- a/revision.c
+++ b/revision.c
@@ -2963,6 +2963,7 @@ void release_revisions(struct rev_info *revs)
 	date_mode_release(&revs->date_mode);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
+	/* TODO (need to handle "no_free"): diff_free(&revs->diffopt) */
 	diff_free(&revs->pruning);
 	reflog_walk_info_release(revs->reflog_info);
 	release_revisions_topo_walk_info(revs->topo_walk_info);
diff --git a/wt-status.c b/wt-status.c
index 61e0c1022f5..102d904adcb 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -2545,8 +2545,9 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)
 	rev_info.diffopt.flags.quick = 1;
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_files(&rev_info, 0);
+	result = diff_result_code(&rev_info.diffopt, result);
 	release_revisions(&rev_info);
-	return diff_result_code(&rev_info.diffopt, result);
+	return result;
 }
 
 /**
@@ -2578,8 +2579,9 @@ int has_uncommitted_changes(struct repository *r,
 
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_index(&rev_info, 1);
+	result = diff_result_code(&rev_info.diffopt, result);
 	release_revisions(&rev_info);
-	return diff_result_code(&rev_info.diffopt, result);
+	return result;
 }
 
 /**
-- 
2.35.1.1509.ge4eeb5bd39e


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* Re: [PATCH v3 08/27] revisions API users: add "goto cleanup" for "rev_info" early exit
  2022-03-25 17:18     ` [PATCH v3 08/27] revisions API users: add "goto cleanup" for "rev_info" early exit Ævar Arnfjörð Bjarmason
@ 2022-03-25 20:30       ` Junio C Hamano
  2022-03-26  0:37         ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 252+ messages in thread
From: Junio C Hamano @ 2022-03-25 20:30 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> It would be a lot cleaner to be able to initialize "struct rev_info"
> with "{ 0 }" here, or if a "REV_INFO_INIT" existed, we'll hopefully
> get around to making the initialization easier in the future (now it
> can't be done via a macro).

If "struct rev_info" can be initialized with "{ 0 }" here, i.e.

-	struct rev_info rev;
+	struct rev_info rev = { 0 };

to give us a valid solution, why wouldn't you be able to do

+#define REV_INFO_INIT { 0 }

elsewhere in a common *.h file, and then

-	struct rev_info rev;
+	struct rev_info rev = REV_INFO_INIT;

to make the fact that "rev" is initialized (and ready to be handed
to the releaser) even more explicit?  It's like arguing against
fixing a code like this:

	struct char *pointer;
	...
	if (condition)
		pointer = malloc(...);
	...
        /* pointer leaks */
 
by initializing

	struct char *pointer = NULL;
	...
	if (condition)
		pointer = malloc(...);
	...
        free(pointer);

because for some reason you are against the macro NULL but you are
willing to spell it out as "0" (without double-quotes)?

Puzzled.

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v3 08/27] revisions API users: add "goto cleanup" for "rev_info" early exit
  2022-03-25 20:30       ` Junio C Hamano
@ 2022-03-26  0:37         ` Ævar Arnfjörð Bjarmason
  2022-03-26  5:24           ` Junio C Hamano
  0 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-26  0:37 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson


On Fri, Mar 25 2022, Junio C Hamano wrote:

> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
>
>> It would be a lot cleaner to be able to initialize "struct rev_info"
>> with "{ 0 }" here, or if a "REV_INFO_INIT" existed, we'll hopefully
>> get around to making the initialization easier in the future (now it
>> can't be done via a macro).
>
> If "struct rev_info" can be initialized with "{ 0 }" here, i.e.
>
> -	struct rev_info rev;
> +	struct rev_info rev = { 0 };
>
> to give us a valid solution, why wouldn't you be able to do
>
> +#define REV_INFO_INIT { 0 }
>
> elsewhere in a common *.h file, and then
>
> -	struct rev_info rev;
> +	struct rev_info rev = REV_INFO_INIT;
>
> to make the fact that "rev" is initialized (and ready to be handed
> to the releaser) even more explicit?  It's like arguing against
> fixing a code like this:
>
> 	struct char *pointer;
> 	...
> 	if (condition)
> 		pointer = malloc(...);
> 	...
>         /* pointer leaks */
>  
> by initializing
>
> 	struct char *pointer = NULL;
> 	...
> 	if (condition)
> 		pointer = malloc(...);
> 	...
>         free(pointer);
>
> because for some reason you are against the macro NULL but you are
> willing to spell it out as "0" (without double-quotes)?

I was fine with having it be { 0 } and have "release" functions
everywhere assume that memset-ing structures to zero makes them safe to
pass to release(), but my reading of your v2 feedback was that you
didn't like making that assumption, so I changed it in the v3 so we
wouldn't assume that.

I then took that REV_INFO_INIT suggestion to mean that this series
could/should be predicated on some cross-codebase refactoring to change
all of the "struct rev_info" initialization, which would be a rather
large digression.

Because I don't see how it makes any sense to have a REV_INFO_INIT if it
doesn't actually give you an init'd "struct rev_info" that's ready for
use. I.e. if you still need to call repo_init_revisions() it's just a
misleading interface.

I also think the cosmetic issues of initialization here really aren't
important at all, fixing these leaks is. So whatever idioms you're OK
with us using here are fine by me, as long as I can be made to clearly
understand what you want for a re-roll :)

Very preferably something that doesn't require refactoring the entirety
of "struct rev_info" init across the codebase...

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v3 08/27] revisions API users: add "goto cleanup" for "rev_info" early exit
  2022-03-26  0:37         ` Ævar Arnfjörð Bjarmason
@ 2022-03-26  5:24           ` Junio C Hamano
  2022-03-28 17:55             ` Derrick Stolee
  0 siblings, 1 reply; 252+ messages in thread
From: Junio C Hamano @ 2022-03-26  5:24 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> Because I don't see how it makes any sense to have a REV_INFO_INIT if it
> doesn't actually give you an init'd "struct rev_info" that's ready for
> use. I.e. if you still need to call repo_init_revisions() it's just a
> misleading interface.

You can say

	struct something *foo = NULL;

	if (some condition)
		foo = alloc_and_init_foo();

	...

	free_and_destruct(foo);

and it is correct that "initialize with NULL" alone would not let
you use the thing as full fledged 'foo', but you can still safely
pass it to free_and_destruct() (or if "something" does not hold
external resources, it could just be free()).  A flow like this:

	struct rev_info revs = REV_INFO_INIT;

	if (!some condition)
		goto leave;
	init(&revs);
	... use revs ...
leave:
        release(&revs);

would exactly be the same thing.

In other words, you say "I do not see how it makes any sense" but to
me it looks more like what does not make sense is your argument
against what was suggested.


^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v3 08/27] revisions API users: add "goto cleanup" for "rev_info" early exit
  2022-03-26  5:24           ` Junio C Hamano
@ 2022-03-28 17:55             ` Derrick Stolee
  2022-03-28 18:55               ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 252+ messages in thread
From: Derrick Stolee @ 2022-03-28 17:55 UTC (permalink / raw)
  To: Junio C Hamano, Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, brian m . carlson

On 3/26/2022 1:24 AM, Junio C Hamano wrote:
> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
> 
>> Because I don't see how it makes any sense to have a REV_INFO_INIT if it
>> doesn't actually give you an init'd "struct rev_info" that's ready for
>> use. I.e. if you still need to call repo_init_revisions() it's just a
>> misleading interface.
> 
> You can say
> 
> 	struct something *foo = NULL;
> 
> 	if (some condition)
> 		foo = alloc_and_init_foo();
> 
> 	...
> 
> 	free_and_destruct(foo);
> 
> and it is correct that "initialize with NULL" alone would not let
> you use the thing as full fledged 'foo', but you can still safely
> pass it to free_and_destruct() (or if "something" does not hold
> external resources, it could just be free()).  A flow like this:
> 
> 	struct rev_info revs = REV_INFO_INIT;
> 
> 	if (!some condition)
> 		goto leave;
> 	init(&revs);
> 	... use revs ...
> leave:
>         release(&revs);
> 
> would exactly be the same thing.
> 
> In other words, you say "I do not see how it makes any sense" but to
> me it looks more like what does not make sense is your argument
> against what was suggested.

Ævar has stated in multiple threads that he prefers to not
initialize data so that static analysis tools can detect a
use-before-initialization of specific members.

However, now that we are intending to free rev_info structs,
we need them to be initialized with NULL pointers because
otherwise the release_revisions() method won't know which
portions were legitimately initialized and which ones were
not.

Maybe this NULL assignment happens as part of
repo_init_revisions(), but that also assumes that there is no
code path that would jump to a "leave" or "cleanup" tag before
running that initialization method (which is the broken case
that Junio mentions above).

Maybe there are tools that would identify that Junio's example
would be bad, but it is also likely that a compiler doesn't
catch that issue and tests don't cover that error condition.

It's my preference to initialize things to all-zeroes whenever
there is any chance of complexity, which is why this topic has
come to my attention on multiple threads.

Thanks,
-Stolee
 

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v3 08/27] revisions API users: add "goto cleanup" for "rev_info" early exit
  2022-03-28 17:55             ` Derrick Stolee
@ 2022-03-28 18:55               ` Ævar Arnfjörð Bjarmason
  2022-03-28 20:03                 ` Junio C Hamano
  0 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-28 18:55 UTC (permalink / raw)
  To: Derrick Stolee
  Cc: Junio C Hamano, git, Martin Ågren, Elijah Newren,
	brian m . carlson


On Mon, Mar 28 2022, Derrick Stolee wrote:

> On 3/26/2022 1:24 AM, Junio C Hamano wrote:
>> Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:
>> 
>>> Because I don't see how it makes any sense to have a REV_INFO_INIT if it
>>> doesn't actually give you an init'd "struct rev_info" that's ready for
>>> use. I.e. if you still need to call repo_init_revisions() it's just a
>>> misleading interface.
>> 
>> You can say
>> 
>> 	struct something *foo = NULL;
>> 
>> 	if (some condition)
>> 		foo = alloc_and_init_foo();
>> 
>> 	...
>> 
>> 	free_and_destruct(foo);
>> 
>> and it is correct that "initialize with NULL" alone would not let
>> you use the thing as full fledged 'foo', but you can still safely
>> pass it to free_and_destruct() (or if "something" does not hold
>> external resources, it could just be free()).  A flow like this:
>> 
>> 	struct rev_info revs = REV_INFO_INIT;
>> 
>> 	if (!some condition)
>> 		goto leave;
>> 	init(&revs);
>> 	... use revs ...
>> leave:
>>         release(&revs);
>> 
>> would exactly be the same thing.
>> 
>> In other words, you say "I do not see how it makes any sense" but to
>> me it looks more like what does not make sense is your argument
>> against what was suggested.
>

[re-arranging a bit]

> However, now that we are intending to free rev_info structs,
> we need them to be initialized with NULL pointers because
> otherwise the release_revisions() method won't know which
> portions were legitimately initialized and which ones were
> not.
>
> Maybe this NULL assignment happens as part of
> repo_init_revisions(), but that also assumes that there is no
> code path that would jump to a "leave" or "cleanup" tag before
> running that initialization method (which is the broken case
> that Junio mentions above).
>
> Maybe there are tools that would identify that Junio's example
> would be bad, but it is also likely that a compiler doesn't
> catch that issue and tests don't cover that error condition.
>
> It's my preference to initialize things to all-zeroes whenever
> there is any chance of complexity, which is why this topic has
> come to my attention on multiple threads.

Mine too, IOW I really don't like the version in my own v3 here, but
wanted to just use an initialization to { 0 } as in the v2.

But I read Junio's reply to
https://lore.kernel.org/git/220324.868rszmga6.gmgdl@evledraar.gmail.com/
as objecting to that general assumption, as we do e.g. in some of this
code:

    git grep 'struct.*=.*\{ 0 \}'

So the v3 rewrite of those was seeking a way around that which didn't
require implementing a full init-from-macro of REV_INFO_INIT.

Junio, would you be OK/prefer to basically have the v2 verison, with
just a dummy macro like this in revision.h?:

    #define REV_INFO_INIT { 0 }

Which we'd then do something more useful with down the line?

> Ævar has stated in multiple threads that he prefers to not
> initialize data so that static analysis tools can detect a
> use-before-initialization of specific members.

Just to be clear, I prefer not doing initialization in cases where the
compiler is guaranteed to help you by skipping them, i.e. init to NULL
within a function where say 2 branchesh both init the value, but if you
were to change that the init to NULL would hide the bug.

(And I'm sure it's not something I came up with, but got pointed out to
me repeatedly in review (by Jeff King or Junio?), until I got it...)

But skipping zero-ing out a struct like "struct rev_info" is not such a
case where the compiler can really help, and we often use that struct at
a far distance from where it was init'd. So I'd really like to see us
not do that as a habit, even if say valgrind can sometimes catch it (but
only if we have 100% test coverage!).

You're probably thinking more of the case of [1]. I consider that a
pretty clear case of running with scissors, I just figured that one was
narrow enough to be OK, but it's good to have [2] instead.

1. https://lore.kernel.org/git/patch-1.1-193534b0f07-20220325T121715Z-avarab@gmail.com/
2. https://lore.kernel.org/git/patch-v2-1.1-9951d92176e-20220328T154049Z-avarab@gmail.com/

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v3 08/27] revisions API users: add "goto cleanup" for "rev_info" early exit
  2022-03-28 18:55               ` Ævar Arnfjörð Bjarmason
@ 2022-03-28 20:03                 ` Junio C Hamano
  0 siblings, 0 replies; 252+ messages in thread
From: Junio C Hamano @ 2022-03-28 20:03 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: Derrick Stolee, git, Martin Ågren, Elijah Newren,
	brian m . carlson

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> Junio, would you be OK/prefer to basically have the v2 verison, with
> just a dummy macro like this in revision.h?:
>
>     #define REV_INFO_INIT { 0 }

FWIW, I do not see that as "dummy" at all.  We may further extend it
in the future, but it is what is minimally required to safely pass
revs to init and release, and what I have been saying since the
previous round of the review,
https://lore.kernel.org/git/xmqqwngji72a.fsf@gitster.g/



^ permalink raw reply	[flat|nested] 252+ messages in thread

* [PATCH v4 00/27] revision.[ch]: add and use release_revisions()
  2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
                       ` (26 preceding siblings ...)
  2022-03-25 17:18     ` [PATCH v3 27/27] revisions API: add a TODO for diff_free(&revs->diffopt) Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11     ` Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
                         ` (27 more replies)
  27 siblings, 28 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

This series reduces the memory leaks spotted by our tests by at least
half, see v3[1] for a more general overview.

Changes since v3:

 * Now based on top of ds/partial-bundle-more, since it's in "next"
   and there's a direct (but relatively minor) semantic conflict with
   it.

 * A new 8/27 moves some code in revisions.[ch] around, to make it
  more straightforward to add a REV_INFO_INIT in 9/27.

 * 9/27 and related is now the old { 0 }-using commit from v2, except
   that { 0 } is now REV_INFO_INIT. The goto patten to avoid assuming
   that we can call release_revisions() on a "struct rev_info" without
   having gone through repo_init_revisions() is gone.

 * A minor change in diff context from rebasing on "master".

1. https://lore.kernel.org/git/cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com/

Ævar Arnfjörð Bjarmason (27):
  t/helper/test-fast-rebase.c: don't leak "struct strbuf"
  blame: use "goto cleanup" for cleanup_scoreboard()
  string_list API users: use string_list_init_{no,}dup
  format-patch: don't leak "extra_headers" or "ref_message_ids"
  revision.[ch]: split freeing of revs->commit into a function
  revision.[ch]: provide and start using a release_revisions()
  revisions API users: add straightforward release_revisions()
  revision.[ch]: document and move code declared around "init"
  revisions API users: use release_revisions() needing REV_INFO_INIT
  stash: always have the owner of "stash_info" free it
  revisions API users: add "goto cleanup" for release_revisions()
  revisions API users: use release_revisions() in http-push.c
  revisions API users: use release_revisions() in builtin/log.c
  revisions API users: use release_revisions() with UNLEAK()
  revisions API users: use release_revisions() for "prune_data" users
  revisions API: have release_revisions() release "commits"
  revisions API: have release_revisions() release "mailmap"
  revisions API: have release_revisions() release "cmdline"
  revisions API: have release_revisions() release "filter"
  revisions API: have release_revisions() release "grep_filter"
  revisions API: have release_revisions() release "prune_data"
  revisions API: clear "boundary_commits" in release_revisions()
  revisions API: release "reflog_info" in release revisions()
  revisions API: call diff_free(&revs->pruning) in revisions_release()
  revisions API: have release_revisions() release "date_mode"
  revisions API: have release_revisions() release "topo_walk_info"
  revisions API: add a TODO for diff_free(&revs->diffopt)

 add-interactive.c                          |  3 +-
 bisect.c                                   | 20 ++++--
 builtin/add.c                              |  3 +-
 builtin/am.c                               |  3 +
 builtin/bisect--helper.c                   |  2 +
 builtin/blame.c                            |  4 +-
 builtin/checkout.c                         |  3 +-
 builtin/commit.c                           |  6 +-
 builtin/describe.c                         |  2 +
 builtin/diff-files.c                       |  8 ++-
 builtin/diff-index.c                       |  5 +-
 builtin/diff.c                             |  2 +-
 builtin/fast-export.c                      |  1 +
 builtin/log.c                              | 36 ++++++----
 builtin/merge.c                            |  2 +
 builtin/pack-objects.c                     |  2 +
 builtin/prune.c                            |  1 +
 builtin/reflog.c                           |  1 +
 builtin/rev-list.c                         | 19 ++++--
 builtin/shortlog.c                         |  8 ++-
 builtin/stash.c                            | 77 +++++++++++-----------
 builtin/submodule--helper.c                | 14 ++--
 bundle.c                                   | 12 ++--
 diff-lib.c                                 |  8 ++-
 fmt-merge-msg.c                            |  1 +
 http-push.c                                |  3 +-
 merge-ort.c                                |  1 +
 merge-recursive.c                          |  5 +-
 midx.c                                     |  1 +
 pack-bitmap-write.c                        |  1 +
 range-diff.c                               |  2 +-
 ref-filter.c                               |  1 +
 reflog-walk.c                              | 24 ++++++-
 reflog-walk.h                              |  1 +
 remote.c                                   |  1 +
 revision.c                                 | 68 ++++++++++++++++---
 revision.h                                 | 65 +++++++++++-------
 sequencer.c                                | 26 ++++++--
 shallow.c                                  |  1 +
 submodule.c                                |  5 +-
 t/helper/test-fast-rebase.c                | 23 +++++--
 t/helper/test-revision-walking.c           |  1 +
 t/lib-git-svn.sh                           |  4 ++
 t/t0056-git-C.sh                           |  1 +
 t/t0062-revision-walking.sh                |  1 +
 t/t0100-previous.sh                        |  1 +
 t/t0101-at-syntax.sh                       |  2 +
 t/t1001-read-tree-m-2way.sh                |  1 +
 t/t1002-read-tree-m-u-2way.sh              |  1 +
 t/t1060-object-corruption.sh               |  2 +
 t/t1401-symbolic-ref.sh                    |  2 +
 t/t1411-reflog-show.sh                     |  1 +
 t/t1412-reflog-loop.sh                     |  2 +
 t/t1415-worktree-refs.sh                   |  1 +
 t/t2015-checkout-unborn.sh                 |  1 +
 t/t2200-add-update.sh                      |  1 +
 t/t3302-notes-index-expensive.sh           |  1 +
 t/t3303-notes-subtrees.sh                  |  1 +
 t/t3305-notes-fanout.sh                    |  1 +
 t/t3408-rebase-multi-line.sh               |  1 +
 t/t4021-format-patch-numbered.sh           |  1 +
 t/t4027-diff-submodule.sh                  |  1 +
 t/t4028-format-patch-mime-headers.sh       |  2 +
 t/t4036-format-patch-signer-mime.sh        |  1 +
 t/t4039-diff-assume-unchanged.sh           |  1 +
 t/t4055-diff-context.sh                    |  1 +
 t/t4066-diff-emit-delay.sh                 |  1 +
 t/t4122-apply-symlink-inside.sh            |  1 +
 t/t4126-apply-empty.sh                     |  1 -
 t/t4128-apply-root.sh                      |  1 +
 t/t4206-log-follow-harder-copies.sh        |  2 +
 t/t4207-log-decoration-colors.sh           |  1 +
 t/t4212-log-corrupt.sh                     |  1 +
 t/t5301-sliding-window.sh                  |  2 +
 t/t5313-pack-bounds-checks.sh              |  2 +
 t/t5316-pack-delta-depth.sh                |  2 +
 t/t5320-delta-islands.sh                   |  2 +
 t/t5322-pack-objects-sparse.sh             |  1 +
 t/t5506-remote-groups.sh                   |  1 +
 t/t5513-fetch-track.sh                     |  1 +
 t/t5515-fetch-merge-logic.sh               |  1 +
 t/t5518-fetch-exit-status.sh               |  1 +
 t/t5532-fetch-proxy.sh                     |  2 +
 t/t5600-clone-fail-cleanup.sh              |  1 +
 t/t5900-repo-selection.sh                  |  2 +
 t/t6002-rev-list-bisect.sh                 |  1 +
 t/t6003-rev-list-topo-order.sh             |  1 +
 t/t6005-rev-list-count.sh                  |  1 +
 t/t6018-rev-list-glob.sh                   |  1 +
 t/t6100-rev-list-in-order.sh               |  1 +
 t/t6101-rev-parse-parents.sh               |  1 +
 t/t6110-rev-list-sparse.sh                 |  1 +
 t/t6114-keep-packs.sh                      |  2 +
 t/t6131-pathspec-icase.sh                  |  2 +
 t/t7008-filter-branch-null-sha1.sh         |  1 +
 t/t7702-repack-cyclic-alternate.sh         |  2 +
 t/t9001-send-email.sh                      |  1 +
 t/t9100-git-svn-basic.sh                   |  1 +
 t/t9101-git-svn-props.sh                   |  2 +
 t/t9104-git-svn-follow-parent.sh           |  2 +
 t/t9106-git-svn-commit-diff-clobber.sh     |  2 +
 t/t9115-git-svn-dcommit-funky-renames.sh   |  1 +
 t/t9116-git-svn-log.sh                     |  1 +
 t/t9122-git-svn-author.sh                  |  2 +
 t/t9127-git-svn-partial-rebuild.sh         |  1 +
 t/t9129-git-svn-i18n-commitencoding.sh     |  1 +
 t/t9132-git-svn-broken-symlink.sh          |  1 +
 t/t9139-git-svn-non-utf8-commitencoding.sh |  1 +
 t/t9146-git-svn-empty-dirs.sh              |  2 +
 t/t9148-git-svn-propset.sh                 |  1 +
 t/t9160-git-svn-preserve-empty-dirs.sh     |  1 +
 t/t9162-git-svn-dcommit-interactive.sh     |  2 +
 t/t9164-git-svn-dcommit-concurrent.sh      |  2 +
 t/t9501-gitweb-standalone-http-status.sh   |  1 +
 wt-status.c                                | 15 +++--
 115 files changed, 436 insertions(+), 148 deletions(-)

Range-diff against v3:
 1:  7c8298f564f =  1:  b99a07f98ee t/helper/test-fast-rebase.c: don't leak "struct strbuf"
 2:  515bacc8f27 =  2:  da5be507c0f blame: use "goto cleanup" for cleanup_scoreboard()
 3:  58b59542c2b =  3:  243f7c8df96 string_list API users: use string_list_init_{no,}dup
 4:  71495dfd073 =  4:  69f0aabe38f format-patch: don't leak "extra_headers" or "ref_message_ids"
 5:  12fb45f02c1 =  5:  2d0ed57ec2e revision.[ch]: split freeing of revs->commit into a function
 6:  1e93a60d81a !  6:  b9beaba16bf revision.[ch]: provide and start using a release_revisions()
    @@ revision.h: void repo_init_revisions(struct repository *r,
      		    struct setup_revision_opt *);
      
     +/**
    -+ * Free data allocated in a "struct rev_info" after it's been setup
    -+ * with repo_init_revisions() and setup_revisions().
    ++ * Free data allocated in a "struct rev_info" after it's been
    ++ * initialized with repo_init_revisions().
     + */
     +void release_revisions(struct rev_info *revs);
     +
 7:  c4fc1c98e7b !  7:  80ec9a131c0 revisions API users: add straightforward release_revisions()
    @@ builtin/merge.c: static int evaluate_result(void)
      
     
      ## builtin/pack-objects.c ##
    -@@ builtin/pack-objects.c: static void read_packs_list_from_stdin(void)
    - 	strbuf_release(&buf);
    - 	string_list_clear(&include_packs, 0);
    - 	string_list_clear(&exclude_packs, 0);
    -+	release_revisions(&revs);
    - }
    - 
    - static void read_object_list_from_stdin(void)
    -@@ builtin/pack-objects.c: static void get_object_list(int ac, const char **av)
    - 	if (unpack_unreachable)
    - 		loosen_unused_packed_objects();
    - 
    -+	release_revisions(&revs);
    - 	oid_array_clear(&recent_objects);
    - }
    - 
    +@@ builtin/pack-objects.c: int cmd_pack_objects(int argc, const char **argv, const char *prefix)
    + 		read_object_list_from_stdin();
    + 	} else if (pfd.have_revs) {
    + 		get_object_list(&pfd.revs, rp.nr, rp.v);
    ++		release_revisions(&pfd.revs);
    + 	} else {
    + 		struct rev_info revs;
    + 
    + 		repo_init_revisions(the_repository, &revs, NULL);
    + 		get_object_list(&revs, rp.nr, rp.v);
    ++		release_revisions(&revs);
    + 	}
    + 	cleanup_preferred_base();
    + 	if (include_tag && nr_result)
     
      ## builtin/prune.c ##
     @@ builtin/prune.c: int cmd_prune(int argc, const char **argv, const char *prefix)
    @@ submodule.c: static void collect_changed_submodules(struct repository *r,
     +	release_revisions(&rev);
      }
      
    - static void free_submodules_oids(struct string_list *submodules)
    + static void free_submodules_data(struct string_list *submodules)
     
      ## t/helper/test-revision-walking.c ##
     @@ t/helper/test-revision-walking.c: static int run_revision_walk(void)
 8:  f8a9443fe6f <  -:  ----------- revisions API users: add "goto cleanup" for "rev_info" early exit
 9:  5f5c0d26395 <  -:  ----------- revisions API users: use release_revisions() in submodule.c edge case
 -:  ----------- >  8:  086cec742b4 revision.[ch]: document and move code declared around "init"
 -:  ----------- >  9:  2f4e65fb534 revisions API users: use release_revisions() needing REV_INFO_INIT
10:  9da5c5f5f5d = 10:  145a0f74b6a stash: always have the owner of "stash_info" free it
11:  da4fcd04345 = 11:  c9898a5a96b revisions API users: add "goto cleanup" for release_revisions()
12:  b6acc61fb61 = 12:  6e41d666223 revisions API users: use release_revisions() in http-push.c
13:  2eae0e809e5 = 13:  02ca92660af revisions API users: use release_revisions() in builtin/log.c
14:  216eb3b41f7 = 14:  1aeb1127f34 revisions API users: use release_revisions() with UNLEAK()
15:  f8e0eb52957 = 15:  c12b3d5a035 revisions API users: use release_revisions() for "prune_data" users
16:  0a5662a9f67 = 16:  2e351702677 revisions API: have release_revisions() release "commits"
17:  65803961ee2 = 17:  f67f1f5a575 revisions API: have release_revisions() release "mailmap"
18:  ac68630dbf6 = 18:  1df61550e5a revisions API: have release_revisions() release "cmdline"
19:  740abb80c57 = 19:  065c3cc91b2 revisions API: have release_revisions() release "filter"
20:  26163e5e754 = 20:  806fef06b87 revisions API: have release_revisions() release "grep_filter"
21:  842faebddc4 = 21:  fabbe452a14 revisions API: have release_revisions() release "prune_data"
22:  bf93197993c = 22:  cb201fa1914 revisions API: clear "boundary_commits" in release_revisions()
23:  af874c17fbd = 23:  0363a69073c revisions API: release "reflog_info" in release revisions()
24:  006fa1fcc34 = 24:  bad359b2983 revisions API: call diff_free(&revs->pruning) in revisions_release()
25:  0eacbd66711 = 25:  9a51c1ae1d1 revisions API: have release_revisions() release "date_mode"
26:  5efc0ae58ed = 26:  b56db37940d revisions API: have release_revisions() release "topo_walk_info"
27:  6c6fdf696af = 27:  b02cb8fcded revisions API: add a TODO for diff_free(&revs->diffopt)
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply	[flat|nested] 252+ messages in thread

* [PATCH v4 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf"
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 02/27] blame: use "goto cleanup" for cleanup_scoreboard() Ævar Arnfjörð Bjarmason
                         ` (26 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Fix a memory leak that's been with us since f9500261e0a (fast-rebase:
write conflict state to working tree, index, and HEAD, 2021-05-20)
changed this code to move these strbuf_release() into an if/else
block.

We'll also add to "reflog_msg" in the "else" arm of the "if" block
being modified here, and we'll append to "branch_msg" in both
cases. But after f9500261e0a only the "if" block would free these two
"struct strbuf".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 t/helper/test-fast-rebase.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c
index fc2d4609043..993b90eaedd 100644
--- a/t/helper/test-fast-rebase.c
+++ b/t/helper/test-fast-rebase.c
@@ -201,8 +201,6 @@ int cmd__fast_rebase(int argc, const char **argv)
 		}
 		if (create_symref("HEAD", branch_name.buf, reflog_msg.buf) < 0)
 			die(_("unable to update HEAD"));
-		strbuf_release(&reflog_msg);
-		strbuf_release(&branch_name);
 
 		prime_cache_tree(the_repository, the_repository->index,
 				 result.tree);
@@ -221,5 +219,8 @@ int cmd__fast_rebase(int argc, const char **argv)
 	if (write_locked_index(&the_index, &lock,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("unable to write %s"), get_index_file());
+
+	strbuf_release(&reflog_msg);
+	strbuf_release(&branch_name);
 	return (result.clean == 0);
 }
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 02/27] blame: use "goto cleanup" for cleanup_scoreboard()
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 03/27] string_list API users: use string_list_init_{no,}dup Ævar Arnfjörð Bjarmason
                         ` (25 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Amend a freeing pattern added in 0906ac2b54b (blame: use changed-path
Bloom filters, 2020-04-16) to use a "goto cleanup", so that we can be
sure that we call cleanup_scoreboard().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/blame.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/builtin/blame.c b/builtin/blame.c
index 8d15b68afc9..885b381ab83 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1167,7 +1167,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 	if (!incremental)
 		setup_pager();
 	else
-		return 0;
+		goto cleanup;
 
 	blame_sort_final(&sb);
 
@@ -1201,6 +1201,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 		printf("num commits: %d\n", sb.num_commits);
 	}
 
+cleanup:
 	cleanup_scoreboard(&sb);
 	return 0;
 }
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 03/27] string_list API users: use string_list_init_{no,}dup
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 02/27] blame: use "goto cleanup" for cleanup_scoreboard() Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids" Ævar Arnfjörð Bjarmason
                         ` (24 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Follow-up on the introduction of string_list_init_nodup() and
string_list_init_dup() in the series merged in bd4232fac33 (Merge
branch 'ab/struct-init', 2021-07-16) and convert code that implicitly
relied on xcalloc() being equivalent to the initializer to use
xmalloc() and string_list_init_{no,}dup() instead.

In the case of get_unmerged() in merge-recursive.c we used the
combination of xcalloc() and assigning "1" to "strdup_strings" to get
what we'd get via string_list_init_dup(), let's use that instead.

Adjacent code in cmd_format_patch() will be changed in a subsequent
commit, since we're changing that let's change the other in-tree
patterns that do the same. Let's also convert a "x == NULL" to "!x"
per our CodingGuidelines, as we need to change the "if" line anyway.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/log.c      | 9 ++++++---
 builtin/shortlog.c | 6 ++++--
 merge-recursive.c  | 4 ++--
 3 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/builtin/log.c b/builtin/log.c
index c211d66d1d0..634dc782cce 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -231,7 +231,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
 	}
 
 	if (mailmap) {
-		rev->mailmap = xcalloc(1, sizeof(struct string_list));
+		rev->mailmap = xmalloc(sizeof(struct string_list));
+		string_list_init_nodup(rev->mailmap);
 		read_mailmap(rev->mailmap);
 	}
 
@@ -2173,8 +2174,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		prepare_bases(&bases, base, list, nr);
 	}
 
-	if (in_reply_to || thread || cover_letter)
-		rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
+	if (in_reply_to || thread || cover_letter) {
+		rev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));
+		string_list_init_nodup(rev.ref_message_ids);
+	}
 	if (in_reply_to) {
 		const char *msgid = clean_message_id(in_reply_to);
 		string_list_append(rev.ref_message_ids, msgid);
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 26c5c0cf935..fcde07c9367 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -81,8 +81,10 @@ static void insert_one_record(struct shortlog *log,
 		format_subject(&subject, oneline, " ");
 		buffer = strbuf_detach(&subject, NULL);
 
-		if (item->util == NULL)
-			item->util = xcalloc(1, sizeof(struct string_list));
+		if (!item->util) {
+			item->util = xmalloc(sizeof(struct string_list));
+			string_list_init_nodup(item->util);
+		}
 		string_list_append(item->util, buffer);
 	}
 }
diff --git a/merge-recursive.c b/merge-recursive.c
index 1ee6364e8b1..32bbba5fbb1 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -522,10 +522,10 @@ static struct stage_data *insert_stage_data(struct repository *r,
  */
 static struct string_list *get_unmerged(struct index_state *istate)
 {
-	struct string_list *unmerged = xcalloc(1, sizeof(struct string_list));
+	struct string_list *unmerged = xmalloc(sizeof(struct string_list));
 	int i;
 
-	unmerged->strdup_strings = 1;
+	string_list_init_dup(unmerged);
 
 	/* TODO: audit for interaction with sparse-index. */
 	ensure_full_index(istate);
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids"
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (2 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 03/27] string_list API users: use string_list_init_{no,}dup Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-04-01 15:13         ` Phillip Wood
  2022-03-31  1:11       ` [PATCH v4 05/27] revision.[ch]: split freeing of revs->commit into a function Ævar Arnfjörð Bjarmason
                         ` (23 subsequent siblings)
  27 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Fix two memory leaks in "struct rev_info" by freeing that memory in
cmd_format_patch(). These two are unusual special-cases in being in
the "struct rev_info", but not being "owned" by the code in
revision.c. I.e. they're members of the struct so that this code in
"builtin/log.c" can pass information code in log-tree.c.

See 20ff06805c6 (format-patch: resurrect extra headers from config,
2006-06-02) and d1566f7883f (git-format-patch: Make the second and
subsequent mails replies to the first, 2006-07-14) for the initial
introduction of "extra_headers" and "ref_message_ids".

We can count on repo_init_revisions() memset()-ing this data to 0
however, so we can count on it being either NULL or something we
allocated. In the case of "extra_headers" let's add a local "char *"
variable to hold it, to avoid the eventual cast from "const char *"
when we free() it.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/log.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/builtin/log.c b/builtin/log.c
index 634dc782cce..6f9928fabfe 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1747,6 +1747,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	struct commit *commit;
 	struct commit **list = NULL;
 	struct rev_info rev;
+	char *to_free = NULL;
 	struct setup_revision_opt s_r_opt;
 	int nr = 0, total, i;
 	int use_stdout = 0;
@@ -1947,7 +1948,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		strbuf_addch(&buf, '\n');
 	}
 
-	rev.extra_headers = strbuf_detach(&buf, NULL);
+	rev.extra_headers = to_free = strbuf_detach(&buf, NULL);
 
 	if (from) {
 		if (split_ident_line(&rev.from_ident, from, strlen(from)))
@@ -2284,6 +2285,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	strbuf_release(&rdiff1);
 	strbuf_release(&rdiff2);
 	strbuf_release(&rdiff_title);
+	free(to_free);
+	if (rev.ref_message_ids)
+		string_list_clear(rev.ref_message_ids, 0);
+	free(rev.ref_message_ids);
 	UNLEAK(rev);
 	return 0;
 }
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 05/27] revision.[ch]: split freeing of revs->commit into a function
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (3 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids" Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-04-01 15:15         ` Phillip Wood
  2022-03-31  1:11       ` [PATCH v4 06/27] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
                         ` (22 subsequent siblings)
  27 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Move the existing code for invoking free_commit_list() and setting
revs->commits to NULL into a new release_revisions_commit_list()
function. This will be used as part of a general free()-ing mechanism
for "struct rev_info".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/revision.c b/revision.c
index 7d435f80480..29f102cb83d 100644
--- a/revision.c
+++ b/revision.c
@@ -2923,6 +2923,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 	return left;
 }
 
+static void release_revisions_commit_list(struct rev_info *revs)
+{
+	if (revs->commits) {
+		free_commit_list(revs->commits);
+		revs->commits = NULL;
+	}
+}
+
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
 {
 	struct commit_list *l = xcalloc(1, sizeof(*l));
@@ -4080,10 +4088,7 @@ static void create_boundary_commit_list(struct rev_info *revs)
 	 * boundary commits anyway.  (This is what the code has always
 	 * done.)
 	 */
-	if (revs->commits) {
-		free_commit_list(revs->commits);
-		revs->commits = NULL;
-	}
+	release_revisions_commit_list(revs);
 
 	/*
 	 * Put all of the actual boundary commits from revs->boundary_commits
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 06/27] revision.[ch]: provide and start using a release_revisions()
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (4 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 05/27] revision.[ch]: split freeing of revs->commit into a function Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-04-01 15:17         ` Phillip Wood
  2022-03-31  1:11       ` [PATCH v4 07/27] revisions API users: add straightforward release_revisions() Ævar Arnfjörð Bjarmason
                         ` (21 subsequent siblings)
  27 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

The users of the revision.[ch] API's "struct rev_info" are a major
source of memory leaks in the test suite under SANITIZE=leak, which in
turn adds a lot of noise when trying to mark up tests with
"TEST_PASSES_SANITIZE_LEAK=true".

The users of that API are largely one-shot, e.g. "git log", so
arguably freeing the memory is a waste of time, but in many cases
they've actually been trying to free the memory, and just doing that
in a buggy manner.

Let's provide a release_revisions() function for these users, and
start migrating them over per the plan outlined in [1]. Right now this
only handles the "pending" member of the struct, but more will be
added in subsequent commits.

Even though we only clear the "pending" member now, let's not leave a
trap in code like the pre-image of index_differs_from(), where we'd
start doing the wrong thing as soon as the release_revisions() learned
to clear its "diffopt". I.e. we need to call release_revisions() after
we've inspected any state in "struct rev_info".

This leaves in place e.g. clear_pathspec(&rev.prune_data) in
stash_working_tree() in builtin/stash.c, subsequent commits will teach
release_revisions() to free "prune_data" and other members that in
some cases are individually cleared by users of "struct rev_info" by
reaching into its members. Those subsequent commits will remove the
relevant calls to e.g. clear_pathspec().

We avoid amending code in index_differs_from() in diff-lib.c as well
as wt_status_collect_changes_index(), has_unstaged_changes() and
has_uncommitted_changes() in wt-status.c in a way that assumes that we
are already clearing the "diffopt" member. That will be handled in a
subsequent commit.

1. https://lore.kernel.org/git/87a6k8daeu.fsf@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/checkout.c | 2 +-
 builtin/stash.c    | 3 +--
 diff-lib.c         | 2 +-
 range-diff.c       | 2 +-
 revision.c         | 5 +++++
 revision.h         | 6 ++++++
 wt-status.c        | 5 +++--
 7 files changed, 18 insertions(+), 7 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 797681481d1..4d9e0bd3ac1 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -629,7 +629,7 @@ static void show_local_changes(struct object *head,
 	diff_setup_done(&rev.diffopt);
 	add_pending_object(&rev, head, NULL);
 	run_diff_index(&rev, 0);
-	object_array_clear(&rev.pending);
+	release_revisions(&rev);
 }
 
 static void describe_detached_head(const char *msg, struct commit *commit)
diff --git a/builtin/stash.c b/builtin/stash.c
index ccdfdab44be..ad74624c2f7 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1266,9 +1266,8 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
 
 done:
 	discard_index(&istate);
-	UNLEAK(rev);
-	object_array_clear(&rev.pending);
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 	strbuf_release(&diff_output);
 	remove_path(stash_index_path.buf);
 	return ret;
diff --git a/diff-lib.c b/diff-lib.c
index ca085a03efc..d6800274bd5 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -662,7 +662,7 @@ int index_differs_from(struct repository *r,
 		diff_flags_or(&rev.diffopt.flags, flags);
 	rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
 	run_diff_index(&rev, 1);
-	object_array_clear(&rev.pending);
+	release_revisions(&rev);
 	return (rev.diffopt.flags.has_changes != 0);
 }
 
diff --git a/range-diff.c b/range-diff.c
index b72eb9fdbee..39cc010c628 100644
--- a/range-diff.c
+++ b/range-diff.c
@@ -596,6 +596,6 @@ int is_range_diff_range(const char *arg)
 	}
 
 	free(copy);
-	object_array_clear(&revs.pending);
+	release_revisions(&revs);
 	return negative > 0 && positive > 0;
 }
diff --git a/revision.c b/revision.c
index 29f102cb83d..9a3c05b69a5 100644
--- a/revision.c
+++ b/revision.c
@@ -2931,6 +2931,11 @@ static void release_revisions_commit_list(struct rev_info *revs)
 	}
 }
 
+void release_revisions(struct rev_info *revs)
+{
+	object_array_clear(&revs->pending);
+}
+
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
 {
 	struct commit_list *l = xcalloc(1, sizeof(*l));
diff --git a/revision.h b/revision.h
index 5bc59c7bfe1..61c780fc4cd 100644
--- a/revision.h
+++ b/revision.h
@@ -377,6 +377,12 @@ void repo_init_revisions(struct repository *r,
 int setup_revisions(int argc, const char **argv, struct rev_info *revs,
 		    struct setup_revision_opt *);
 
+/**
+ * Free data allocated in a "struct rev_info" after it's been
+ * initialized with repo_init_revisions().
+ */
+void release_revisions(struct rev_info *revs);
+
 void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
 			const struct option *options,
 			const char * const usagestr[]);
diff --git a/wt-status.c b/wt-status.c
index d33f9272b72..922cf787f95 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -662,7 +662,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)
 
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_index(&rev, 1);
-	object_array_clear(&rev.pending);
+	release_revisions(&rev);
 	clear_pathspec(&rev.prune_data);
 }
 
@@ -2545,6 +2545,7 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)
 	rev_info.diffopt.flags.quick = 1;
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_files(&rev_info, 0);
+	release_revisions(&rev_info);
 	return diff_result_code(&rev_info.diffopt, result);
 }
 
@@ -2577,7 +2578,7 @@ int has_uncommitted_changes(struct repository *r,
 
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_index(&rev_info, 1);
-	object_array_clear(&rev_info.pending);
+	release_revisions(&rev_info);
 	return diff_result_code(&rev_info.diffopt, result);
 }
 
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 07/27] revisions API users: add straightforward release_revisions()
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (5 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 06/27] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 08/27] revision.[ch]: document and move code declared around "init" Ævar Arnfjörð Bjarmason
                         ` (20 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Add a release_revisions() to various users of "struct rev_list" in
those straightforward cases where we only need to add the
release_revisions() call to the end of a block, and don't need to
e.g. refactor anything to use a "goto cleanup" pattern.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 add-interactive.c                | 1 +
 bisect.c                         | 2 ++
 builtin/add.c                    | 1 +
 builtin/am.c                     | 3 +++
 builtin/bisect--helper.c         | 2 ++
 builtin/blame.c                  | 1 +
 builtin/checkout.c               | 1 +
 builtin/commit.c                 | 1 +
 builtin/describe.c               | 2 ++
 builtin/fast-export.c            | 1 +
 builtin/merge.c                  | 2 ++
 builtin/pack-objects.c           | 2 ++
 builtin/prune.c                  | 1 +
 builtin/reflog.c                 | 1 +
 builtin/shortlog.c               | 2 ++
 builtin/submodule--helper.c      | 1 +
 fmt-merge-msg.c                  | 1 +
 merge-ort.c                      | 1 +
 merge-recursive.c                | 1 +
 midx.c                           | 1 +
 pack-bitmap-write.c              | 1 +
 ref-filter.c                     | 1 +
 remote.c                         | 1 +
 sequencer.c                      | 3 +++
 shallow.c                        | 1 +
 submodule.c                      | 2 ++
 t/helper/test-revision-walking.c | 1 +
 wt-status.c                      | 1 +
 28 files changed, 39 insertions(+)

diff --git a/add-interactive.c b/add-interactive.c
index 72472103017..54cdfc82017 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -570,6 +570,7 @@ static int get_modified_files(struct repository *r,
 
 		if (ps)
 			clear_pathspec(&rev.prune_data);
+		release_revisions(&rev);
 	}
 	hashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);
 	if (unmerged_count)
diff --git a/bisect.c b/bisect.c
index 9e6a2b7f201..cc6b8b6230d 100644
--- a/bisect.c
+++ b/bisect.c
@@ -884,6 +884,7 @@ static int check_ancestors(struct repository *r, int rev_nr,
 	/* Clean up objects used, as they will be reused. */
 	clear_commit_marks_many(rev_nr, rev, ALL_REV_FLAGS);
 
+	release_revisions(&revs);
 	return res;
 }
 
@@ -964,6 +965,7 @@ static void show_diff_tree(struct repository *r,
 
 	setup_revisions(ARRAY_SIZE(argv) - 1, argv, &opt, NULL);
 	log_tree_commit(&opt, commit);
+	release_revisions(&opt);
 }
 
 /*
diff --git a/builtin/add.c b/builtin/add.c
index 3ffb86a4338..f507d2191cd 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -340,6 +340,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
 
 	unlink(file);
 	free(file);
+	release_revisions(&rev);
 	return 0;
 }
 
diff --git a/builtin/am.c b/builtin/am.c
index 0f4111bafa0..93bec62afa9 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1397,6 +1397,7 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm
 	add_pending_object(&rev_info, &commit->object, "");
 	diff_setup_done(&rev_info.diffopt);
 	log_tree_commit(&rev_info, commit);
+	release_revisions(&rev_info);
 }
 
 /**
@@ -1429,6 +1430,7 @@ static void write_index_patch(const struct am_state *state)
 	add_pending_object(&rev_info, &tree->object, "");
 	diff_setup_done(&rev_info.diffopt);
 	run_diff_index(&rev_info, 1);
+	release_revisions(&rev_info);
 }
 
 /**
@@ -1582,6 +1584,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
 		add_pending_oid(&rev_info, "HEAD", &our_tree, 0);
 		diff_setup_done(&rev_info.diffopt);
 		run_diff_index(&rev_info, 1);
+		release_revisions(&rev_info);
 	}
 
 	if (run_apply(state, index_path))
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 8b2b259ff0d..e4d7b6779ae 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -555,6 +555,7 @@ static int bisect_skipped_commits(struct bisect_terms *terms)
 	reset_revision_walk();
 
 	strbuf_release(&commit_name);
+	release_revisions(&revs);
 	fclose(fp);
 	return 0;
 }
@@ -1041,6 +1042,7 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar
 						oid_to_hex(&commit->object.oid));
 
 			reset_revision_walk();
+			release_revisions(&revs);
 		} else {
 			strvec_push(&argv_state, argv[i]);
 		}
diff --git a/builtin/blame.c b/builtin/blame.c
index 885b381ab83..24bac822c56 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1203,5 +1203,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 
 cleanup:
 	cleanup_scoreboard(&sb);
+	release_revisions(&revs);
 	return 0;
 }
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 4d9e0bd3ac1..7ad4a7113c9 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1082,6 +1082,7 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne
 
 	/* Clean up objects used, as they will be reused. */
 	repo_clear_commit_marks(the_repository, ALL_REV_FLAGS);
+	release_revisions(&revs);
 }
 
 static int switch_branches(const struct checkout_opts *opts,
diff --git a/builtin/commit.c b/builtin/commit.c
index 009a1de0a3d..c7eda9bbb72 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1123,6 +1123,7 @@ static const char *find_author_by_nickname(const char *name)
 		strbuf_release(&buf);
 		format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
 		clear_mailmap(&mailmap);
+		release_revisions(&revs);
 		return strbuf_detach(&buf, NULL);
 	}
 	die(_("--author '%s' is not 'Name <email>' and matches no existing author"), name);
diff --git a/builtin/describe.c b/builtin/describe.c
index 42159cd26bd..a76f1a1a7a7 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -517,6 +517,7 @@ static void describe_blob(struct object_id oid, struct strbuf *dst)
 
 	traverse_commit_list(&revs, process_commit, process_object, &pcd);
 	reset_revision_walk();
+	release_revisions(&revs);
 }
 
 static void describe(const char *arg, int last_one)
@@ -667,6 +668,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
 				suffix = NULL;
 			else
 				suffix = dirty;
+			release_revisions(&revs);
 		}
 		describe("HEAD", 1);
 	} else if (dirty) {
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index a7d72697fba..f34ae451ee3 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -1275,6 +1275,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
 		printf("done\n");
 
 	refspec_clear(&refspecs);
+	release_revisions(&revs);
 
 	return 0;
 }
diff --git a/builtin/merge.c b/builtin/merge.c
index f178f5a3ee1..d9784d4891c 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -443,6 +443,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
 	}
 	write_file_buf(git_path_squash_msg(the_repository), out.buf, out.len);
 	strbuf_release(&out);
+	release_revisions(&rev);
 }
 
 static void finish(struct commit *head_commit,
@@ -998,6 +999,7 @@ static int evaluate_result(void)
 	 */
 	cnt += count_unmerged_entries();
 
+	release_revisions(&rev);
 	return cnt;
 }
 
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 014dcd4bc98..180fff03569 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -4177,11 +4177,13 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 		read_object_list_from_stdin();
 	} else if (pfd.have_revs) {
 		get_object_list(&pfd.revs, rp.nr, rp.v);
+		release_revisions(&pfd.revs);
 	} else {
 		struct rev_info revs;
 
 		repo_init_revisions(the_repository, &revs, NULL);
 		get_object_list(&revs, rp.nr, rp.v);
+		release_revisions(&revs);
 	}
 	cleanup_preferred_base();
 	if (include_tag && nr_result)
diff --git a/builtin/prune.c b/builtin/prune.c
index c2bcdc07db4..df376b2ed1e 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -196,5 +196,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
 		prune_shallow(show_only ? PRUNE_SHOW_ONLY : 0);
 	}
 
+	release_revisions(&revs);
 	return 0;
 }
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 9407f835cb6..592d5d33442 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -248,6 +248,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 		if (verbose)
 			printf(_("Marking reachable objects..."));
 		mark_reachable_objects(&revs, 0, 0, NULL);
+		release_revisions(&revs);
 		if (verbose)
 			putchar('\n');
 	}
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index fcde07c9367..35825f075e3 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -422,6 +422,8 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 	else
 		get_from_rev(&rev, &log);
 
+	release_revisions(&rev);
+
 	shortlog_output(&log);
 	if (log.file != stdout)
 		fclose(log.file);
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 5301612d24b..24980863f68 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1269,6 +1269,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 		run_diff_files(&rev, 0);
 	prepare_submodule_summary(info, &list);
 	strvec_clear(&diff_args);
+	release_revisions(&rev);
 	return 0;
 }
 
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index baca57d5b64..f48f44f9cd1 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -699,6 +699,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 			shortlog(origins.items[i].string,
 				 origins.items[i].util,
 				 head, &rev, opts, out);
+		release_revisions(&rev);
 	}
 
 	strbuf_complete_line(out);
diff --git a/merge-ort.c b/merge-ort.c
index 8545354dafd..4c0d4eba19b 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -1594,6 +1594,7 @@ static int find_first_merges(struct repository *repo,
 	}
 
 	object_array_clear(&merges);
+	release_revisions(&revs);
 	return result->nr;
 }
 
diff --git a/merge-recursive.c b/merge-recursive.c
index 32bbba5fbb1..acd13b2b069 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -1160,6 +1160,7 @@ static int find_first_merges(struct repository *repo,
 	}
 
 	object_array_clear(&merges);
+	release_revisions(&revs);
 	return result->nr;
 }
 
diff --git a/midx.c b/midx.c
index 107365d2114..d5724df7d60 100644
--- a/midx.c
+++ b/midx.c
@@ -1061,6 +1061,7 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr
 	if (indexed_commits_nr_p)
 		*indexed_commits_nr_p = cb.commits_nr;
 
+	release_revisions(&revs);
 	return cb.commits;
 }
 
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index cf681547f2e..c43375bd344 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -326,6 +326,7 @@ static void bitmap_builder_init(struct bitmap_builder *bb,
 	trace2_data_intmax("pack-bitmap-write", the_repository,
 			   "num_maximal_commits", num_maximal);
 
+	release_revisions(&revs);
 	free_commit_list(reusable);
 }
 
diff --git a/ref-filter.c b/ref-filter.c
index 7838bd22b8d..a91688bbf17 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2392,6 +2392,7 @@ static void reach_filter(struct ref_array *array,
 		clear_commit_marks(merge_commit, ALL_REV_FLAGS);
 	}
 
+	release_revisions(&revs);
 	free(to_clear);
 }
 
diff --git a/remote.c b/remote.c
index 42a4e7106e1..fa3152a5d52 100644
--- a/remote.c
+++ b/remote.c
@@ -2172,6 +2172,7 @@ static int stat_branch_pair(const char *branch_name, const char *base,
 	clear_commit_marks(theirs, ALL_REV_FLAGS);
 
 	strvec_clear(&argv);
+	release_revisions(&revs);
 	return 1;
 }
 
diff --git a/sequencer.c b/sequencer.c
index a1bb39383db..f9d7acd1065 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1347,6 +1347,7 @@ void print_commit_summary(struct repository *r,
 		log_tree_commit(&rev, commit);
 	}
 
+	release_revisions(&rev);
 	strbuf_release(&format);
 }
 
@@ -3415,6 +3416,7 @@ static int make_patch(struct repository *r,
 		unuse_commit_buffer(commit, commit_buffer);
 	}
 	strbuf_release(&buf);
+	release_revisions(&log_tree_opt);
 
 	return res;
 }
@@ -4525,6 +4527,7 @@ static int pick_commits(struct repository *r,
 					      &log_tree_opt.diffopt);
 				log_tree_diff_flush(&log_tree_opt);
 			}
+			release_revisions(&log_tree_opt);
 		}
 		flush_rewritten_pending();
 		if (!stat(rebase_path_rewritten_list(), &st) &&
diff --git a/shallow.c b/shallow.c
index e158be58b05..797a593633f 100644
--- a/shallow.c
+++ b/shallow.c
@@ -262,6 +262,7 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,
 		if ((o->flags & both_flags) == both_flags)
 			o->flags &= ~not_shallow_flag;
 	}
+	release_revisions(&revs);
 	return result;
 }
 
diff --git a/submodule.c b/submodule.c
index 86c8f0f89db..4df04ae14e4 100644
--- a/submodule.c
+++ b/submodule.c
@@ -925,9 +925,11 @@ static void collect_changed_submodules(struct repository *r,
 		diff_rev.diffopt.format_callback_data = &data;
 		diff_rev.dense_combined_merges = 1;
 		diff_tree_combined_merge(commit, &diff_rev);
+		release_revisions(&diff_rev);
 	}
 
 	reset_revision_walk();
+	release_revisions(&rev);
 }
 
 static void free_submodules_data(struct string_list *submodules)
diff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c
index 625b2dbf822..4a45d5bac2a 100644
--- a/t/helper/test-revision-walking.c
+++ b/t/helper/test-revision-walking.c
@@ -43,6 +43,7 @@ static int run_revision_walk(void)
 	}
 
 	reset_revision_walk();
+	release_revisions(&rev);
 	return got_revision;
 }
 
diff --git a/wt-status.c b/wt-status.c
index 922cf787f95..f9100621375 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1152,6 +1152,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
 		rev.diffopt.b_prefix = "w/";
 		run_diff_files(&rev, 0);
 	}
+	release_revisions(&rev);
 }
 
 static void wt_longstatus_print_tracking(struct wt_status *s)
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 08/27] revision.[ch]: document and move code declared around "init"
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (6 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 07/27] revisions API users: add straightforward release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-04-01 15:20         ` Phillip Wood
  2022-03-31  1:11       ` [PATCH v4 09/27] revisions API users: use release_revisions() needing REV_INFO_INIT Ævar Arnfjörð Bjarmason
                         ` (19 subsequent siblings)
  27 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

A subsequent commit will add "REV_INFO_INIT" macro adjacent to
repo_init_revisions(), unfortunately between the "struct rev_info"
itself and that function we've added various miscellaneous code
between the two over the years.

Let's move that code either lower in revision.h, giving it API docs
while we're at it, or in cases where it wasn't public API at all move
it into revision.c No lines of code are changed here, only moved
around. The only changes are the addition of new API comments.

The "tree_difference" variable could also be declared like this, which
I think would be a lot clearer, but let's leave that for now to keep
this a move-only change:

	static enum {
		REV_TREE_SAME,
		REV_TREE_NEW, /* Only new files */
		REV_TREE_OLD, /* Only files removed */
		REV_TREE_DIFFERENT, /* Mixed changes */
	} tree_difference = REV_TREE_SAME;

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c |  4 ++++
 revision.h | 50 ++++++++++++++++++++++++--------------------------
 2 files changed, 28 insertions(+), 26 deletions(-)

diff --git a/revision.c b/revision.c
index 9a3c05b69a5..8fc157e9ee7 100644
--- a/revision.c
+++ b/revision.c
@@ -606,6 +606,10 @@ static struct commit *one_relevant_parent(const struct rev_info *revs,
  *
  *   2. We saw anything except REV_TREE_NEW.
  */
+#define REV_TREE_SAME		0
+#define REV_TREE_NEW		1	/* Only new files */
+#define REV_TREE_OLD		2	/* Only files removed */
+#define REV_TREE_DIFFERENT	3	/* Mixed changes */
 static int tree_difference = REV_TREE_SAME;
 
 static void file_add_remove(struct diff_options *options,
diff --git a/revision.h b/revision.h
index 61c780fc4cd..19f886472aa 100644
--- a/revision.h
+++ b/revision.h
@@ -329,32 +329,6 @@ struct rev_info {
 	struct tmp_objdir *remerge_objdir;
 };
 
-int ref_excluded(struct string_list *, const char *path);
-void clear_ref_exclusion(struct string_list **);
-void add_ref_exclusion(struct string_list **, const char *exclude);
-
-
-#define REV_TREE_SAME		0
-#define REV_TREE_NEW		1	/* Only new files */
-#define REV_TREE_OLD		2	/* Only files removed */
-#define REV_TREE_DIFFERENT	3	/* Mixed changes */
-
-/* revision.c */
-typedef void (*show_early_output_fn_t)(struct rev_info *, struct commit_list *);
-extern volatile show_early_output_fn_t show_early_output;
-
-struct setup_revision_opt {
-	const char *def;
-	void (*tweak)(struct rev_info *, struct setup_revision_opt *);
-	unsigned int	assume_dashdash:1,
-			allow_exclude_promisor_objects:1;
-	unsigned revarg_opt;
-};
-
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define init_revisions(revs, prefix) repo_init_revisions(the_repository, revs, prefix)
-#endif
-
 /**
  * Initialize a rev_info structure with default values. The third parameter may
  * be NULL or can be prefix path, and then the `.prefix` variable will be set
@@ -366,6 +340,9 @@ struct setup_revision_opt {
 void repo_init_revisions(struct repository *r,
 			 struct rev_info *revs,
 			 const char *prefix);
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define init_revisions(revs, prefix) repo_init_revisions(the_repository, revs, prefix)
+#endif
 
 /**
  * Parse revision information, filling in the `rev_info` structure, and
@@ -374,6 +351,13 @@ void repo_init_revisions(struct repository *r,
  * head of the argument list. The last parameter is used in case no
  * parameter given by the first two arguments.
  */
+struct setup_revision_opt {
+	const char *def;
+	void (*tweak)(struct rev_info *, struct setup_revision_opt *);
+	unsigned int	assume_dashdash:1,
+			allow_exclude_promisor_objects:1;
+	unsigned revarg_opt;
+};
 int setup_revisions(int argc, const char **argv, struct rev_info *revs,
 		    struct setup_revision_opt *);
 
@@ -423,6 +407,14 @@ void mark_trees_uninteresting_sparse(struct repository *r, struct oidset *trees)
 
 void show_object_with_name(FILE *, struct object *, const char *);
 
+/**
+ * Helpers to check if a "struct string_list" item matches wild
+ * wildmatch().
+ */
+int ref_excluded(struct string_list *, const char *path);
+void clear_ref_exclusion(struct string_list **);
+void add_ref_exclusion(struct string_list **, const char *exclude);
+
 /**
  * This function can be used if you want to add commit objects as revision
  * information. You can use the `UNINTERESTING` object flag to indicate if
@@ -478,4 +470,10 @@ int rewrite_parents(struct rev_info *revs,
  */
 struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit);
 
+/**
+ * Global for the (undocumented) "--early-output" flag for "git log".
+ */
+typedef void (*show_early_output_fn_t)(struct rev_info *, struct commit_list *);
+extern volatile show_early_output_fn_t show_early_output;
+
 #endif
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 09/27] revisions API users: use release_revisions() needing REV_INFO_INIT
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (7 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 08/27] revision.[ch]: document and move code declared around "init" Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-03-31 21:14         ` Junio C Hamano
  2022-03-31  1:11       ` [PATCH v4 10/27] stash: always have the owner of "stash_info" free it Ævar Arnfjörð Bjarmason
                         ` (18 subsequent siblings)
  27 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Use release_revisions() to various users of "struct rev_list" which
need to have their "struct rev_info" zero-initialized before we can
start using it.

To do this add a stub "REV_INFO_INIT" macro, ideally macro would be
able to fully initialize a "struct rev_info", but all it does is the
equivalent of assigning "{ 0 }" to the struct, the API user will still
need to use repo_init_revisions(). In some future follow-up work we'll
hopefully make REV_INFO_INIT be a "stand-alone" init likke STRBUF_INIT
and other similar macros.

For the bundle.c code see the early exit case added in
3bbbe467f29 (bundle verify: error out if called without an object
database, 2019-05-27).

For the relevant bisect.c code see 45b6370812c (bisect: libify
`check_good_are_ancestors_of_bad` and its dependents, 2020-02-17).

For the submodule.c code see the "goto" on "(!left || !right || !sub)"
added in 8e6df65015f (submodule: refactor show_submodule_summary with
helper function, 2016-08-31).

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 bisect.c                    | 18 ++++++++++++------
 builtin/submodule--helper.c |  3 ++-
 bundle.c                    | 12 ++++++++----
 revision.h                  | 13 ++++++++++++-
 submodule.c                 |  3 ++-
 5 files changed, 36 insertions(+), 13 deletions(-)

diff --git a/bisect.c b/bisect.c
index cc6b8b6230d..b63669cc9d7 100644
--- a/bisect.c
+++ b/bisect.c
@@ -1010,7 +1010,7 @@ void read_bisect_terms(const char **read_bad, const char **read_good)
  */
 enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 {
-	struct rev_info revs;
+	struct rev_info revs = REV_INFO_INIT;
 	struct commit_list *tried;
 	int reaches = 0, all = 0, nr, steps;
 	enum bisect_error res = BISECT_OK;
@@ -1035,7 +1035,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 
 	res = check_good_are_ancestors_of_bad(r, prefix, no_checkout);
 	if (res)
-		return res;
+		goto cleanup;
 
 	bisect_rev_setup(r, &revs, prefix, "%s", "^%s", 1);
 
@@ -1060,14 +1060,16 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 		       term_good,
 		       term_bad);
 
-		return BISECT_FAILED;
+		res = BISECT_FAILED;
+		goto cleanup;
 	}
 
 	if (!all) {
 		fprintf(stderr, _("No testable commit found.\n"
 			"Maybe you started with bad path arguments?\n"));
 
-		return BISECT_NO_TESTABLE_COMMIT;
+		res = BISECT_NO_TESTABLE_COMMIT;
+		goto cleanup;
 	}
 
 	bisect_rev = &revs.commits->item->object.oid;
@@ -1087,7 +1089,8 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 		 * for negative return values for early returns up
 		 * until the cmd_bisect__helper() caller.
 		 */
-		return BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;
+		res = BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;
+		goto cleanup;
 	}
 
 	nr = all - reaches - 1;
@@ -1106,7 +1109,10 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 	/* Clean up objects used, as they will be reused. */
 	repo_clear_commit_marks(r, ALL_REV_FLAGS);
 
-	return bisect_checkout(bisect_rev, no_checkout);
+	res = bisect_checkout(bisect_rev, no_checkout);
+cleanup:
+	release_revisions(&revs);
+	return res;
 }
 
 static inline int log2i(int n)
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 24980863f68..cda33ee4d2b 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -766,7 +766,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
 {
 	char *displaypath;
 	struct strvec diff_files_args = STRVEC_INIT;
-	struct rev_info rev;
+	struct rev_info rev = REV_INFO_INIT;
 	int diff_files_result;
 	struct strbuf buf = STRBUF_INIT;
 	const char *git_dir;
@@ -853,6 +853,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
 cleanup:
 	strvec_clear(&diff_files_args);
 	free(displaypath);
+	release_revisions(&rev);
 }
 
 static void status_submodule_cb(const struct cache_entry *list_item,
diff --git a/bundle.c b/bundle.c
index d50cfb5aa7e..6a870a6edb7 100644
--- a/bundle.c
+++ b/bundle.c
@@ -196,14 +196,16 @@ int verify_bundle(struct repository *r,
 	 * to be verbose about the errors
 	 */
 	struct string_list *p = &header->prerequisites;
-	struct rev_info revs;
+	struct rev_info revs = REV_INFO_INIT;
 	const char *argv[] = {NULL, "--all", NULL};
 	struct commit *commit;
 	int i, ret = 0, req_nr;
 	const char *message = _("Repository lacks these prerequisite commits:");
 
-	if (!r || !r->objects || !r->objects->odb)
-		return error(_("need a repository to verify a bundle"));
+	if (!r || !r->objects || !r->objects->odb) {
+		ret = error(_("need a repository to verify a bundle"));
+		goto cleanup;
+	}
 
 	repo_init_revisions(r, &revs, NULL);
 	for (i = 0; i < p->nr; i++) {
@@ -221,7 +223,7 @@ int verify_bundle(struct repository *r,
 		error("%s %s", oid_to_hex(oid), name);
 	}
 	if (revs.pending.nr != p->nr)
-		return ret;
+		goto cleanup;
 	req_nr = revs.pending.nr;
 	setup_revisions(2, argv, &revs, NULL);
 
@@ -284,6 +286,8 @@ int verify_bundle(struct repository *r,
 			printf_ln("The bundle uses this filter: %s",
 				  list_objects_filter_spec(&header->filter));
 	}
+cleanup:
+	release_revisions(&revs);
 	return ret;
 }
 
diff --git a/revision.h b/revision.h
index 19f886472aa..c920753e8b9 100644
--- a/revision.h
+++ b/revision.h
@@ -329,6 +329,17 @@ struct rev_info {
 	struct tmp_objdir *remerge_objdir;
 };
 
+/**
+ * Initialize the "struct rev_info" structure with a macro.
+ *
+ * This is not sufficient (yet!) to initialize a "struct rev_info",
+ * but it's OK (but redundant) to use it before a call to
+ * repo_init_revisions(), which does the real initialization. By using
+ * this it's safe to call release_revisions() on the "struct rev_info"
+ * without having called repo_init_revisions().
+ */
+#define REV_INFO_INIT { 0 }
+
 /**
  * Initialize a rev_info structure with default values. The third parameter may
  * be NULL or can be prefix path, and then the `.prefix` variable will be set
@@ -363,7 +374,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs,
 
 /**
  * Free data allocated in a "struct rev_info" after it's been
- * initialized with repo_init_revisions().
+ * initialized with repo_init_revisions() or REV_INFO_INIT.
  */
 void release_revisions(struct rev_info *revs);
 
diff --git a/submodule.c b/submodule.c
index 4df04ae14e4..e796151ac6f 100644
--- a/submodule.c
+++ b/submodule.c
@@ -619,7 +619,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,
 		struct object_id *one, struct object_id *two,
 		unsigned dirty_submodule)
 {
-	struct rev_info rev;
+	struct rev_info rev = REV_INFO_INIT;
 	struct commit *left = NULL, *right = NULL;
 	struct commit_list *merge_bases = NULL;
 	struct repository *sub;
@@ -645,6 +645,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,
 	print_submodule_diff_summary(sub, &rev, o);
 
 out:
+	release_revisions(&rev);
 	if (merge_bases)
 		free_commit_list(merge_bases);
 	clear_commit_marks(left, ~0);
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 10/27] stash: always have the owner of "stash_info" free it
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (8 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 09/27] revisions API users: use release_revisions() needing REV_INFO_INIT Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-04-01 15:29         ` Phillip Wood
  2022-04-01 15:33         ` Phillip Wood
  2022-03-31  1:11       ` [PATCH v4 11/27] revisions API users: add "goto cleanup" for release_revisions() Ævar Arnfjörð Bjarmason
                         ` (17 subsequent siblings)
  27 siblings, 2 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Change the initialization of the "revision" member of "struct
stash_info" to be initialized vi a macro, and more importantly that
that initializing function be tasked to free it, usually via a "goto
cleanup" pattern.

Despite the "revision" name (and the topic of the series containing
this commit) the "stash info" has nothing to do with the "struct
rev_info". I'm making this change because in the subsequent commit
when we do want to free the "struct rev_info" via a "goto cleanup"
pattern we'd otherwise free() uninitialized memory in some cases, as
we only strbuf_init() the string in get_stash_info().

So while it's the smallest possible change, let's convert all users of
this pattern in the file while we're at it.

A good follow-up to this change would be to change all the "ret = -1;
goto done;" in this file to instead use a "goto cleanup", and
initialize "int ret = -1" at the start of the relevant functions. That
would allow us to drop a lot of needless brace verbosity on two-line
"if" statements, but let's leave that alone for now.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/stash.c | 70 ++++++++++++++++++++++++++-----------------------
 1 file changed, 37 insertions(+), 33 deletions(-)

diff --git a/builtin/stash.c b/builtin/stash.c
index ad74624c2f7..891572d807c 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -116,6 +116,10 @@ struct stash_info {
 	int has_u;
 };
 
+#define STASH_INFO_INIT { \
+	.revision = STRBUF_INIT, \
+}
+
 static void free_stash_info(struct stash_info *info)
 {
 	strbuf_release(&info->revision);
@@ -157,10 +161,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
 	if (argc == 1)
 		commit = argv[0];
 
-	strbuf_init(&info->revision, 0);
 	if (!commit) {
 		if (!ref_exists(ref_stash)) {
-			free_stash_info(info);
 			fprintf_ln(stderr, _("No stash entries found."));
 			return -1;
 		}
@@ -174,11 +176,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
 
 	revision = info->revision.buf;
 
-	if (get_oid(revision, &info->w_commit)) {
-		error(_("%s is not a valid reference"), revision);
-		free_stash_info(info);
-		return -1;
-	}
+	if (get_oid(revision, &info->w_commit))
+		return error(_("%s is not a valid reference"), revision);
 
 	assert_stash_like(info, revision);
 
@@ -197,7 +196,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
 		info->is_stash_ref = !strcmp(expanded_ref, ref_stash);
 		break;
 	default: /* Invalid or ambiguous */
-		free_stash_info(info);
+		break;
 	}
 
 	free(expanded_ref);
@@ -598,10 +597,10 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
 
 static int apply_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret;
+	int ret = -1;
 	int quiet = 0;
 	int index = 0;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_BOOL(0, "index", &index,
@@ -613,9 +612,10 @@ static int apply_stash(int argc, const char **argv, const char *prefix)
 			     git_stash_apply_usage, 0);
 
 	if (get_stash_info(&info, argc, argv))
-		return -1;
+		goto cleanup;
 
 	ret = do_apply_stash(prefix, &info, index, quiet);
+cleanup:
 	free_stash_info(&info);
 	return ret;
 }
@@ -662,9 +662,9 @@ static void assert_stash_ref(struct stash_info *info)
 
 static int drop_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret;
+	int ret = -1;
 	int quiet = 0;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_END()
@@ -674,21 +674,22 @@ static int drop_stash(int argc, const char **argv, const char *prefix)
 			     git_stash_drop_usage, 0);
 
 	if (get_stash_info(&info, argc, argv))
-		return -1;
+		goto cleanup;
 
 	assert_stash_ref(&info);
 
 	ret = do_drop_stash(&info, quiet);
+cleanup:
 	free_stash_info(&info);
 	return ret;
 }
 
 static int pop_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret;
+	int ret = -1;
 	int index = 0;
 	int quiet = 0;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_BOOL(0, "index", &index,
@@ -700,7 +701,7 @@ static int pop_stash(int argc, const char **argv, const char *prefix)
 			     git_stash_pop_usage, 0);
 
 	if (get_stash_info(&info, argc, argv))
-		return -1;
+		goto cleanup;
 
 	assert_stash_ref(&info);
 	if ((ret = do_apply_stash(prefix, &info, index, quiet)))
@@ -709,15 +710,16 @@ static int pop_stash(int argc, const char **argv, const char *prefix)
 	else
 		ret = do_drop_stash(&info, quiet);
 
+cleanup:
 	free_stash_info(&info);
 	return ret;
 }
 
 static int branch_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret;
+	int ret = -1;
 	const char *branch = NULL;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct child_process cp = CHILD_PROCESS_INIT;
 	struct option options[] = {
 		OPT_END()
@@ -734,7 +736,7 @@ static int branch_stash(int argc, const char **argv, const char *prefix)
 	branch = argv[0];
 
 	if (get_stash_info(&info, argc - 1, argv + 1))
-		return -1;
+		goto cleanup;
 
 	cp.git_cmd = 1;
 	strvec_pushl(&cp.args, "checkout", "-b", NULL);
@@ -746,8 +748,8 @@ static int branch_stash(int argc, const char **argv, const char *prefix)
 	if (!ret && info.is_stash_ref)
 		ret = do_drop_stash(&info, 0);
 
+cleanup:
 	free_stash_info(&info);
-
 	return ret;
 }
 
@@ -825,8 +827,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op
 static int show_stash(int argc, const char **argv, const char *prefix)
 {
 	int i;
-	int ret = 0;
-	struct stash_info info;
+	int ret = -1;
+	struct stash_info info = STASH_INFO_INIT;
 	struct rev_info rev;
 	struct strvec stash_args = STRVEC_INIT;
 	struct strvec revision_args = STRVEC_INIT;
@@ -861,10 +863,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 			strvec_push(&revision_args, argv[i]);
 	}
 
-	ret = get_stash_info(&info, stash_args.nr, stash_args.v);
-	strvec_clear(&stash_args);
-	if (ret)
-		return -1;
+	if (get_stash_info(&info, stash_args.nr, stash_args.v))
+		goto cleanup;
 
 	/*
 	 * The config settings are applied only if there are not passed
@@ -878,8 +878,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 			rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
 
 		if (!show_stat && !show_patch) {
-			free_stash_info(&info);
-			return 0;
+			ret = 0;
+			goto cleanup;
 		}
 	}
 
@@ -912,8 +912,11 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 	}
 	log_tree_diff_flush(&rev);
 
+	ret = diff_result_code(&rev.diffopt, 0);;
+cleanup:
+	strvec_clear(&stash_args);
 	free_stash_info(&info);
-	return diff_result_code(&rev.diffopt, 0);
+	return ret;
 }
 
 static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,
@@ -1409,9 +1412,9 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
 
 static int create_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret = 0;
+	int ret;
 	struct strbuf stash_msg_buf = STRBUF_INIT;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct pathspec ps;
 
 	/* Starting with argv[1], since argv[0] is "create" */
@@ -1426,6 +1429,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)
 	if (!ret)
 		printf_ln("%s", oid_to_hex(&info.w_commit));
 
+	free_stash_info(&info);
 	strbuf_release(&stash_msg_buf);
 	return ret;
 }
@@ -1434,7 +1438,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
 			 int keep_index, int patch_mode, int include_untracked, int only_staged)
 {
 	int ret = 0;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct strbuf patch = STRBUF_INIT;
 	struct strbuf stash_msg_buf = STRBUF_INIT;
 	struct strbuf untracked_files = STRBUF_INIT;
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 11/27] revisions API users: add "goto cleanup" for release_revisions()
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (9 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 10/27] stash: always have the owner of "stash_info" free it Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 12/27] revisions API users: use release_revisions() in http-push.c Ævar Arnfjörð Bjarmason
                         ` (16 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Add a release_revisions() to various users of "struct rev_info" which
requires a minor refactoring to a "goto cleanup" pattern to use that
function.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/diff-files.c        |  8 ++++++--
 builtin/rev-list.c          | 19 ++++++++++++-------
 builtin/stash.c             |  1 +
 builtin/submodule--helper.c | 10 +++++++---
 sequencer.c                 | 23 ++++++++++++++++-------
 t/helper/test-fast-rebase.c | 18 +++++++++++++-----
 6 files changed, 55 insertions(+), 24 deletions(-)

diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index 70103c40952..2bfaf9ba7ae 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -77,8 +77,12 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
 
 	if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
 		perror("read_cache_preload");
-		return -1;
+		result = -1;
+		goto cleanup;
 	}
+cleanup:
 	result = run_diff_files(&rev, options);
-	return diff_result_code(&rev.diffopt, result);
+	result = diff_result_code(&rev.diffopt, result);
+	release_revisions(&rev);
+	return result;
 }
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 572da1472e5..2f0b6c759e4 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -502,6 +502,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 	int use_bitmap_index = 0;
 	int filter_provided_objects = 0;
 	const char *show_progress = NULL;
+	int ret = 0;
 
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage(rev_list_usage);
@@ -585,7 +586,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 		}
 		if (!strcmp(arg, "--test-bitmap")) {
 			test_bitmap_walk(&revs);
-			return 0;
+			goto cleanup;
 		}
 		if (skip_prefix(arg, "--progress=", &arg)) {
 			show_progress = arg;
@@ -674,11 +675,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 
 	if (use_bitmap_index) {
 		if (!try_bitmap_count(&revs, filter_provided_objects))
-			return 0;
+			goto cleanup;
 		if (!try_bitmap_disk_usage(&revs, filter_provided_objects))
-			return 0;
+			goto cleanup;
 		if (!try_bitmap_traversal(&revs, filter_provided_objects))
-			return 0;
+			goto cleanup;
 	}
 
 	if (prepare_revision_walk(&revs))
@@ -698,8 +699,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 
 		find_bisection(&revs.commits, &reaches, &all, bisect_flags);
 
-		if (bisect_show_vars)
-			return show_bisect_vars(&info, reaches, all);
+		if (bisect_show_vars) {
+			ret = show_bisect_vars(&info, reaches, all);
+			goto cleanup;
+		}
 	}
 
 	if (filter_provided_objects) {
@@ -754,5 +757,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 	if (show_disk_usage)
 		printf("%"PRIuMAX"\n", (uintmax_t)total_disk_usage);
 
-	return 0;
+cleanup:
+	release_revisions(&revs);
+	return ret;
 }
diff --git a/builtin/stash.c b/builtin/stash.c
index 891572d807c..76d162387e2 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -916,6 +916,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 cleanup:
 	strvec_clear(&stash_args);
 	free_stash_info(&info);
+	release_revisions(&rev);
 	return ret;
 }
 
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index cda33ee4d2b..1bd31c85946 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1232,6 +1232,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 	struct strvec diff_args = STRVEC_INIT;
 	struct rev_info rev;
 	struct module_cb_list list = MODULE_CB_LIST_INIT;
+	int ret = 0;
 
 	strvec_push(&diff_args, get_diff_cmd(diff_cmd));
 	if (info->cached)
@@ -1257,11 +1258,13 @@ static int compute_summary_module_list(struct object_id *head_oid,
 			setup_work_tree();
 		if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
 			perror("read_cache_preload");
-			return -1;
+			ret = -1;
+			goto cleanup;
 		}
 	} else if (read_cache() < 0) {
 		perror("read_cache");
-		return -1;
+		ret = -1;
+		goto cleanup;
 	}
 
 	if (diff_cmd == DIFF_INDEX)
@@ -1269,9 +1272,10 @@ static int compute_summary_module_list(struct object_id *head_oid,
 	else
 		run_diff_files(&rev, 0);
 	prepare_submodule_summary(info, &list);
+cleanup:
 	strvec_clear(&diff_args);
 	release_revisions(&rev);
-	return 0;
+	return ret;
 }
 
 static int module_summary(int argc, const char **argv, const char *prefix)
diff --git a/sequencer.c b/sequencer.c
index f9d7acd1065..41ae5e25278 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -5354,6 +5354,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
 	int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
 	int reapply_cherry_picks = flags & TODO_LIST_REAPPLY_CHERRY_PICKS;
 	int skipped_commit = 0;
+	int ret = 0;
 
 	repo_init_revisions(r, &revs, NULL);
 	revs.verbose_header = 1;
@@ -5377,14 +5378,20 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
 	pp.fmt = revs.commit_format;
 	pp.output_encoding = get_log_output_encoding();
 
-	if (setup_revisions(argc, argv, &revs, NULL) > 1)
-		return error(_("make_script: unhandled options"));
+	if (setup_revisions(argc, argv, &revs, NULL) > 1) {
+		ret = error(_("make_script: unhandled options"));
+		goto cleanup;
+	}
 
-	if (prepare_revision_walk(&revs) < 0)
-		return error(_("make_script: error preparing revisions"));
+	if (prepare_revision_walk(&revs) < 0) {
+		ret = error(_("make_script: error preparing revisions"));
+		goto cleanup;
+	}
 
-	if (rebase_merges)
-		return make_script_with_merges(&pp, &revs, out, flags);
+	if (rebase_merges) {
+		ret = make_script_with_merges(&pp, &revs, out, flags);
+		goto cleanup;
+	}
 
 	while ((commit = get_revision(&revs))) {
 		int is_empty = is_original_commit_empty(commit);
@@ -5408,7 +5415,9 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
 	if (skipped_commit)
 		advise_if_enabled(ADVICE_SKIPPED_CHERRY_PICKS,
 				  _("use --reapply-cherry-picks to include skipped commits"));
-	return 0;
+cleanup:
+	release_revisions(&revs);
+	return ret;
 }
 
 /*
diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c
index 993b90eaedd..4e5553e2024 100644
--- a/t/helper/test-fast-rebase.c
+++ b/t/helper/test-fast-rebase.c
@@ -99,6 +99,7 @@ int cmd__fast_rebase(int argc, const char **argv)
 	struct merge_result result;
 	struct strbuf reflog_msg = STRBUF_INIT;
 	struct strbuf branch_name = STRBUF_INIT;
+	int ret = 0;
 
 	/*
 	 * test-tool stuff doesn't set up the git directory by default; need to
@@ -137,13 +138,17 @@ int cmd__fast_rebase(int argc, const char **argv)
 	revs.topo_order = 1;
 	strvec_pushl(&rev_walk_args, "", argv[4], "--not", argv[3], NULL);
 
-	if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1)
-		return error(_("unhandled options"));
+	if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) {
+		ret = error(_("unhandled options"));
+		goto cleanup;
+	}
 
 	strvec_clear(&rev_walk_args);
 
-	if (prepare_revision_walk(&revs) < 0)
-		return error(_("error preparing revisions"));
+	if (prepare_revision_walk(&revs) < 0) {
+		ret = error(_("error preparing revisions"));
+		goto cleanup;
+	}
 
 	init_merge_options(&merge_opt, the_repository);
 	memset(&result, 0, sizeof(result));
@@ -220,7 +225,10 @@ int cmd__fast_rebase(int argc, const char **argv)
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("unable to write %s"), get_index_file());
 
+	ret = (result.clean == 0);
+cleanup:
 	strbuf_release(&reflog_msg);
 	strbuf_release(&branch_name);
-	return (result.clean == 0);
+	release_revisions(&revs);
+	return ret;
 }
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 12/27] revisions API users: use release_revisions() in http-push.c
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (10 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 11/27] revisions API users: add "goto cleanup" for release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 13/27] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
                         ` (15 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

In the case of cmd_main() in http-push.c we need to move the
deceleration of the "struct rev-list" into the loop over the
"remote_refs" when adding a release_revisions().

We'd previously set up the "revs" for each remote, but would
potentially leak memory on each one.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 http-push.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/http-push.c b/http-push.c
index f0c044dcf76..01e7c2ac5c8 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1689,7 +1689,6 @@ int cmd_main(int argc, const char **argv)
 	struct refspec rs = REFSPEC_INIT_PUSH;
 	struct remote_lock *ref_lock = NULL;
 	struct remote_lock *info_ref_lock = NULL;
-	struct rev_info revs;
 	int delete_branch = 0;
 	int force_delete = 0;
 	int objects_to_send;
@@ -1825,6 +1824,7 @@ int cmd_main(int argc, const char **argv)
 
 	new_refs = 0;
 	for (ref = remote_refs; ref; ref = ref->next) {
+		struct rev_info revs;
 		struct strvec commit_argv = STRVEC_INIT;
 
 		if (!ref->peer_ref)
@@ -1941,6 +1941,7 @@ int cmd_main(int argc, const char **argv)
 		unlock_remote(ref_lock);
 		check_locks();
 		strvec_clear(&commit_argv);
+		release_revisions(&revs);
 	}
 
 	/* Update remote server info if appropriate */
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 13/27] revisions API users: use release_revisions() in builtin/log.c
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (11 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 12/27] revisions API users: use release_revisions() in http-push.c Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-04-02  9:22         ` Phillip Wood
  2022-03-31  1:11       ` [PATCH v4 14/27] revisions API users: use release_revisions() with UNLEAK() Ævar Arnfjörð Bjarmason
                         ` (14 subsequent siblings)
  27 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

In preparation for having the "log" family of functions make wider use
of release_revisions() let's have them call it just before
exiting. This changes the "log", "whatchanged", "show",
"format-patch", etc. commands, all of which live in this file.

The release_revisions() API still only frees the "pending" member, but
will learn to release more members of "struct rev_info" in subsequent
commits.

In the case of "format-patch" revert the addition of UNLEAK() in
dee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16),
which will cause several tests that previously passed under
"TEST_PASSES_SANITIZE_LEAK=true" to start failing.

In subsequent commits we'll now be able to use those tests to check
whether that part of the API is really leaking memory, and will fix
all of those memory leaks. Removing the UNLEAK() allows us to make
incremental progress in that direction. See [1] for further details
about this approach.

Note that the release_revisions() will not be sufficient to deal with
the code in cmd_show() added in 5d7eeee2ac6 (git-show: grok blobs,
trees and tags, too, 2006-12-14) which clobbers the "pending" array in
the case of "OBJ_COMMIT". That will need to be dealt with by some
future follow-up work.

1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/log.c          | 20 ++++++++++++--------
 t/t4126-apply-empty.sh |  2 --
 2 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/builtin/log.c b/builtin/log.c
index 6f9928fabfe..c40ebe1c3f4 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -295,6 +295,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
 	cmd_log_init_finish(argc, argv, prefix, rev, opt);
 }
 
+static int cmd_log_deinit(int ret, struct rev_info *rev)
+{
+	release_revisions(rev);
+	return ret;
+}
+
 /*
  * This gives a rough estimate for how many commits we
  * will print out in the list.
@@ -558,7 +564,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
 	cmd_log_init(argc, argv, prefix, &rev, &opt);
 	if (!rev.diffopt.output_format)
 		rev.diffopt.output_format = DIFF_FORMAT_RAW;
-	return cmd_log_walk(&rev);
+	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 }
 
 static void show_tagger(const char *buf, struct rev_info *rev)
@@ -677,7 +683,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
 	cmd_log_init(argc, argv, prefix, &rev, &opt);
 
 	if (!rev.no_walk)
-		return cmd_log_walk(&rev);
+		return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 
 	count = rev.pending.nr;
 	objects = rev.pending.objects;
@@ -732,8 +738,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
 			ret = error(_("unknown type: %d"), o->type);
 		}
 	}
-	free(objects);
-	return ret;
+	return cmd_log_deinit(ret, &rev);
 }
 
 /*
@@ -761,7 +766,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
 	rev.always_show_header = 1;
 	cmd_log_init_finish(argc, argv, prefix, &rev, &opt);
 
-	return cmd_log_walk(&rev);
+	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 }
 
 static void log_setup_revisions_tweak(struct rev_info *rev,
@@ -792,7 +797,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)
 	opt.revarg_opt = REVARG_COMMITTISH;
 	opt.tweak = log_setup_revisions_tweak;
 	cmd_log_init(argc, argv, prefix, &rev, &opt);
-	return cmd_log_walk(&rev);
+	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 }
 
 /* format-patch */
@@ -2289,8 +2294,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	if (rev.ref_message_ids)
 		string_list_clear(rev.ref_message_ids, 0);
 	free(rev.ref_message_ids);
-	UNLEAK(rev);
-	return 0;
+	return cmd_log_deinit(0, &rev);
 }
 
 static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
diff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh
index 33860d38290..66a7ba8ab8f 100755
--- a/t/t4126-apply-empty.sh
+++ b/t/t4126-apply-empty.sh
@@ -2,8 +2,6 @@
 
 test_description='apply empty'
 
-
-TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 14/27] revisions API users: use release_revisions() with UNLEAK()
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (12 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 13/27] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 15/27] revisions API users: use release_revisions() for "prune_data" users Ævar Arnfjörð Bjarmason
                         ` (13 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Use a release_revisions() with those "struct rev_list" users which
already "UNLEAK" the struct. It may seem odd to simultaneously attempt
to free() memory, but also to explicitly ignore whether we have memory
leaks in the same.

As explained in preceding commits this is being done to use the
built-in commands as a guinea pig for whether the release_revisions()
function works as expected, we'd like to test e.g. whether we segfault
as we change it. In subsequent commits we'll then remove these
UNLEAK() as the function is made to free the memory that caused us to
add them in the first place.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/diff-index.c | 4 +++-
 builtin/diff.c       | 1 +
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 5fd23ab5b6c..3a83183c312 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -71,5 +71,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
 	}
 	result = run_diff_index(&rev, option);
 	UNLEAK(rev);
-	return diff_result_code(&rev.diffopt, result);
+	result = diff_result_code(&rev.diffopt, result);
+	release_revisions(&rev);
+	return result;
 }
diff --git a/builtin/diff.c b/builtin/diff.c
index bb7fafca618..dd48336da56 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -595,6 +595,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
 	if (1 < rev.diffopt.skip_stat_unmatch)
 		refresh_index_quietly();
 	UNLEAK(rev);
+	release_revisions(&rev);
 	UNLEAK(ent);
 	UNLEAK(blob);
 	return result;
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 15/27] revisions API users: use release_revisions() for "prune_data" users
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (13 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 14/27] revisions API users: use release_revisions() with UNLEAK() Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 16/27] revisions API: have release_revisions() release "commits" Ævar Arnfjörð Bjarmason
                         ` (12 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Use release_revisions() for users of "struct rev_list" that reach into
the "struct rev_info" and clear the "prune_data" already.

In a subsequent commit we'll teach release_revisions() to clear this
itself, but in the meantime let's invoke release_revisions() here to
clear anything else we may have missed, and for reasons of having
consistent boilerplate.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/add.c   | 1 +
 builtin/stash.c | 1 +
 diff-lib.c      | 1 +
 wt-status.c     | 1 +
 4 files changed, 4 insertions(+)

diff --git a/builtin/add.c b/builtin/add.c
index f507d2191cd..115a26ea633 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -143,6 +143,7 @@ int add_files_to_cache(const char *prefix,
 	rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
 	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 	return !!data.add_errors;
 }
 
diff --git a/builtin/stash.c b/builtin/stash.c
index 76d162387e2..a6ee030d4bd 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1060,6 +1060,7 @@ static int check_changes_tracked_files(const struct pathspec *ps)
 
 done:
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 	return ret;
 }
 
diff --git a/diff-lib.c b/diff-lib.c
index d6800274bd5..0f16281253f 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -642,6 +642,7 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
 	if (diff_cache(&revs, tree_oid, NULL, 1))
 		exit(128);
 	clear_pathspec(&revs.prune_data);
+	release_revisions(&revs);
 	return 0;
 }
 
diff --git a/wt-status.c b/wt-status.c
index f9100621375..a14fad1e03a 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -617,6 +617,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_files(&rev, 0);
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 }
 
 static void wt_status_collect_changes_index(struct wt_status *s)
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 16/27] revisions API: have release_revisions() release "commits"
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (14 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 15/27] revisions API users: use release_revisions() for "prune_data" users Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 17/27] revisions API: have release_revisions() release "mailmap" Ævar Arnfjörð Bjarmason
                         ` (11 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"commits" in the "struct rev_info".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/revision.c b/revision.c
index 8fc157e9ee7..394e2dcdd90 100644
--- a/revision.c
+++ b/revision.c
@@ -2937,6 +2937,7 @@ static void release_revisions_commit_list(struct rev_info *revs)
 
 void release_revisions(struct rev_info *revs)
 {
+	release_revisions_commit_list(revs);
 	object_array_clear(&revs->pending);
 }
 
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 17/27] revisions API: have release_revisions() release "mailmap"
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (15 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 16/27] revisions API: have release_revisions() release "commits" Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 18/27] revisions API: have release_revisions() release "cmdline" Ævar Arnfjörð Bjarmason
                         ` (10 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"mailmap" in the "struct rev_info".

The log family of functions now calls the clear_mailmap() function
added in fa8afd18e5a (revisions API: provide and use a
release_revisions(), 2021-09-19), allowing us to whitelist some tests
with "TEST_PASSES_SANITIZE_LEAK=true".

Unfortunately having a pointer to a mailmap in "struct rev_info"
instead of an embedded member that we "own" get a bit messy, as can be
seen in the change to builtin/commit.c.

When we free() this data we won't be able to tell apart a pointer to a
"mailmap" on the heap from one on the stack. As seen in
ea57bc0d41b (log: add --use-mailmap option, 2013-01-05) the "log"
family allocates it on the heap, but in the find_author_by_nickname()
code added in ea16794e430 (commit: search author pattern against
mailmap, 2013-08-23) we allocated it on the stack instead.

Ideally we'd simply change that member to a "struct string_list
mailmap" and never free() the "mailmap" itself, but that would be a
much larger change to the revisions API.

We have code that needs to hand an existing "mailmap" to a "struct
rev_info", while we could change all of that, let's not go there
now.

The complexity isn't in the ownership of the "mailmap" per-se, but
that various things assume a "rev_info.mailmap == NULL" means "doesn't
want mailmap", if we changed that to an init'd "struct string_list
we'd need to carefully refactor things to change those assumptions.

Let's instead always free() it, and simply declare that if you add
such a "mailmap" it must be allocated on the heap. Any modern libc
will correctly panic if we free() a stack variable, so this should be
safe going forward.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/commit.c                   | 5 ++---
 revision.c                         | 9 +++++++++
 t/t0056-git-C.sh                   | 1 +
 t/t3302-notes-index-expensive.sh   | 1 +
 t/t4055-diff-context.sh            | 1 +
 t/t4066-diff-emit-delay.sh         | 1 +
 t/t7008-filter-branch-null-sha1.sh | 1 +
 7 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index c7eda9bbb72..cd6cebcf8c8 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1100,7 +1100,6 @@ static const char *find_author_by_nickname(const char *name)
 	struct rev_info revs;
 	struct commit *commit;
 	struct strbuf buf = STRBUF_INIT;
-	struct string_list mailmap = STRING_LIST_INIT_NODUP;
 	const char *av[20];
 	int ac = 0;
 
@@ -1111,7 +1110,8 @@ static const char *find_author_by_nickname(const char *name)
 	av[++ac] = buf.buf;
 	av[++ac] = NULL;
 	setup_revisions(ac, av, &revs, NULL);
-	revs.mailmap = &mailmap;
+	revs.mailmap = xmalloc(sizeof(struct string_list));
+	string_list_init_nodup(revs.mailmap);
 	read_mailmap(revs.mailmap);
 
 	if (prepare_revision_walk(&revs))
@@ -1122,7 +1122,6 @@ static const char *find_author_by_nickname(const char *name)
 		ctx.date_mode.type = DATE_NORMAL;
 		strbuf_release(&buf);
 		format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
-		clear_mailmap(&mailmap);
 		release_revisions(&revs);
 		return strbuf_detach(&buf, NULL);
 	}
diff --git a/revision.c b/revision.c
index 394e2dcdd90..965e913ebba 100644
--- a/revision.c
+++ b/revision.c
@@ -2935,10 +2935,19 @@ static void release_revisions_commit_list(struct rev_info *revs)
 	}
 }
 
+static void release_revisions_mailmap(struct string_list *mailmap)
+{
+	if (!mailmap)
+		return;
+	clear_mailmap(mailmap);
+	free(mailmap);
+}
+
 void release_revisions(struct rev_info *revs)
 {
 	release_revisions_commit_list(revs);
 	object_array_clear(&revs->pending);
+	release_revisions_mailmap(revs->mailmap);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
diff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh
index 2630e756dab..752aa8c9454 100755
--- a/t/t0056-git-C.sh
+++ b/t/t0056-git-C.sh
@@ -2,6 +2,7 @@
 
 test_description='"-C <path>" option and its effects on other path-related options'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success '"git -C <path>" runs git from the directory <path>' '
diff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh
index bb5fea02a03..d0c4d38b4d4 100755
--- a/t/t3302-notes-index-expensive.sh
+++ b/t/t3302-notes-index-expensive.sh
@@ -8,6 +8,7 @@ test_description='Test commit notes index (expensive!)'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 create_repo () {
diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh
index 741e0803c1a..73048d0a526 100755
--- a/t/t4055-diff-context.sh
+++ b/t/t4055-diff-context.sh
@@ -5,6 +5,7 @@
 
 test_description='diff.context configuration'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t4066-diff-emit-delay.sh b/t/t4066-diff-emit-delay.sh
index a1de63b77f8..0ecb3915412 100755
--- a/t/t4066-diff-emit-delay.sh
+++ b/t/t4066-diff-emit-delay.sh
@@ -4,6 +4,7 @@ test_description='test combined/stat/moved interaction'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # This test covers a weird 3-way interaction between "--cc -p", which will run
diff --git a/t/t7008-filter-branch-null-sha1.sh b/t/t7008-filter-branch-null-sha1.sh
index 9ba9f24ad2f..93fbc92b8db 100755
--- a/t/t7008-filter-branch-null-sha1.sh
+++ b/t/t7008-filter-branch-null-sha1.sh
@@ -1,6 +1,7 @@
 #!/bin/sh
 
 test_description='filter-branch removal of trees with null sha1'
+
 . ./test-lib.sh
 
 test_expect_success 'setup: base commits' '
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 18/27] revisions API: have release_revisions() release "cmdline"
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (16 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 17/27] revisions API: have release_revisions() release "mailmap" Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 19/27] revisions API: have release_revisions() release "filter" Ævar Arnfjörð Bjarmason
                         ` (9 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"cmdline" in the "struct rev_info". This in combination with a
preceding change to free "commits" and "mailmap" means that we can
whitelist another test under "TEST_PASSES_SANITIZE_LEAK=true".

There was a proposal in [1] to do away with xstrdup()-ing this
add_rev_cmdline(), perhaps that would be worthwhile, but for now let's
just free() it.

We could also make that a "char *" in "struct rev_cmdline_entry"
itself, but since we own it let's expose it as a constant to outside
callers. I proposed that in [2] but have since changed my mind. See
14d30cdfc04 (ref-filter: fix memory leak in `free_array_item()`,
2019-07-10), c514c62a4fd (checkout: fix leak of non-existent branch
names, 2020-08-14) and other log history hits for "free((char *)" for
prior art.

This includes the tests we had false-positive passes on before my
6798b08e848 (perl Git.pm: don't ignore signalled failure in
_cmd_close(), 2022-02-01), now they pass for real.

Since there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to
list those that don't pass than to touch most of those 66. So let's
introduce a "TEST_FAILS_SANITIZE_LEAK=true", which if set in the tests
won't cause lib-git-svn.sh to set "TEST_PASSES_SANITIZE_LEAK=true.

This change also marks all the tests that we removed
"TEST_FAILS_SANITIZE_LEAK=true" from in an earlier commit due to
removing the UNLEAK() from cmd_format_patch(), we can now assert that
its API use doesn't leak any "struct rev_info" memory.

This change also made commit "t5503-tagfollow.sh" pass on current
master, but that would regress when combined with
ps/fetch-atomic-fixup's de004e848a9 (t5503: simplify setup of test
which exercises failure of backfill, 2022-03-03) (through no fault of
that topic, that change started using "git clone" in the test, which
has an outstanding leak). Let's leave that test out for now to avoid
in-flight semantic conflicts.

1. https://lore.kernel.org/git/YUj%2FgFRh6pwrZalY@carlos-mbp.lan/
2. https://lore.kernel.org/git/87o88obkb1.fsf@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c                                 | 12 ++++++++++++
 t/lib-git-svn.sh                           |  4 ++++
 t/t0062-revision-walking.sh                |  1 +
 t/t0101-at-syntax.sh                       |  2 ++
 t/t1060-object-corruption.sh               |  1 +
 t/t3303-notes-subtrees.sh                  |  1 +
 t/t3305-notes-fanout.sh                    |  1 +
 t/t3408-rebase-multi-line.sh               |  1 +
 t/t4027-diff-submodule.sh                  |  1 +
 t/t4128-apply-root.sh                      |  1 +
 t/t4212-log-corrupt.sh                     |  1 +
 t/t5515-fetch-merge-logic.sh               |  1 +
 t/t5518-fetch-exit-status.sh               |  1 +
 t/t6002-rev-list-bisect.sh                 |  1 +
 t/t6003-rev-list-topo-order.sh             |  1 +
 t/t6005-rev-list-count.sh                  |  1 +
 t/t6018-rev-list-glob.sh                   |  1 +
 t/t6100-rev-list-in-order.sh               |  1 +
 t/t9100-git-svn-basic.sh                   |  1 +
 t/t9101-git-svn-props.sh                   |  2 ++
 t/t9104-git-svn-follow-parent.sh           |  2 ++
 t/t9106-git-svn-commit-diff-clobber.sh     |  2 ++
 t/t9115-git-svn-dcommit-funky-renames.sh   |  1 +
 t/t9116-git-svn-log.sh                     |  2 ++
 t/t9122-git-svn-author.sh                  |  2 ++
 t/t9127-git-svn-partial-rebuild.sh         |  2 ++
 t/t9129-git-svn-i18n-commitencoding.sh     |  1 +
 t/t9132-git-svn-broken-symlink.sh          |  1 +
 t/t9139-git-svn-non-utf8-commitencoding.sh |  1 +
 t/t9146-git-svn-empty-dirs.sh              |  2 ++
 t/t9148-git-svn-propset.sh                 |  1 +
 t/t9151-svn-mergeinfo.sh                   |  1 +
 t/t9160-git-svn-preserve-empty-dirs.sh     |  1 +
 t/t9162-git-svn-dcommit-interactive.sh     |  2 ++
 t/t9164-git-svn-dcommit-concurrent.sh      |  2 ++
 t/t9501-gitweb-standalone-http-status.sh   |  1 +
 36 files changed, 60 insertions(+)

diff --git a/revision.c b/revision.c
index 965e913ebba..8177afebda8 100644
--- a/revision.c
+++ b/revision.c
@@ -2935,6 +2935,17 @@ static void release_revisions_commit_list(struct rev_info *revs)
 	}
 }
 
+static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)
+{
+	unsigned int i;
+
+	if (!cmdline)
+		return;
+	for (i = 0; i < cmdline->nr; i++)
+		free((char *)cmdline->rev[i].name);
+	FREE_AND_NULL(cmdline->rev);
+}
+
 static void release_revisions_mailmap(struct string_list *mailmap)
 {
 	if (!mailmap)
@@ -2947,6 +2958,7 @@ void release_revisions(struct rev_info *revs)
 {
 	release_revisions_commit_list(revs);
 	object_array_clear(&revs->pending);
+	release_revisions_cmdline(&revs->cmdline);
 	release_revisions_mailmap(revs->mailmap);
 }
 
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 2fde2353fd3..ea28971e8ee 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -1,3 +1,7 @@
+if test -z "$TEST_FAILS_SANITIZE_LEAK"
+then
+	TEST_PASSES_SANITIZE_LEAK=true
+fi
 . ./test-lib.sh
 
 if test -n "$NO_SVN_TESTS"
diff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh
index 8e215867b8c..b9480c81781 100755
--- a/t/t0062-revision-walking.sh
+++ b/t/t0062-revision-walking.sh
@@ -5,6 +5,7 @@
 
 test_description='Test revision walking api'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 cat >run_twice_expected <<-EOF
diff --git a/t/t0101-at-syntax.sh b/t/t0101-at-syntax.sh
index a1998b558f9..878aadd64c9 100755
--- a/t/t0101-at-syntax.sh
+++ b/t/t0101-at-syntax.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='various @{whatever} syntax tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh
index bc89371f534..e8a58b15897 100755
--- a/t/t1060-object-corruption.sh
+++ b/t/t1060-object-corruption.sh
@@ -1,6 +1,7 @@
 #!/bin/sh
 
 test_description='see how we handle various forms of corruption'
+
 . ./test-lib.sh
 
 # convert "1234abcd" to ".git/objects/12/34abcd"
diff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh
index eac193757bf..bc9b791d1b9 100755
--- a/t/t3303-notes-subtrees.sh
+++ b/t/t3303-notes-subtrees.sh
@@ -5,6 +5,7 @@ test_description='Test commit notes organized in subtrees'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 number_of_commits=100
diff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh
index 9976d787f47..64a9915761a 100755
--- a/t/t3305-notes-fanout.sh
+++ b/t/t3305-notes-fanout.sh
@@ -2,6 +2,7 @@
 
 test_description='Test that adding/removing many notes triggers automatic fanout restructuring'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 path_has_fanout() {
diff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh
index cde3562e3a6..7b4607d72f2 100755
--- a/t/t3408-rebase-multi-line.sh
+++ b/t/t3408-rebase-multi-line.sh
@@ -5,6 +5,7 @@ test_description='rebasing a commit with multi-line first paragraph.'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
index 295da987cce..40164ae07d2 100755
--- a/t/t4027-diff-submodule.sh
+++ b/t/t4027-diff-submodule.sh
@@ -2,6 +2,7 @@
 
 test_description='difference in submodules'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh
 
diff --git a/t/t4128-apply-root.sh b/t/t4128-apply-root.sh
index f6db5a79dd9..ed94c90204e 100755
--- a/t/t4128-apply-root.sh
+++ b/t/t4128-apply-root.sh
@@ -2,6 +2,7 @@
 
 test_description='apply same filename'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh
index 0244888a5a7..30a219894bb 100755
--- a/t/t4212-log-corrupt.sh
+++ b/t/t4212-log-corrupt.sh
@@ -2,6 +2,7 @@
 
 test_description='git log with invalid commit headers'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh
index 320d26796d2..c100a809c5e 100755
--- a/t/t5515-fetch-merge-logic.sh
+++ b/t/t5515-fetch-merge-logic.sh
@@ -14,6 +14,7 @@ export GIT_TEST_PROTOCOL_VERSION
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 build_script () {
diff --git a/t/t5518-fetch-exit-status.sh b/t/t5518-fetch-exit-status.sh
index 5c4ac2556e7..c13120088fa 100755
--- a/t/t5518-fetch-exit-status.sh
+++ b/t/t5518-fetch-exit-status.sh
@@ -8,6 +8,7 @@ test_description='fetch exit status test'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index b95a0212adf..162cf50778d 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -4,6 +4,7 @@
 #
 test_description='Tests git rev-list --bisect functionality'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
 
diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh
index 24d1836f417..1f7d7dd20c1 100755
--- a/t/t6003-rev-list-topo-order.sh
+++ b/t/t6003-rev-list-topo-order.sh
@@ -5,6 +5,7 @@
 
 test_description='Tests git rev-list --topo-order functionality'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
 
diff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh
index e960049f647..0729f800c3c 100755
--- a/t/t6005-rev-list-count.sh
+++ b/t/t6005-rev-list-count.sh
@@ -2,6 +2,7 @@
 
 test_description='git rev-list --max-count and --skip test'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
index 24b34add833..e1abc5c2b32 100755
--- a/t/t6018-rev-list-glob.sh
+++ b/t/t6018-rev-list-glob.sh
@@ -5,6 +5,7 @@ test_description='rev-list/rev-parse --glob'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 commit () {
diff --git a/t/t6100-rev-list-in-order.sh b/t/t6100-rev-list-in-order.sh
index e934bc239c5..88ed7bd75a7 100755
--- a/t/t6100-rev-list-in-order.sh
+++ b/t/t6100-rev-list-in-order.sh
@@ -2,6 +2,7 @@
 
 test_description='rev-list testing in-commit-order'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a commit history with trees, blobs' '
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index fea41b3c360..7c5b847f584 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -8,6 +8,7 @@ test_description='git svn basic tests'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 prepare_utf8_locale
diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
index 8b5681dd68a..d043e80fc34 100755
--- a/t/t9101-git-svn-props.sh
+++ b/t/t9101-git-svn-props.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn property tests'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 mkdir import
diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
index c7d8e0bf00f..5cf2ef4b8b0 100755
--- a/t/t9104-git-svn-follow-parent.sh
+++ b/t/t9104-git-svn-follow-parent.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn fetching'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
index aec45bca3b7..3cab0b9720a 100755
--- a/t/t9106-git-svn-commit-diff-clobber.sh
+++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -2,6 +2,8 @@
 #
 # Copyright (c) 2006 Eric Wong
 test_description='git svn commit-diff clobber'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh
index 743fbe1fe46..419f055721d 100755
--- a/t/t9115-git-svn-dcommit-funky-renames.sh
+++ b/t/t9115-git-svn-dcommit-funky-renames.sh
@@ -5,6 +5,7 @@
 
 test_description='git svn dcommit can commit renames of files with ugly names'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'load repository with strange names' '
diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh
index 0a9f1ef366d..34f6c80dea3 100755
--- a/t/t9116-git-svn-log.sh
+++ b/t/t9116-git-svn-log.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn log tests'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup repository and import' '
diff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh
index 9e8fe38e7ef..527ba3d2932 100755
--- a/t/t9122-git-svn-author.sh
+++ b/t/t9122-git-svn-author.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='git svn authorship'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup svn repository' '
diff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh
index 2e4789d061f..90b1b30dde5 100755
--- a/t/t9127-git-svn-partial-rebuild.sh
+++ b/t/t9127-git-svn-partial-rebuild.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn partial-rebuild tests'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize svnrepo' '
diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh
index 01e1e8a8f76..185248a4cd7 100755
--- a/t/t9129-git-svn-i18n-commitencoding.sh
+++ b/t/t9129-git-svn-i18n-commitencoding.sh
@@ -4,6 +4,7 @@
 
 test_description='git svn honors i18n.commitEncoding in config'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 compare_git_head_with () {
diff --git a/t/t9132-git-svn-broken-symlink.sh b/t/t9132-git-svn-broken-symlink.sh
index aeceffaf7b0..4d8d0584b79 100755
--- a/t/t9132-git-svn-broken-symlink.sh
+++ b/t/t9132-git-svn-broken-symlink.sh
@@ -2,6 +2,7 @@
 
 test_description='test that git handles an svn repository with empty symlinks'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 test_expect_success 'load svn dumpfile' '
 	svnadmin load "$rawsvnrepo" <<EOF
diff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh
index 22d80b0be2b..b7f756b2b7f 100755
--- a/t/t9139-git-svn-non-utf8-commitencoding.sh
+++ b/t/t9139-git-svn-non-utf8-commitencoding.sh
@@ -4,6 +4,7 @@
 
 test_description='git svn refuses to dcommit non-UTF8 messages'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 # ISO-2022-JP can pass for valid UTF-8, so skipping that in this test
diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh
index 80cb55fee70..79c26ed69c1 100755
--- a/t/t9146-git-svn-empty-dirs.sh
+++ b/t/t9146-git-svn-empty-dirs.sh
@@ -3,6 +3,8 @@
 # Copyright (c) 2009 Eric Wong
 
 test_description='git svn creates empty directories'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9148-git-svn-propset.sh b/t/t9148-git-svn-propset.sh
index aebb28995e5..6cc76a07b39 100755
--- a/t/t9148-git-svn-propset.sh
+++ b/t/t9148-git-svn-propset.sh
@@ -5,6 +5,7 @@
 
 test_description='git svn propset tests'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup propset via import' '
diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh
index c93a5beab25..85221d439bd 100755
--- a/t/t9151-svn-mergeinfo.sh
+++ b/t/t9151-svn-mergeinfo.sh
@@ -5,6 +5,7 @@
 
 test_description='git-svn svn mergeinfo properties'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'load svn dump' "
diff --git a/t/t9160-git-svn-preserve-empty-dirs.sh b/t/t9160-git-svn-preserve-empty-dirs.sh
index 36c6b1a12ff..9cf7a1427ab 100755
--- a/t/t9160-git-svn-preserve-empty-dirs.sh
+++ b/t/t9160-git-svn-preserve-empty-dirs.sh
@@ -9,6 +9,7 @@ This test uses git to clone a Subversion repository that contains empty
 directories, and checks that corresponding directories are created in the
 local Git repository with placeholder files.'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 GIT_REPO=git-svn-repo
diff --git a/t/t9162-git-svn-dcommit-interactive.sh b/t/t9162-git-svn-dcommit-interactive.sh
index e38d9fa37b5..e2aa8ed88a9 100755
--- a/t/t9162-git-svn-dcommit-interactive.sh
+++ b/t/t9162-git-svn-dcommit-interactive.sh
@@ -3,6 +3,8 @@
 # Copyright (c) 2011 Frédéric Heitzmann
 
 test_description='git svn dcommit --interactive series'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9164-git-svn-dcommit-concurrent.sh b/t/t9164-git-svn-dcommit-concurrent.sh
index 8466269bf50..1465156072e 100755
--- a/t/t9164-git-svn-dcommit-concurrent.sh
+++ b/t/t9164-git-svn-dcommit-concurrent.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='concurrent git svn dcommit'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 
diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh
index 32814e75df5..c900231079c 100755
--- a/t/t9501-gitweb-standalone-http-status.sh
+++ b/t/t9501-gitweb-standalone-http-status.sh
@@ -13,6 +13,7 @@ code and message.'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./lib-gitweb.sh
 
 #
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 19/27] revisions API: have release_revisions() release "filter"
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (17 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 18/27] revisions API: have release_revisions() release "cmdline" Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 20/27] revisions API: have release_revisions() release "grep_filter" Ævar Arnfjörð Bjarmason
                         ` (8 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"filter" in the "struct rev_info". This in combination with a
preceding change to free "cmdline" means that we can mark another set
of tests as passing under "TEST_PASSES_SANITIZE_LEAK=true".

The "filter" member was added recently in ffaa137f646 (revision: put
object filter into struct rev_info, 2022-03-09), and this fixes leaks
intruded in the subsequent leak 7940941de1f (pack-objects: use
rev.filter when possible, 2022-03-09) and 105c6f14ad3 (bundle: parse
filter capability, 2022-03-09).

The "builtin/pack-objects.c" leak in 7940941de1f was effectively with
us already, but the variable was referred to by a "static" file-scoped
variable. The "bundle.c " leak in 105c6f14ad3 was newly introduced
with the new "filter" feature for bundles.

The "t5600-clone-fail-cleanup.sh" change here to add
"TEST_PASSES_SANITIZE_LEAK=true" is one of the cases where
run-command.c in not carrying the abort() exit code upwards would have
had that test passing before, but now it *actually* passes[1]. We
should fix the lack of 1=1 mapping of SANITIZE=leak testing to actual
leaks some other time, but it's an existing edge case, let's just mark
the really-passing test as passing for now.

1. https://lore.kernel.org/git/220303.86fsnz5o9w.gmgdl@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c                         | 1 +
 t/t1060-object-corruption.sh       | 1 +
 t/t2015-checkout-unborn.sh         | 1 +
 t/t4207-log-decoration-colors.sh   | 1 +
 t/t5301-sliding-window.sh          | 2 ++
 t/t5313-pack-bounds-checks.sh      | 2 ++
 t/t5316-pack-delta-depth.sh        | 2 ++
 t/t5320-delta-islands.sh           | 2 ++
 t/t5322-pack-objects-sparse.sh     | 1 +
 t/t5506-remote-groups.sh           | 1 +
 t/t5513-fetch-track.sh             | 1 +
 t/t5532-fetch-proxy.sh             | 2 ++
 t/t5600-clone-fail-cleanup.sh      | 1 +
 t/t5900-repo-selection.sh          | 2 ++
 t/t6101-rev-parse-parents.sh       | 1 +
 t/t6114-keep-packs.sh              | 2 ++
 t/t7702-repack-cyclic-alternate.sh | 2 ++
 t/t9127-git-svn-partial-rebuild.sh | 1 -
 18 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/revision.c b/revision.c
index 8177afebda8..2d52f1b518d 100644
--- a/revision.c
+++ b/revision.c
@@ -2959,6 +2959,7 @@ void release_revisions(struct rev_info *revs)
 	release_revisions_commit_list(revs);
 	object_array_clear(&revs->pending);
 	release_revisions_cmdline(&revs->cmdline);
+	list_objects_filter_release(&revs->filter);
 	release_revisions_mailmap(revs->mailmap);
 }
 
diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh
index e8a58b15897..5b8e47e346c 100755
--- a/t/t1060-object-corruption.sh
+++ b/t/t1060-object-corruption.sh
@@ -2,6 +2,7 @@
 
 test_description='see how we handle various forms of corruption'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # convert "1234abcd" to ".git/objects/12/34abcd"
diff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh
index a9721215fab..9425aae6395 100755
--- a/t/t2015-checkout-unborn.sh
+++ b/t/t2015-checkout-unborn.sh
@@ -4,6 +4,7 @@ test_description='checkout from unborn branch'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh
index b8709424981..36ac6aff1e4 100755
--- a/t/t4207-log-decoration-colors.sh
+++ b/t/t4207-log-decoration-colors.sh
@@ -8,6 +8,7 @@ test_description='Test for "git log --decorate" colors'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t5301-sliding-window.sh b/t/t5301-sliding-window.sh
index 76f9798ab95..3ccaaeb3977 100755
--- a/t/t5301-sliding-window.sh
+++ b/t/t5301-sliding-window.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='mmap sliding window tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success \
diff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh
index 535313e4dc8..cc4cfaa9d37 100755
--- a/t/t5313-pack-bounds-checks.sh
+++ b/t/t5313-pack-bounds-checks.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='bounds-checking of access to mmapped on-disk file formats'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 clear_base () {
diff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh
index e9045009a11..eb4ef3dda4d 100755
--- a/t/t5316-pack-delta-depth.sh
+++ b/t/t5316-pack-delta-depth.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='pack-objects breaks long cross-pack delta chains'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # This mirrors a repeated push setup:
diff --git a/t/t5320-delta-islands.sh b/t/t5320-delta-islands.sh
index fea92a5777f..124d47603df 100755
--- a/t/t5320-delta-islands.sh
+++ b/t/t5320-delta-islands.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='exercise delta islands'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # returns true iff $1 is a delta based on $2
diff --git a/t/t5322-pack-objects-sparse.sh b/t/t5322-pack-objects-sparse.sh
index d39958c066d..770695c9278 100755
--- a/t/t5322-pack-objects-sparse.sh
+++ b/t/t5322-pack-objects-sparse.sh
@@ -4,6 +4,7 @@ test_description='pack-objects object selection using sparse algorithm'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup repo' '
diff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh
index 8f150c0793e..5bac03ede81 100755
--- a/t/t5506-remote-groups.sh
+++ b/t/t5506-remote-groups.sh
@@ -4,6 +4,7 @@ test_description='git remote group handling'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 mark() {
diff --git a/t/t5513-fetch-track.sh b/t/t5513-fetch-track.sh
index 65d1e05bd62..c46c4dbaefc 100755
--- a/t/t5513-fetch-track.sh
+++ b/t/t5513-fetch-track.sh
@@ -2,6 +2,7 @@
 
 test_description='fetch follows remote-tracking branches correctly'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh
index 9c2798603b4..d664912799b 100755
--- a/t/t5532-fetch-proxy.sh
+++ b/t/t5532-fetch-proxy.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='fetching via git:// using core.gitproxy'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup remote repo' '
diff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh
index 34b3df40275..c814afa5656 100755
--- a/t/t5600-clone-fail-cleanup.sh
+++ b/t/t5600-clone-fail-cleanup.sh
@@ -13,6 +13,7 @@ Unless the directory already exists, in which case we clean up only what we
 wrote.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 corrupt_repo () {
diff --git a/t/t5900-repo-selection.sh b/t/t5900-repo-selection.sh
index 14e59c5b3e4..a84faac242d 100755
--- a/t/t5900-repo-selection.sh
+++ b/t/t5900-repo-selection.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='selecting remote repo in ambiguous cases'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 reset() {
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
index c571fa51797..a3a41c7a3e4 100755
--- a/t/t6101-rev-parse-parents.sh
+++ b/t/t6101-rev-parse-parents.sh
@@ -8,6 +8,7 @@ test_description='Test git rev-parse with different parent options'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_cmp_rev_output () {
diff --git a/t/t6114-keep-packs.sh b/t/t6114-keep-packs.sh
index 9239d8aa46d..44246f8a63e 100755
--- a/t/t6114-keep-packs.sh
+++ b/t/t6114-keep-packs.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='rev-list with .keep packs'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t7702-repack-cyclic-alternate.sh b/t/t7702-repack-cyclic-alternate.sh
index 93b74867ac8..f3cdb98eec2 100755
--- a/t/t7702-repack-cyclic-alternate.sh
+++ b/t/t7702-repack-cyclic-alternate.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='repack involving cyclic alternate'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh
index 90b1b30dde5..97f495bd49b 100755
--- a/t/t9127-git-svn-partial-rebuild.sh
+++ b/t/t9127-git-svn-partial-rebuild.sh
@@ -5,7 +5,6 @@
 
 test_description='git svn partial-rebuild tests'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize svnrepo' '
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 20/27] revisions API: have release_revisions() release "grep_filter"
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (18 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 19/27] revisions API: have release_revisions() release "filter" Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 21/27] revisions API: have release_revisions() release "prune_data" Ævar Arnfjörð Bjarmason
                         ` (7 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"grep_filter" in the "struct rev_info".This allows us to mark a test
as passing under "TEST_PASSES_SANITIZE_LEAK=true".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c               | 1 +
 t/t9151-svn-mergeinfo.sh | 1 -
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/revision.c b/revision.c
index 2d52f1b518d..349c3f77536 100644
--- a/revision.c
+++ b/revision.c
@@ -2961,6 +2961,7 @@ void release_revisions(struct rev_info *revs)
 	release_revisions_cmdline(&revs->cmdline);
 	list_objects_filter_release(&revs->filter);
 	release_revisions_mailmap(revs->mailmap);
+	free_grep_patterns(&revs->grep_filter);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh
index 85221d439bd..c93a5beab25 100755
--- a/t/t9151-svn-mergeinfo.sh
+++ b/t/t9151-svn-mergeinfo.sh
@@ -5,7 +5,6 @@
 
 test_description='git-svn svn mergeinfo properties'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'load svn dump' "
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 21/27] revisions API: have release_revisions() release "prune_data"
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (19 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 20/27] revisions API: have release_revisions() release "grep_filter" Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 22/27] revisions API: clear "boundary_commits" in release_revisions() Ævar Arnfjörð Bjarmason
                         ` (6 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"prune_data" in the "struct rev_info". This means that any code that
calls "release_revisions()" already can get rid of adjacent calls to
clear_pathspec().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 add-interactive.c | 2 --
 builtin/add.c     | 1 -
 builtin/stash.c   | 2 --
 diff-lib.c        | 1 -
 revision.c        | 1 +
 wt-status.c       | 2 --
 6 files changed, 1 insertion(+), 8 deletions(-)

diff --git a/add-interactive.c b/add-interactive.c
index 54cdfc82017..6047e8f6489 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -568,8 +568,6 @@ static int get_modified_files(struct repository *r,
 			run_diff_files(&rev, 0);
 		}
 
-		if (ps)
-			clear_pathspec(&rev.prune_data);
 		release_revisions(&rev);
 	}
 	hashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);
diff --git a/builtin/add.c b/builtin/add.c
index 115a26ea633..fc729e14c17 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -142,7 +142,6 @@ int add_files_to_cache(const char *prefix,
 	rev.diffopt.flags.override_submodule_config = 1;
 	rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
 	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 	return !!data.add_errors;
 }
diff --git a/builtin/stash.c b/builtin/stash.c
index a6ee030d4bd..5237d0d061a 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1059,7 +1059,6 @@ static int check_changes_tracked_files(const struct pathspec *ps)
 	}
 
 done:
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 	return ret;
 }
@@ -1271,7 +1270,6 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
 
 done:
 	discard_index(&istate);
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 	strbuf_release(&diff_output);
 	remove_path(stash_index_path.buf);
diff --git a/diff-lib.c b/diff-lib.c
index 0f16281253f..298265e5b54 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -641,7 +641,6 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
 
 	if (diff_cache(&revs, tree_oid, NULL, 1))
 		exit(128);
-	clear_pathspec(&revs.prune_data);
 	release_revisions(&revs);
 	return 0;
 }
diff --git a/revision.c b/revision.c
index 349c3f77536..c7a0e08765a 100644
--- a/revision.c
+++ b/revision.c
@@ -2960,6 +2960,7 @@ void release_revisions(struct rev_info *revs)
 	object_array_clear(&revs->pending);
 	release_revisions_cmdline(&revs->cmdline);
 	list_objects_filter_release(&revs->filter);
+	clear_pathspec(&revs->prune_data);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
 }
diff --git a/wt-status.c b/wt-status.c
index a14fad1e03a..61e0c1022f5 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -616,7 +616,6 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
 	rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_files(&rev, 0);
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 }
 
@@ -664,7 +663,6 @@ static void wt_status_collect_changes_index(struct wt_status *s)
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_index(&rev, 1);
 	release_revisions(&rev);
-	clear_pathspec(&rev.prune_data);
 }
 
 static int add_file_to_list(const struct object_id *oid,
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 22/27] revisions API: clear "boundary_commits" in release_revisions()
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (20 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 21/27] revisions API: have release_revisions() release "prune_data" Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 23/27] revisions API: release "reflog_info" in release revisions() Ævar Arnfjörð Bjarmason
                         ` (5 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Clear the "boundary_commits" object_array in release_revisions(). This
makes a few more tests pass under SANITIZE=leak, including
"t/t4126-apply-empty.sh" which started failed as an UNLEAK() in
cmd_format_patch() was removed in a preceding commit.

This also re-marks the various tests relying on "git format-patch" as
passing under "SANITIZE=leak", in the preceding "revisions API users:
use release_revisions() in builtin/log.c" commit those were marked as
failing as we removed the UNLEAK(rev) from cmd_format_patch() in
"builtin/log.c".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c                           | 1 +
 t/t4021-format-patch-numbered.sh     | 1 +
 t/t4028-format-patch-mime-headers.sh | 2 ++
 t/t4036-format-patch-signer-mime.sh  | 1 +
 t/t4122-apply-symlink-inside.sh      | 1 +
 t/t4126-apply-empty.sh               | 1 +
 t/t6110-rev-list-sparse.sh           | 1 +
 t/t9001-send-email.sh                | 1 +
 t/t9116-git-svn-log.sh               | 1 -
 9 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/revision.c b/revision.c
index c7a0e08765a..9806240770c 100644
--- a/revision.c
+++ b/revision.c
@@ -2958,6 +2958,7 @@ void release_revisions(struct rev_info *revs)
 {
 	release_revisions_commit_list(revs);
 	object_array_clear(&revs->pending);
+	object_array_clear(&revs->boundary_commits);
 	release_revisions_cmdline(&revs->cmdline);
 	list_objects_filter_release(&revs->filter);
 	clear_pathspec(&revs->prune_data);
diff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh
index 9be65fd4440..1219aa226dc 100755
--- a/t/t4021-format-patch-numbered.sh
+++ b/t/t4021-format-patch-numbered.sh
@@ -5,6 +5,7 @@
 
 test_description='Format-patch numbering options'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4028-format-patch-mime-headers.sh b/t/t4028-format-patch-mime-headers.sh
index 204ba673cb5..60cb819c42e 100755
--- a/t/t4028-format-patch-mime-headers.sh
+++ b/t/t4028-format-patch-mime-headers.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='format-patch mime headers and extra headers do not conflict'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create commit with utf-8 body' '
diff --git a/t/t4036-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh
index 98d9713d8b2..48655bcc789 100755
--- a/t/t4036-format-patch-signer-mime.sh
+++ b/t/t4036-format-patch-signer-mime.sh
@@ -2,6 +2,7 @@
 
 test_description='format-patch -s should force MIME encoding as needed'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh
index aa52de401b9..96965373036 100755
--- a/t/t4122-apply-symlink-inside.sh
+++ b/t/t4122-apply-symlink-inside.sh
@@ -4,6 +4,7 @@ test_description='apply to deeper directory without getting fooled with symlink'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh
index 66a7ba8ab8f..ece9fae207d 100755
--- a/t/t4126-apply-empty.sh
+++ b/t/t4126-apply-empty.sh
@@ -2,6 +2,7 @@
 
 test_description='apply empty'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t6110-rev-list-sparse.sh b/t/t6110-rev-list-sparse.sh
index 13c1da53528..ddefc7f24ee 100755
--- a/t/t6110-rev-list-sparse.sh
+++ b/t/t6110-rev-list-sparse.sh
@@ -4,6 +4,7 @@ test_description='operations that cull histories in unusual ways'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 84d0f40d76a..dfa6b20f7a6 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -4,6 +4,7 @@ test_description='git send-email'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # May be altered later in the test
diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh
index 34f6c80dea3..d74d7b2de68 100755
--- a/t/t9116-git-svn-log.sh
+++ b/t/t9116-git-svn-log.sh
@@ -5,7 +5,6 @@
 
 test_description='git svn log tests'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup repository and import' '
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 23/27] revisions API: release "reflog_info" in release revisions()
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (21 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 22/27] revisions API: clear "boundary_commits" in release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release() Ævar Arnfjörð Bjarmason
                         ` (4 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Add a missing reflog_walk_info_release() to "reflog-walk.c" and use it
in release_revisions().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 reflog-walk.c            | 24 +++++++++++++++++++++++-
 reflog-walk.h            |  1 +
 revision.c               |  1 +
 t/t0100-previous.sh      |  1 +
 t/t1401-symbolic-ref.sh  |  2 ++
 t/t1411-reflog-show.sh   |  1 +
 t/t1412-reflog-loop.sh   |  2 ++
 t/t1415-worktree-refs.sh |  1 +
 8 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/reflog-walk.c b/reflog-walk.c
index 8ac4b284b6b..7aa6595a51f 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -8,7 +8,7 @@
 
 struct complete_reflogs {
 	char *ref;
-	const char *short_ref;
+	char *short_ref;
 	struct reflog_info {
 		struct object_id ooid, noid;
 		char *email;
@@ -51,9 +51,16 @@ static void free_complete_reflog(struct complete_reflogs *array)
 	}
 	free(array->items);
 	free(array->ref);
+	free(array->short_ref);
 	free(array);
 }
 
+static void complete_reflogs_clear(void *util, const char *str)
+{
+	struct complete_reflogs *array = util;
+	free_complete_reflog(array);
+}
+
 static struct complete_reflogs *read_complete_reflog(const char *ref)
 {
 	struct complete_reflogs *reflogs =
@@ -116,6 +123,21 @@ void init_reflog_walk(struct reflog_walk_info **info)
 	(*info)->complete_reflogs.strdup_strings = 1;
 }
 
+void reflog_walk_info_release(struct reflog_walk_info *info)
+{
+	size_t i;
+
+	if (!info)
+		return;
+
+	for (i = 0; i < info->nr; i++)
+		free(info->logs[i]);
+	string_list_clear_func(&info->complete_reflogs,
+			       complete_reflogs_clear);
+	free(info->logs);
+	free(info);
+}
+
 int add_reflog_for_walk(struct reflog_walk_info *info,
 		struct commit *commit, const char *name)
 {
diff --git a/reflog-walk.h b/reflog-walk.h
index e9e00ffd479..8076f10d9fb 100644
--- a/reflog-walk.h
+++ b/reflog-walk.h
@@ -8,6 +8,7 @@ struct reflog_walk_info;
 struct date_mode;
 
 void init_reflog_walk(struct reflog_walk_info **info);
+void reflog_walk_info_release(struct reflog_walk_info *info);
 int add_reflog_for_walk(struct reflog_walk_info *info,
 			struct commit *commit, const char *name);
 void show_reflog_message(struct reflog_walk_info *info, int,
diff --git a/revision.c b/revision.c
index 9806240770c..24b6bc3ac7e 100644
--- a/revision.c
+++ b/revision.c
@@ -2964,6 +2964,7 @@ void release_revisions(struct rev_info *revs)
 	clear_pathspec(&revs->prune_data);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
+	reflog_walk_info_release(revs->reflog_info);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh
index 69beb59f627..a16cc3d2983 100755
--- a/t/t0100-previous.sh
+++ b/t/t0100-previous.sh
@@ -5,6 +5,7 @@ test_description='previous branch syntax @{-n}'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'branch -d @{-1}' '
diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh
index 132a1b885ac..9fb0b90f252 100755
--- a/t/t1401-symbolic-ref.sh
+++ b/t/t1401-symbolic-ref.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='basic symbolic-ref tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # If the tests munging HEAD fail, they can break detection of
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
index 0bb319b944a..3770ceffafd 100755
--- a/t/t1411-reflog-show.sh
+++ b/t/t1411-reflog-show.sh
@@ -4,6 +4,7 @@ test_description='Test reflog display routines'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t1412-reflog-loop.sh b/t/t1412-reflog-loop.sh
index 977603f7f1c..ff30874f940 100755
--- a/t/t1412-reflog-loop.sh
+++ b/t/t1412-reflog-loop.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='reflog walk shows repeated commits again'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup commits' '
diff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh
index a3e6ea08088..3b531842dd4 100755
--- a/t/t1415-worktree-refs.sh
+++ b/t/t1415-worktree-refs.sh
@@ -2,6 +2,7 @@
 
 test_description='per-worktree refs'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release()
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (22 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 23/27] revisions API: release "reflog_info" in release revisions() Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 25/27] revisions API: have release_revisions() release "date_mode" Ævar Arnfjörð Bjarmason
                         ` (3 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Call diff_free() on the "pruning" member of "struct rev_info".  Doing
so makes several tests pass under SANITIZE=leak.

This was also the last missing piece that allows us to remove the
UNLEAK() in "cmd_diff" and "cmd_diff_index", which allows us to use
those commands as a canary for general leaks in the revisions API. See
[1] for further rationale, and 886e1084d78 (builtin/: add UNLEAKs,
2017-10-01) for the commit that added the UNLEAK() there.

1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/diff-index.c                | 1 -
 builtin/diff.c                      | 1 -
 revision.c                          | 1 +
 t/t1001-read-tree-m-2way.sh         | 1 +
 t/t1002-read-tree-m-u-2way.sh       | 1 +
 t/t2200-add-update.sh               | 1 +
 t/t4039-diff-assume-unchanged.sh    | 1 +
 t/t4206-log-follow-harder-copies.sh | 2 ++
 t/t6131-pathspec-icase.sh           | 2 ++
 9 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 3a83183c312..7d158af6b6d 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -70,7 +70,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
 		return -1;
 	}
 	result = run_diff_index(&rev, option);
-	UNLEAK(rev);
 	result = diff_result_code(&rev.diffopt, result);
 	release_revisions(&rev);
 	return result;
diff --git a/builtin/diff.c b/builtin/diff.c
index dd48336da56..f539132ac68 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -594,7 +594,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
 	result = diff_result_code(&rev.diffopt, result);
 	if (1 < rev.diffopt.skip_stat_unmatch)
 		refresh_index_quietly();
-	UNLEAK(rev);
 	release_revisions(&rev);
 	UNLEAK(ent);
 	UNLEAK(blob);
diff --git a/revision.c b/revision.c
index 24b6bc3ac7e..6490179fb83 100644
--- a/revision.c
+++ b/revision.c
@@ -2964,6 +2964,7 @@ void release_revisions(struct rev_info *revs)
 	clear_pathspec(&revs->prune_data);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
+	diff_free(&revs->pruning);
 	reflog_walk_info_release(revs->reflog_info);
 }
 
diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
index 0710b1fb1e9..516a6112fdc 100755
--- a/t/t1001-read-tree-m-2way.sh
+++ b/t/t1001-read-tree-m-2way.sh
@@ -21,6 +21,7 @@ In the test, these paths are used:
 	yomin   - not in H or M
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 
diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh
index 46cbd5514a6..bd5313caec9 100755
--- a/t/t1002-read-tree-m-u-2way.sh
+++ b/t/t1002-read-tree-m-u-2way.sh
@@ -9,6 +9,7 @@ This is identical to t1001, but uses -u to update the work tree as well.
 
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index 0c38f8e3569..be394f1131a 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -14,6 +14,7 @@ only the updates to dir/sub.
 Also tested are "git add -u" without limiting, and "git add -u"
 without contents changes, and other conditions'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh
index 0eb0314a8b3..78090e6852d 100755
--- a/t/t4039-diff-assume-unchanged.sh
+++ b/t/t4039-diff-assume-unchanged.sh
@@ -2,6 +2,7 @@
 
 test_description='diff with assume-unchanged entries'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # external diff has been tested in t4020-diff-external.sh
diff --git a/t/t4206-log-follow-harder-copies.sh b/t/t4206-log-follow-harder-copies.sh
index 4871a5dc92f..33ecf82c7f9 100755
--- a/t/t4206-log-follow-harder-copies.sh
+++ b/t/t4206-log-follow-harder-copies.sh
@@ -6,6 +6,8 @@
 test_description='Test --follow should always find copies hard in git log.
 
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh
 
diff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh
index 39fc3f6769b..770cce026cb 100755
--- a/t/t6131-pathspec-icase.sh
+++ b/t/t6131-pathspec-icase.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test case insensitive pathspec limiting'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 if test_have_prereq CASE_INSENSITIVE_FS
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 25/27] revisions API: have release_revisions() release "date_mode"
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (23 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release() Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 26/27] revisions API: have release_revisions() release "topo_walk_info" Ævar Arnfjörð Bjarmason
                         ` (2 subsequent siblings)
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"date_mode" in the "struct ref_info".

This uses the date_mode_release() function added in 974c919d36d (date
API: add and use a date_mode_release(), 2022-02-16). As that commit
notes "t7004-tag.sh" tests for the leaks that are being fixed
here. That test now fails "only" 44 tests, instead of the 46 it failed
before this change.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/revision.c b/revision.c
index 6490179fb83..31fc5e58da0 100644
--- a/revision.c
+++ b/revision.c
@@ -2962,6 +2962,7 @@ void release_revisions(struct rev_info *revs)
 	release_revisions_cmdline(&revs->cmdline);
 	list_objects_filter_release(&revs->filter);
 	clear_pathspec(&revs->prune_data);
+	date_mode_release(&revs->date_mode);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
 	diff_free(&revs->pruning);
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 26/27] revisions API: have release_revisions() release "topo_walk_info"
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (24 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 25/27] revisions API: have release_revisions() release "date_mode" Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-03-31  1:11       ` [PATCH v4 27/27] revisions API: add a TODO for diff_free(&revs->diffopt) Ævar Arnfjörð Bjarmason
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Refactor the existing reset_topo_walk() into a thin wrapper for a
release_revisions_topo_walk_info() + resetting the member to "NULL",
and call release_revisions_topo_walk_info() from release_revisions().

This fixes memory leaks that have been with us ever since
"topo_walk_info" was added to revision.[ch] in
f0d9cc4196a (revision.c: begin refactoring --topo-order logic,
2018-11-01).

Due to various other leaks this makes no tests pass in their entirety,
but e.g. before this running this on git.git:

    ./git -P log --pretty=tformat:"%P   %H | %s" --parents --full-history --topo-order -3 -- README.md

Would report under SANITIZE=leak:

    SUMMARY: LeakSanitizer: 531064 byte(s) leaked in 6 allocation(s).

Now we'll free all of that memory.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/revision.c b/revision.c
index 31fc5e58da0..33a458d7c0c 100644
--- a/revision.c
+++ b/revision.c
@@ -2954,6 +2954,8 @@ static void release_revisions_mailmap(struct string_list *mailmap)
 	free(mailmap);
 }
 
+static void release_revisions_topo_walk_info(struct topo_walk_info *info);
+
 void release_revisions(struct rev_info *revs)
 {
 	release_revisions_commit_list(revs);
@@ -2967,6 +2969,7 @@ void release_revisions(struct rev_info *revs)
 	free_grep_patterns(&revs->grep_filter);
 	diff_free(&revs->pruning);
 	reflog_walk_info_release(revs->reflog_info);
+	release_revisions_topo_walk_info(revs->topo_walk_info);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
@@ -3479,17 +3482,22 @@ static void compute_indegrees_to_depth(struct rev_info *revs,
 		indegree_walk_step(revs);
 }
 
-static void reset_topo_walk(struct rev_info *revs)
+static void release_revisions_topo_walk_info(struct topo_walk_info *info)
 {
-	struct topo_walk_info *info = revs->topo_walk_info;
-
+	if (!info)
+		return;
 	clear_prio_queue(&info->explore_queue);
 	clear_prio_queue(&info->indegree_queue);
 	clear_prio_queue(&info->topo_queue);
 	clear_indegree_slab(&info->indegree);
 	clear_author_date_slab(&info->author_date);
+	free(info);
+}
 
-	FREE_AND_NULL(revs->topo_walk_info);
+static void reset_topo_walk(struct rev_info *revs)
+{
+	release_revisions_topo_walk_info(revs->topo_walk_info);
+	revs->topo_walk_info = NULL;
 }
 
 static void init_topo_walk(struct rev_info *revs)
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v4 27/27] revisions API: add a TODO for diff_free(&revs->diffopt)
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (25 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 26/27] revisions API: have release_revisions() release "topo_walk_info" Ævar Arnfjörð Bjarmason
@ 2022-03-31  1:11       ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
  27 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-03-31  1:11 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Ævar Arnfjörð Bjarmason

Add a TODO comment indicating that we should release "diffopt" in
release_revisions(). In a preceding commit we started releasing the
"pruning" member of the same type, but handling "diffopt" will require
us to untangle the "no_free" conditions I added in e900d494dcf (diff:
add an API for deferred freeing, 2021-02-11).

Let's leave a TODO comment to that effect, and so that we don't forget
refactor code that was changed to use release_revisions() in earlier
commits to stop using the "diffopt" member after a call to
release_revisions(). This works currently, but would become a logic
error as soon as we started freeing "diffopt". Doing that change now
doesn't harm anything, and future-proofs us against a later change to
release_revisions().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 diff-lib.c  | 4 +++-
 revision.c  | 1 +
 wt-status.c | 6 ++++--
 3 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/diff-lib.c b/diff-lib.c
index 298265e5b54..7eb66a417aa 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -651,6 +651,7 @@ int index_differs_from(struct repository *r,
 {
 	struct rev_info rev;
 	struct setup_revision_opt opt;
+	unsigned has_changes;
 
 	repo_init_revisions(r, &rev, NULL);
 	memset(&opt, 0, sizeof(opt));
@@ -662,8 +663,9 @@ int index_differs_from(struct repository *r,
 		diff_flags_or(&rev.diffopt.flags, flags);
 	rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
 	run_diff_index(&rev, 1);
+	has_changes = rev.diffopt.flags.has_changes;
 	release_revisions(&rev);
-	return (rev.diffopt.flags.has_changes != 0);
+	return (has_changes != 0);
 }
 
 static struct strbuf *idiff_prefix_cb(struct diff_options *opt, void *data)
diff --git a/revision.c b/revision.c
index 33a458d7c0c..6235946a34e 100644
--- a/revision.c
+++ b/revision.c
@@ -2967,6 +2967,7 @@ void release_revisions(struct rev_info *revs)
 	date_mode_release(&revs->date_mode);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
+	/* TODO (need to handle "no_free"): diff_free(&revs->diffopt) */
 	diff_free(&revs->pruning);
 	reflog_walk_info_release(revs->reflog_info);
 	release_revisions_topo_walk_info(revs->topo_walk_info);
diff --git a/wt-status.c b/wt-status.c
index 61e0c1022f5..102d904adcb 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -2545,8 +2545,9 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)
 	rev_info.diffopt.flags.quick = 1;
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_files(&rev_info, 0);
+	result = diff_result_code(&rev_info.diffopt, result);
 	release_revisions(&rev_info);
-	return diff_result_code(&rev_info.diffopt, result);
+	return result;
 }
 
 /**
@@ -2578,8 +2579,9 @@ int has_uncommitted_changes(struct repository *r,
 
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_index(&rev_info, 1);
+	result = diff_result_code(&rev_info.diffopt, result);
 	release_revisions(&rev_info);
-	return diff_result_code(&rev_info.diffopt, result);
+	return result;
 }
 
 /**
-- 
2.35.1.1557.g4e3e9cdb5e0


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* Re: [PATCH v4 09/27] revisions API users: use release_revisions() needing REV_INFO_INIT
  2022-03-31  1:11       ` [PATCH v4 09/27] revisions API users: use release_revisions() needing REV_INFO_INIT Ævar Arnfjörð Bjarmason
@ 2022-03-31 21:14         ` Junio C Hamano
  2022-04-01 10:31           ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 252+ messages in thread
From: Junio C Hamano @ 2022-03-31 21:14 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:

> Use release_revisions() to various users of "struct rev_list" which
> need to have their "struct rev_info" zero-initialized before we can
> start using it.
>
> To do this add a stub "REV_INFO_INIT" macro, ideally macro would be
> able to fully initialize a "struct rev_info", but all it does is the
> equivalent of assigning "{ 0 }" to the struct, the API user will still
> need to use repo_init_revisions(). In some future follow-up work we'll
> hopefully make REV_INFO_INIT be a "stand-alone" init likke STRBUF_INIT
> and other similar macros.

I do not think we want to leave such a misleading paragraph to
future developers.

Yes, We may want to move some of what init_revisions() does to
REV_INFO_INIT(), and for that, it helps to start using greppable
string REV_INFO_INT early rather than { 0 } to ease such transition,
and that is what we should be stressing, instead of ranting "it does
not do anything, so why are we stupidly introducing a name, instead
of writing { 0 }, which is what amounts to it anyway?" without
explicitly saying so but hinting with words like "stub", "all it
does is", and "will still need to".

Is that some kind of passive-aggressive thing?

You cannot use get_revision() even after calling init_revisions(),
and still need to use setup_revisions() before hand, but that does
not mean init_revisions() is not doing its job.  It may be
implemented as the zero-initialization right now, but it misses the
point to put a stress on the fact that it doesn't do much now.

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v4 09/27] revisions API users: use release_revisions() needing REV_INFO_INIT
  2022-03-31 21:14         ` Junio C Hamano
@ 2022-04-01 10:31           ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-01 10:31 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: git, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson


On Thu, Mar 31 2022, Junio C Hamano wrote:

> Ævar Arnfjörð Bjarmason  <avarab@gmail.com> writes:
>
>> Use release_revisions() to various users of "struct rev_list" which
>> need to have their "struct rev_info" zero-initialized before we can
>> start using it.
>>
>> To do this add a stub "REV_INFO_INIT" macro, ideally macro would be
>> able to fully initialize a "struct rev_info", but all it does is the
>> equivalent of assigning "{ 0 }" to the struct, the API user will still
>> need to use repo_init_revisions(). In some future follow-up work we'll
>> hopefully make REV_INFO_INIT be a "stand-alone" init likke STRBUF_INIT
>> and other similar macros.
>
> I do not think we want to leave such a misleading paragraph to
> future developers.
>
> Yes, We may want to move some of what init_revisions() does to
> REV_INFO_INIT(), and for that, it helps to start using greppable
> string REV_INFO_INT early rather than { 0 } to ease such transition,
> and that is what we should be stressing, instead of ranting "it does
> not do anything, so why are we stupidly introducing a name, instead
> of writing { 0 }, which is what amounts to it anyway?" without
> explicitly saying so but hinting with words like "stub", "all it
> does is", and "will still need to".
>
> Is that some kind of passive-aggressive thing?

No, I'm not trying to be a dick. I'm just confused, sorry :)

I.e. I didn't grok what you really wanted from that REV_INFO_INIT
pattern in the beginning, and I largely draft my commit messages as a
way to adequately explain things to myself.

The "stub" and "not sufficient (yet!)" part of the docs is a (probably
too leaky) artifact of having (after the whole thread about
REV_INFO_INIT started) gotten that mostly working as a "real"
initializer.

Which I figured I'd submit at some point after this lands. The grep.c
case was quite tricky, but the rest look pretty easy, I just didn't
finish them.

I can re-roll & omit this entirely from the commit message, and the
API docs.

I just thought, and still think, that it's worth drawing the API user's
attention to the fact that unlike most stand-alone *_INIT macros this
one isn't sufficient to be up & running with a "struct rev_info".

Which isn't the case with {STRBUF,STRVEC}_INIT, CHILD_PROCESS_INIT,
STRING_LIST_INIT_* etc. etc., i.e. if you've used any of these:

    git grep -h -o '\S+_INIT;' | sort | uniq -c | sort -nr

You're likely to expect them to "flow" a certain way when it comes to
initializing the struct on the stack, but this one will be a bit of an
odd case while repo_init_revisions() is still required, which seems to
me to be worth explaining.

But not enough to argue about it, so whatever you'd prefer...

> You cannot use get_revision() even after calling init_revisions(),
> and still need to use setup_revisions() before hand, but that does
> not mean init_revisions() is not doing its job.  It may be
> implemented as the zero-initialization right now, but it misses the
> point to put a stress on the fact that it doesn't do much now.

I don't think it's broken, having gotten most of the way towards
rewriting it to be macro-init'd there are some tricky edge cases where
we might always need to call a function to fully initialize it.

But per the above I thought it made sense to explain that this
particular *_INIT is a bit of an odd case if you've gotten used to other
such macros.

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v4 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids"
  2022-03-31  1:11       ` [PATCH v4 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids" Ævar Arnfjörð Bjarmason
@ 2022-04-01 15:13         ` Phillip Wood
  2022-04-01 17:16           ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 252+ messages in thread
From: Phillip Wood @ 2022-04-01 15:13 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Hi Ævar

On 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:
> Fix two memory leaks in "struct rev_info" by freeing that memory in
> cmd_format_patch(). These two are unusual special-cases in being in
> the "struct rev_info", but not being "owned" by the code in
> revision.c. I.e. they're members of the struct so that this code in
> "builtin/log.c" can pass information code in log-tree.c.

I'm not sure that I necessarily agree that these are owned by 
builtin/log.c. For rev.extra_headers it is set in builtin/log.c but 
never used there which makes me think we are transferring ownership to 
struct rev_info. For ref_message_ids it is less clear cut but having it 
owned by struct rev_info and freeing it in release_revisions() would 
make things clearer I think. Having some members owned by struct 
rev_info but others allocated and freed by other code is confusing and 
is likely to lead to memory errors. I don't think struct rev_info is 
borrowing a reference to these items as they are being allocated for 
it's exclusive use.

Best Wishes

Phillip

> See 20ff06805c6 (format-patch: resurrect extra headers from config,
> 2006-06-02) and d1566f7883f (git-format-patch: Make the second and
> subsequent mails replies to the first, 2006-07-14) for the initial
> introduction of "extra_headers" and "ref_message_ids".
> 
> We can count on repo_init_revisions() memset()-ing this data to 0
> however, so we can count on it being either NULL or something we
> allocated. In the case of "extra_headers" let's add a local "char *"
> variable to hold it, to avoid the eventual cast from "const char *"
> when we free() it.
> 
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>   builtin/log.c | 7 ++++++-
>   1 file changed, 6 insertions(+), 1 deletion(-)
> 
> diff --git a/builtin/log.c b/builtin/log.c
> index 634dc782cce..6f9928fabfe 100644
> --- a/builtin/log.c
> +++ b/builtin/log.c
> @@ -1747,6 +1747,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
>   	struct commit *commit;
>   	struct commit **list = NULL;
>   	struct rev_info rev;
> +	char *to_free = NULL;
>   	struct setup_revision_opt s_r_opt;
>   	int nr = 0, total, i;
>   	int use_stdout = 0;
> @@ -1947,7 +1948,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
>   		strbuf_addch(&buf, '\n');
>   	}
>   
> -	rev.extra_headers = strbuf_detach(&buf, NULL);
> +	rev.extra_headers = to_free = strbuf_detach(&buf, NULL);
>   
>   	if (from) {
>   		if (split_ident_line(&rev.from_ident, from, strlen(from)))
> @@ -2284,6 +2285,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
>   	strbuf_release(&rdiff1);
>   	strbuf_release(&rdiff2);
>   	strbuf_release(&rdiff_title);
> +	free(to_free);
> +	if (rev.ref_message_ids)
> +		string_list_clear(rev.ref_message_ids, 0);
> +	free(rev.ref_message_ids);
>   	UNLEAK(rev);
>   	return 0;
>   }

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v4 05/27] revision.[ch]: split freeing of revs->commit into a function
  2022-03-31  1:11       ` [PATCH v4 05/27] revision.[ch]: split freeing of revs->commit into a function Ævar Arnfjörð Bjarmason
@ 2022-04-01 15:15         ` Phillip Wood
  2022-04-01 17:03           ` Junio C Hamano
  0 siblings, 1 reply; 252+ messages in thread
From: Phillip Wood @ 2022-04-01 15:15 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Hi Ævar

On 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:
> Move the existing code for invoking free_commit_list() and setting
> revs->commits to NULL into a new release_revisions_commit_list()
> function. This will be used as part of a general free()-ing mechanism
> for "struct rev_info".
> 
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>   revision.c | 13 +++++++++----
>   1 file changed, 9 insertions(+), 4 deletions(-)
> 
> diff --git a/revision.c b/revision.c
> index 7d435f80480..29f102cb83d 100644
> --- a/revision.c
> +++ b/revision.c
> @@ -2923,6 +2923,14 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
>   	return left;
>   }
>   
> +static void release_revisions_commit_list(struct rev_info *revs)
> +{
> +	if (revs->commits) {

This guard copied for the original is not needed, free_commit_list() 
will handle NULL just fine.

Best Wishes

Phillip
> +		free_commit_list(revs->commits);
> +		revs->commits = NULL;
> +	}
> +}
> +
>   static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
>   {
>   	struct commit_list *l = xcalloc(1, sizeof(*l));
> @@ -4080,10 +4088,7 @@ static void create_boundary_commit_list(struct rev_info *revs)
>   	 * boundary commits anyway.  (This is what the code has always
>   	 * done.)
>   	 */
> -	if (revs->commits) {
> -		free_commit_list(revs->commits);
> -		revs->commits = NULL;
> -	}
> +	release_revisions_commit_list(revs);
>   
>   	/*
>   	 * Put all of the actual boundary commits from revs->boundary_commits

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v4 06/27] revision.[ch]: provide and start using a release_revisions()
  2022-03-31  1:11       ` [PATCH v4 06/27] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-04-01 15:17         ` Phillip Wood
  2022-04-01 17:25           ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 252+ messages in thread
From: Phillip Wood @ 2022-04-01 15:17 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Hi Ævar

On 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:
> The users of the revision.[ch] API's "struct rev_info" are a major
> source of memory leaks in the test suite under SANITIZE=leak, which in
> turn adds a lot of noise when trying to mark up tests with
> "TEST_PASSES_SANITIZE_LEAK=true".
> 
> The users of that API are largely one-shot, e.g. "git log", so
> arguably freeing the memory is a waste of time, but in many cases
> they've actually been trying to free the memory, and just doing that
> in a buggy manner.

I've not looked that closely at these changes but builtin/log.c is not 
modified so I'm a bit confused by the mention of "git log" above.

Best Wishes

Phillip

> Let's provide a release_revisions() function for these users, and
> start migrating them over per the plan outlined in [1]. Right now this
> only handles the "pending" member of the struct, but more will be
> added in subsequent commits.
> 
> Even though we only clear the "pending" member now, let's not leave a
> trap in code like the pre-image of index_differs_from(), where we'd
> start doing the wrong thing as soon as the release_revisions() learned
> to clear its "diffopt". I.e. we need to call release_revisions() after
> we've inspected any state in "struct rev_info".
> 
> This leaves in place e.g. clear_pathspec(&rev.prune_data) in
> stash_working_tree() in builtin/stash.c, subsequent commits will teach
> release_revisions() to free "prune_data" and other members that in
> some cases are individually cleared by users of "struct rev_info" by
> reaching into its members. Those subsequent commits will remove the
> relevant calls to e.g. clear_pathspec().
> 
> We avoid amending code in index_differs_from() in diff-lib.c as well
> as wt_status_collect_changes_index(), has_unstaged_changes() and
> has_uncommitted_changes() in wt-status.c in a way that assumes that we
> are already clearing the "diffopt" member. That will be handled in a
> subsequent commit.
> 
> 1. https://lore.kernel.org/git/87a6k8daeu.fsf@evledraar.gmail.com/
> 
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>   builtin/checkout.c | 2 +-
>   builtin/stash.c    | 3 +--
>   diff-lib.c         | 2 +-
>   range-diff.c       | 2 +-
>   revision.c         | 5 +++++
>   revision.h         | 6 ++++++
>   wt-status.c        | 5 +++--
>   7 files changed, 18 insertions(+), 7 deletions(-)
> 
> diff --git a/builtin/checkout.c b/builtin/checkout.c
> index 797681481d1..4d9e0bd3ac1 100644
> --- a/builtin/checkout.c
> +++ b/builtin/checkout.c
> @@ -629,7 +629,7 @@ static void show_local_changes(struct object *head,
>   	diff_setup_done(&rev.diffopt);
>   	add_pending_object(&rev, head, NULL);
>   	run_diff_index(&rev, 0);
> -	object_array_clear(&rev.pending);
> +	release_revisions(&rev);
>   }
>   
>   static void describe_detached_head(const char *msg, struct commit *commit)
> diff --git a/builtin/stash.c b/builtin/stash.c
> index ccdfdab44be..ad74624c2f7 100644
> --- a/builtin/stash.c
> +++ b/builtin/stash.c
> @@ -1266,9 +1266,8 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
>   
>   done:
>   	discard_index(&istate);
> -	UNLEAK(rev);
> -	object_array_clear(&rev.pending);
>   	clear_pathspec(&rev.prune_data);
> +	release_revisions(&rev);
>   	strbuf_release(&diff_output);
>   	remove_path(stash_index_path.buf);
>   	return ret;
> diff --git a/diff-lib.c b/diff-lib.c
> index ca085a03efc..d6800274bd5 100644
> --- a/diff-lib.c
> +++ b/diff-lib.c
> @@ -662,7 +662,7 @@ int index_differs_from(struct repository *r,
>   		diff_flags_or(&rev.diffopt.flags, flags);
>   	rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
>   	run_diff_index(&rev, 1);
> -	object_array_clear(&rev.pending);
> +	release_revisions(&rev);
>   	return (rev.diffopt.flags.has_changes != 0);
>   }
>   
> diff --git a/range-diff.c b/range-diff.c
> index b72eb9fdbee..39cc010c628 100644
> --- a/range-diff.c
> +++ b/range-diff.c
> @@ -596,6 +596,6 @@ int is_range_diff_range(const char *arg)
>   	}
>   
>   	free(copy);
> -	object_array_clear(&revs.pending);
> +	release_revisions(&revs);
>   	return negative > 0 && positive > 0;
>   }
> diff --git a/revision.c b/revision.c
> index 29f102cb83d..9a3c05b69a5 100644
> --- a/revision.c
> +++ b/revision.c
> @@ -2931,6 +2931,11 @@ static void release_revisions_commit_list(struct rev_info *revs)
>   	}
>   }
>   
> +void release_revisions(struct rev_info *revs)
> +{
> +	object_array_clear(&revs->pending);
> +}
> +
>   static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
>   {
>   	struct commit_list *l = xcalloc(1, sizeof(*l));
> diff --git a/revision.h b/revision.h
> index 5bc59c7bfe1..61c780fc4cd 100644
> --- a/revision.h
> +++ b/revision.h
> @@ -377,6 +377,12 @@ void repo_init_revisions(struct repository *r,
>   int setup_revisions(int argc, const char **argv, struct rev_info *revs,
>   		    struct setup_revision_opt *);
>   
> +/**
> + * Free data allocated in a "struct rev_info" after it's been
> + * initialized with repo_init_revisions().
> + */
> +void release_revisions(struct rev_info *revs);
> +
>   void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
>   			const struct option *options,
>   			const char * const usagestr[]);
> diff --git a/wt-status.c b/wt-status.c
> index d33f9272b72..922cf787f95 100644
> --- a/wt-status.c
> +++ b/wt-status.c
> @@ -662,7 +662,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)
>   
>   	copy_pathspec(&rev.prune_data, &s->pathspec);
>   	run_diff_index(&rev, 1);
> -	object_array_clear(&rev.pending);
> +	release_revisions(&rev);
>   	clear_pathspec(&rev.prune_data);
>   }
>   
> @@ -2545,6 +2545,7 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)
>   	rev_info.diffopt.flags.quick = 1;
>   	diff_setup_done(&rev_info.diffopt);
>   	result = run_diff_files(&rev_info, 0);
> +	release_revisions(&rev_info);
>   	return diff_result_code(&rev_info.diffopt, result);
>   }
>   
> @@ -2577,7 +2578,7 @@ int has_uncommitted_changes(struct repository *r,
>   
>   	diff_setup_done(&rev_info.diffopt);
>   	result = run_diff_index(&rev_info, 1);
> -	object_array_clear(&rev_info.pending);
> +	release_revisions(&rev_info);
>   	return diff_result_code(&rev_info.diffopt, result);
>   }
>   

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v4 08/27] revision.[ch]: document and move code declared around "init"
  2022-03-31  1:11       ` [PATCH v4 08/27] revision.[ch]: document and move code declared around "init" Ævar Arnfjörð Bjarmason
@ 2022-04-01 15:20         ` Phillip Wood
  0 siblings, 0 replies; 252+ messages in thread
From: Phillip Wood @ 2022-04-01 15:20 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Hi Ævar

On 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:
> A subsequent commit will add "REV_INFO_INIT" macro adjacent to
> repo_init_revisions(), unfortunately between the "struct rev_info"
> itself and that function we've added various miscellaneous code
> between the two over the years.
> 
> Let's move that code either lower in revision.h, giving it API docs
> while we're at it, or in cases where it wasn't public API at all move
> it into revision.c No lines of code are changed here, only moved
> around. The only changes are the addition of new API comments.

It's nice to see to see the API being documented

 >[...]
>   
> +/**
> + * Helpers to check if a "struct string_list" item matches wild
> + * wildmatch().

wild wildmatch()??

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v4 10/27] stash: always have the owner of "stash_info" free it
  2022-03-31  1:11       ` [PATCH v4 10/27] stash: always have the owner of "stash_info" free it Ævar Arnfjörð Bjarmason
@ 2022-04-01 15:29         ` Phillip Wood
  2022-04-01 17:29           ` Ævar Arnfjörð Bjarmason
  2022-04-01 15:33         ` Phillip Wood
  1 sibling, 1 reply; 252+ messages in thread
From: Phillip Wood @ 2022-04-01 15:29 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Hi Ævar

On 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:
> Change the initialization of the "revision" member of "struct
> stash_info" to be initialized vi a macro, and more importantly that
> that initializing function be tasked to free it, usually via a "goto
> cleanup" pattern.
> 
> Despite the "revision" name (and the topic of the series containing
> this commit) the "stash info" has nothing to do with the "struct
> rev_info". I'm making this change because in the subsequent commit
> when we do want to free the "struct rev_info" via a "goto cleanup"
> pattern we'd otherwise free() uninitialized memory in some cases, as
> we only strbuf_init() the string in get_stash_info().
> 
> So while it's the smallest possible change, let's convert all users of
> this pattern in the file while we're at it.
> 
> A good follow-up to this change would be to change all the "ret = -1;
> goto done;" in this file to instead use a "goto cleanup", and
> initialize "int ret = -1" at the start of the relevant functions. That
> would allow us to drop a lot of needless brace verbosity on two-line
> "if" statements, but let's leave that alone for now.

You seem to have made this change here.

>[...]
> @@ -861,10 +863,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)
>   			strvec_push(&revision_args, argv[i]);
>   	}
>   
> -	ret = get_stash_info(&info, stash_args.nr, stash_args.v);
> -	strvec_clear(&stash_args);
> -	if (ret)
> -		return -1;
> +	if (get_stash_info(&info, stash_args.nr, stash_args.v))
> +		goto cleanup;
>   
>   	/*
>   	 * The config settings are applied only if there are not passed
> @@ -878,8 +878,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)
>   			rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
>   
>   		if (!show_stat && !show_patch) {
> -			free_stash_info(&info);
> -			return 0;
> +			ret = 0;
> +			goto cleanup;
>   		}
>   	}
>   
> @@ -912,8 +912,11 @@ static int show_stash(int argc, const char **argv, const char *prefix)
>   	}
>   	log_tree_diff_flush(&rev);
>   
> +	ret = diff_result_code(&rev.diffopt, 0);;
> +cleanup:
> +	strvec_clear(&stash_args);

This seems to be fixing a leak that's not mentioned in the commit message.

>   	free_stash_info(&info);
> -	return diff_result_code(&rev.diffopt, 0);
> +	return ret;
>   }
>[...]
> @@ -1434,7 +1438,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
>   			 int keep_index, int patch_mode, int include_untracked, int only_staged)
>   {
>   	int ret = 0;
> -	struct stash_info info;
> +	struct stash_info info = STASH_INFO_INIT;

There doesn't seem to be a call to free_stash_info() in this function.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v4 10/27] stash: always have the owner of "stash_info" free it
  2022-03-31  1:11       ` [PATCH v4 10/27] stash: always have the owner of "stash_info" free it Ævar Arnfjörð Bjarmason
  2022-04-01 15:29         ` Phillip Wood
@ 2022-04-01 15:33         ` Phillip Wood
  1 sibling, 0 replies; 252+ messages in thread
From: Phillip Wood @ 2022-04-01 15:33 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson


Hi Ævar

On 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:
> Change the initialization of the "revision" member of "struct
> stash_info" to be initialized vi a macro, and more importantly that
> that initializing function be tasked to free it, usually via a "goto
> cleanup" pattern.
> 
> Despite the "revision" name (and the topic of the series containing
> this commit) the "stash info" has nothing to do with the "struct
> rev_info". I'm making this change because in the subsequent commit
> when we do want to free the "struct rev_info" via a "goto cleanup"
> pattern we'd otherwise free() uninitialized memory in some cases, as
> we only strbuf_init() the string in get_stash_info().
> 
> So while it's the smallest possible change, let's convert all users of
> this pattern in the file while we're at it.
> 
> A good follow-up to this change would be to change all the "ret = -1;
> goto done;" in this file to instead use a "goto cleanup", and
> initialize "int ret = -1" at the start of the relevant functions. That
> would allow us to drop a lot of needless brace verbosity on two-line
> "if" statements, but let's leave that alone for now.
>[...]   
> @@ -912,8 +912,11 @@ static int show_stash(int argc, const char **argv, const char *prefix)
>   	}
>   	log_tree_diff_flush(&rev);
>   
> +	ret = diff_result_code(&rev.diffopt, 0);;

Extra ';'

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v4 05/27] revision.[ch]: split freeing of revs->commit into a function
  2022-04-01 15:15         ` Phillip Wood
@ 2022-04-01 17:03           ` Junio C Hamano
  0 siblings, 0 replies; 252+ messages in thread
From: Junio C Hamano @ 2022-04-01 17:03 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Ævar Arnfjörð Bjarmason, git, Martin Ågren,
	Elijah Newren, Derrick Stolee, brian m . carlson

Phillip Wood <phillip.wood123@gmail.com> writes:

>>   +static void release_revisions_commit_list(struct rev_info *revs)
>> +{
>> +	if (revs->commits) {
>
> This guard copied for the original is not needed, free_commit_list()
> will handle NULL just fine.

Thanks for a careful reading.

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v4 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids"
  2022-04-01 15:13         ` Phillip Wood
@ 2022-04-01 17:16           ` Ævar Arnfjörð Bjarmason
  2022-04-01 19:32             ` Junio C Hamano
  0 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-01 17:16 UTC (permalink / raw)
  To: Phillip Wood
  Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren,
	Derrick Stolee, brian m . carlson


On Fri, Apr 01 2022, Phillip Wood wrote:

> Hi Ævar
>
> On 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:
>> Fix two memory leaks in "struct rev_info" by freeing that memory in
>> cmd_format_patch(). These two are unusual special-cases in being in
>> the "struct rev_info", but not being "owned" by the code in
>> revision.c. I.e. they're members of the struct so that this code in
>> "builtin/log.c" can pass information code in log-tree.c.
>
> I'm not sure that I necessarily agree that these are owned by
> builtin/log.c. For rev.extra_headers it is set in builtin/log.c but 
> never used there which makes me think we are transferring ownership to
> struct rev_info. For ref_message_ids it is less clear cut but having
> it owned by struct rev_info and freeing it in release_revisions()
> would make things clearer I think. Having some members owned by struct 
> rev_info but others allocated and freed by other code is confusing and
> is likely to lead to memory errors. I don't think struct rev_info is 
> borrowing a reference to these items as they are being allocated for
> it's exclusive use.

This really isn't for the use of revision.[ch] at all, but just
something that's used as ad-hoc message passing between builtin/log.c
and log-tree.c. It just so happens that it's done by ferrying it via the
struct rev_info.

The below patch fails tests, it's just a quick (and obviously flawed)
search/replacement that I hacked up to get the removal of these from
revision.h to compile, which shows that it's just something between
log.c and log-tree.c, pretty much.

Anyway, while I'm 100% in agreement with you that this *should* be fixed
I'd really like to do the bare minimum to address leaks in this initial
iteration.

I.e. you're right that this relatively fragile, and I'm also concern
about catching memory errors.

But being able to run (most of) the format-patch tests tests as a result
of this more isolated leak will leave us much better position to
validate any subsequent larger fixes, such as (something like) the
below.

diff --git a/builtin/log.c b/builtin/log.c
index c211d66d1d0..dc1acd730d1 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1192,7 +1192,8 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
 			      struct commit *origin,
 			      int nr, struct commit **list,
 			      const char *branch_name,
-			      int quiet)
+			      int quiet, const char *extra_headers,
+			      struct string_list *ref_message_ids)
 {
 	const char *committer;
 	struct shortlog log;
@@ -1212,7 +1213,9 @@ static void make_cover_letter(struct rev_info *rev, int use_separate_file,
 	    open_next_file(NULL, rev->numbered_files ? NULL : "cover-letter", rev, quiet))
 		die(_("failed to create cover-letter file"));
 
-	log_write_email_headers(rev, head, &pp.after_subject, &need_8bit_cte, 0);
+	log_write_email_headers(rev, head, pp.after_subject, ref_message_ids,
+				&need_8bit_cte, 0);
+	pp.after_subject = extra_headers;
 
 	for (i = 0; !need_8bit_cte && i < nr; i++) {
 		const char *buf = get_commit_buffer(list[i], NULL);
@@ -1777,6 +1780,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	struct strbuf rdiff2 = STRBUF_INIT;
 	struct strbuf rdiff_title = STRBUF_INIT;
 	int creation_factor = -1;
+	char *extra_headers;
+	struct string_list *ref_message_ids;
 
 	const struct option builtin_format_patch_options[] = {
 		OPT_CALLBACK_F('n', "numbered", &numbered, NULL,
@@ -1946,7 +1951,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		strbuf_addch(&buf, '\n');
 	}
 
-	rev.extra_headers = strbuf_detach(&buf, NULL);
+	extra_headers = strbuf_detach(&buf, NULL);
 
 	if (from) {
 		if (split_ident_line(&rev.from_ident, from, strlen(from)))
@@ -2174,10 +2179,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	}
 
 	if (in_reply_to || thread || cover_letter)
-		rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
+		ref_message_ids = xcalloc(1, sizeof(struct string_list));
 	if (in_reply_to) {
 		const char *msgid = clean_message_id(in_reply_to);
-		string_list_append(rev.ref_message_ids, msgid);
+		string_list_append(ref_message_ids, msgid);
 	}
 	rev.numbered_files = just_numbers;
 	rev.patch_suffix = fmt_patch_suffix;
@@ -2185,7 +2190,8 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		if (thread)
 			gen_message_id(&rev, "cover");
 		make_cover_letter(&rev, !!output_directory,
-				  origin, nr, list, branch_name, quiet);
+				  origin, nr, list, branch_name, quiet,
+				  extra_headers, ref_message_ids);
 		print_bases(&bases, rev.diffopt.file);
 		print_signature(rev.diffopt.file);
 		total++;
@@ -2229,11 +2235,11 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 				 * --in-reply-to, if specified.
 				 */
 				if (thread == THREAD_SHALLOW
-				    && rev.ref_message_ids->nr > 0
+				    && ref_message_ids->nr > 0
 				    && (!cover_letter || rev.nr > 1))
 					free(rev.message_id);
 				else
-					string_list_append(rev.ref_message_ids,
+					string_list_append(ref_message_ids,
 							   rev.message_id);
 			}
 			gen_message_id(&rev, oid_to_hex(&commit->object.oid));
diff --git a/log-tree.c b/log-tree.c
index 38e5cccc1a1..66170b75254 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -419,11 +419,11 @@ void fmt_output_email_subject(struct strbuf *sb, struct rev_info *opt)
 }
 
 void log_write_email_headers(struct rev_info *opt, struct commit *commit,
-			     const char **extra_headers_p,
+			     const char *extra_headers,
+			     struct string_list *ref_message_ids,
 			     int *need_8bit_cte_p,
 			     int maybe_multipart)
 {
-	const char *extra_headers = opt->extra_headers;
 	const char *name = oid_to_hex(opt->zero_commit ?
 				      null_oid() : &commit->object.oid);
 
@@ -435,13 +435,13 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
 		fprintf(opt->diffopt.file, "Message-Id: <%s>\n", opt->message_id);
 		graph_show_oneline(opt->graph);
 	}
-	if (opt->ref_message_ids && opt->ref_message_ids->nr > 0) {
+	if (ref_message_ids && ref_message_ids->nr > 0) {
 		int i, n;
-		n = opt->ref_message_ids->nr;
-		fprintf(opt->diffopt.file, "In-Reply-To: <%s>\n", opt->ref_message_ids->items[n-1].string);
+		n = ref_message_ids->nr;
+		fprintf(opt->diffopt.file, "In-Reply-To: <%s>\n", ref_message_ids->items[n-1].string);
 		for (i = 0; i < n; i++)
 			fprintf(opt->diffopt.file, "%s<%s>\n", (i > 0 ? "\t" : "References: "),
-			       opt->ref_message_ids->items[i].string);
+			       ref_message_ids->items[i].string);
 		graph_show_oneline(opt->graph);
 	}
 	if (opt->mime_boundary && maybe_multipart) {
@@ -488,7 +488,6 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
 		opt->diffopt.stat_sep = buffer.buf;
 		strbuf_release(&filename);
 	}
-	*extra_headers_p = extra_headers;
 }
 
 static void show_sig_lines(struct rev_info *opt, int status, const char *bol)
@@ -621,13 +620,12 @@ static void next_commentary_block(struct rev_info *opt, struct strbuf *sb)
 	opt->shown_dashes = 1;
 }
 
-void show_log(struct rev_info *opt)
+void show_log_extra_headers(struct rev_info *opt, const char *extra_headers)
 {
 	struct strbuf msgbuf = STRBUF_INIT;
 	struct log_info *log = opt->loginfo;
 	struct commit *commit = log->commit, *parent = log->parent;
 	int abbrev_commit = opt->abbrev_commit ? opt->abbrev : the_hash_algo->hexsz;
-	const char *extra_headers = opt->extra_headers;
 	struct pretty_print_context ctx = {0};
 
 	opt->loginfo = NULL;
@@ -687,7 +685,7 @@ void show_log(struct rev_info *opt)
 	 */
 
 	if (cmit_fmt_is_mail(opt->commit_format)) {
-		log_write_email_headers(opt, commit, &extra_headers,
+		log_write_email_headers(opt, commit, extra_headers, NULL,
 					&ctx.need_8bit_cte, 1);
 		ctx.rev = opt;
 		ctx.print_email_subject = 1;
@@ -848,6 +846,11 @@ void show_log(struct rev_info *opt)
 	}
 }
 
+void show_log(struct rev_info *opt)
+{
+	show_log_extra_headers(opt, NULL);
+}
+
 int log_tree_diff_flush(struct rev_info *opt)
 {
 	opt->shown_dashes = 0;
diff --git a/log-tree.h b/log-tree.h
index e7e4641cf83..32ae026f6fb 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -16,6 +16,7 @@ struct decoration_filter {
 int parse_decorate_color_config(const char *var, const char *slot_name, const char *value);
 int log_tree_diff_flush(struct rev_info *);
 int log_tree_commit(struct rev_info *, struct commit *);
+void show_log_extra_headers(struct rev_info *opt, const char *extra_headers);
 void show_log(struct rev_info *opt);
 void format_decorations_extended(struct strbuf *sb, const struct commit *commit,
 			     int use_color,
@@ -26,7 +27,8 @@ void format_decorations_extended(struct strbuf *sb, const struct commit *commit,
 			     format_decorations_extended((strbuf), (commit), (color), " (", ", ", ")")
 void show_decorations(struct rev_info *opt, struct commit *commit);
 void log_write_email_headers(struct rev_info *opt, struct commit *commit,
-			     const char **extra_headers_p,
+			     const char *extra_headers,
+			     struct string_list *ref_message_ids,
 			     int *need_8bit_cte_p,
 			     int maybe_multipart);
 void load_ref_decorations(struct decoration_filter *filter, int flags);
diff --git a/revision.h b/revision.h
index 5bc59c7bfe1..e2e08673bba 100644
--- a/revision.h
+++ b/revision.h
@@ -243,9 +243,7 @@ struct rev_info {
 	const char	*reroll_count;
 	char		*message_id;
 	struct ident_split from_ident;
-	struct string_list *ref_message_ids;
 	int		add_signoff;
-	const char	*extra_headers;
 	const char	*log_reencode;
 	const char	*subject_prefix;
 	int		patch_name_max;

^ permalink raw reply related	[flat|nested] 252+ messages in thread

* Re: [PATCH v4 06/27] revision.[ch]: provide and start using a release_revisions()
  2022-04-01 15:17         ` Phillip Wood
@ 2022-04-01 17:25           ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-01 17:25 UTC (permalink / raw)
  To: Phillip Wood
  Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren,
	Derrick Stolee, brian m . carlson


On Fri, Apr 01 2022, Phillip Wood wrote:

> Hi Ævar
>
> On 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:
>> The users of the revision.[ch] API's "struct rev_info" are a major
>> source of memory leaks in the test suite under SANITIZE=leak, which in
>> turn adds a lot of noise when trying to mark up tests with
>> "TEST_PASSES_SANITIZE_LEAK=true".
>> The users of that API are largely one-shot, e.g. "git log", so
>> arguably freeing the memory is a waste of time, but in many cases
>> they've actually been trying to free the memory, and just doing that
>> in a buggy manner.
>
> I've not looked that closely at these changes but builtin/log.c is not
> modified so I'm a bit confused by the mention of "git log" above.

Along with git-rev-list it's the main user-facing interface to
revision.c. I'll clarify that in a re-roll.

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v4 10/27] stash: always have the owner of "stash_info" free it
  2022-04-01 15:29         ` Phillip Wood
@ 2022-04-01 17:29           ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-01 17:29 UTC (permalink / raw)
  To: Phillip Wood
  Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren,
	Derrick Stolee, brian m . carlson


On Fri, Apr 01 2022, Phillip Wood wrote:

> Hi Ævar
>
> On 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:
>> Change the initialization of the "revision" member of "struct
>> stash_info" to be initialized vi a macro, and more importantly that
>> that initializing function be tasked to free it, usually via a "goto
>> cleanup" pattern.
>> Despite the "revision" name (and the topic of the series containing
>> this commit) the "stash info" has nothing to do with the "struct
>> rev_info". I'm making this change because in the subsequent commit
>> when we do want to free the "struct rev_info" via a "goto cleanup"
>> pattern we'd otherwise free() uninitialized memory in some cases, as
>> we only strbuf_init() the string in get_stash_info().
>> So while it's the smallest possible change, let's convert all users
>> of
>> this pattern in the file while we're at it.
>> A good follow-up to this change would be to change all the "ret =
>> -1;
>> goto done;" in this file to instead use a "goto cleanup", and
>> initialize "int ret = -1" at the start of the relevant functions. That
>> would allow us to drop a lot of needless brace verbosity on two-line
>> "if" statements, but let's leave that alone for now.
>
> You seem to have made this change here.
>
>>[...]
>> @@ -861,10 +863,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)
>>   			strvec_push(&revision_args, argv[i]);
>>   	}
>>   -	ret = get_stash_info(&info, stash_args.nr, stash_args.v);
>> -	strvec_clear(&stash_args);
>> -	if (ret)
>> -		return -1;
>> +	if (get_stash_info(&info, stash_args.nr, stash_args.v))
>> +		goto cleanup;
>>     	/*
>>   	 * The config settings are applied only if there are not passed
>> @@ -878,8 +878,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)
>>   			rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
>>     		if (!show_stat && !show_patch) {
>> -			free_stash_info(&info);
>> -			return 0;
>> +			ret = 0;
>> +			goto cleanup;
>>   		}
>>   	}
>>   @@ -912,8 +912,11 @@ static int show_stash(int argc, const char
>> **argv, const char *prefix)
>>   	}
>>   	log_tree_diff_flush(&rev);
>>   +	ret = diff_result_code(&rev.diffopt, 0);;
>> +cleanup:
>> +	strvec_clear(&stash_args);
>
> This seems to be fixing a leak that's not mentioned in the commit message.

This is just moving the exsting strvec_clear() shown above to the
"cleanup" branch, as we change return to "goto cleanup".

>>   	free_stash_info(&info);
>> -	return diff_result_code(&rev.diffopt, 0);
>> +	return ret;
>>   }
>>[...]
>> @@ -1434,7 +1438,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
>>   			 int keep_index, int patch_mode, int include_untracked, int only_staged)
>>   {
>>   	int ret = 0;
>> -	struct stash_info info;
>> +	struct stash_info info = STASH_INFO_INIT;
>
> There doesn't seem to be a call to free_stash_info() in this function.

Hrm, I think you're right about that (but it was an existing leak). Will
fix.

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v4 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids"
  2022-04-01 17:16           ` Ævar Arnfjörð Bjarmason
@ 2022-04-01 19:32             ` Junio C Hamano
  0 siblings, 0 replies; 252+ messages in thread
From: Junio C Hamano @ 2022-04-01 19:32 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: Phillip Wood, git, Martin Ågren, Elijah Newren,
	Derrick Stolee, brian m . carlson

Ævar Arnfjörð Bjarmason <avarab@gmail.com> writes:

> The below patch fails tests, it's just a quick (and obviously flawed)
> search/replacement that I hacked up to get the removal of these from
> revision.h to compile, which shows that it's just something between
> log.c and log-tree.c, pretty much.

Given that the way most end-users see log-tree.c in action is via
log.c (is there a useful entry point into log-tree that bypasses
log.c that implements end-user actions?), I would actually think it
is that extra_headers is quite an integral part of rev_info
structure, which is how the revision traversal API passes
information relevant to the current traversal around various layors
of the implementation.  So, I am not sure what the above experiment
shows us.

> Anyway, while I'm 100% in agreement with you that this *should* be fixed
> I'd really like to do the bare minimum to address leaks in this initial
> iteration.
>
> I.e. you're right that this relatively fragile, and I'm also concern
> about catching memory errors.

As long as we know there are yet more work to do (are we leaving
NEEDSWORK comments as we go, by the way?), I think that's fine.  We
cannot go all the way in one go for a large code base like revisions
API, and we can leave a small corner "not quite optimial", and we can
even leave it in "sometimes still leaks" status, as long as we never
make anything to free too much by mistake.

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v4 13/27] revisions API users: use release_revisions() in builtin/log.c
  2022-03-31  1:11       ` [PATCH v4 13/27] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
@ 2022-04-02  9:22         ` Phillip Wood
  2022-04-03 14:07           ` Ævar Arnfjörð Bjarmason
  2022-04-03 21:49           ` Junio C Hamano
  0 siblings, 2 replies; 252+ messages in thread
From: Phillip Wood @ 2022-04-02  9:22 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Hi Ævar

On 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:
> In preparation for having the "log" family of functions make wider use
> of release_revisions() let's have them call it just before
> exiting. This changes the "log", "whatchanged", "show",
> "format-patch", etc. commands, all of which live in this file.
> 
> The release_revisions() API still only frees the "pending" member, but
> will learn to release more members of "struct rev_info" in subsequent
> commits.
> 
> In the case of "format-patch" revert the addition of UNLEAK() in
> dee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16),
> which will cause several tests that previously passed under
> "TEST_PASSES_SANITIZE_LEAK=true" to start failing.
> 
> In subsequent commits we'll now be able to use those tests to check
> whether that part of the API is really leaking memory, and will fix
> all of those memory leaks. Removing the UNLEAK() allows us to make
> incremental progress in that direction. See [1] for further details
> about this approach.

This breaks "git bisect" but only when running the test suite to detect 
leaks so I guess that's not too bad. An alternative would be to manually 
remove the UNLEAK() when you're testing rather than committing the change.

> Note that the release_revisions() will not be sufficient to deal with
> the code in cmd_show() added in 5d7eeee2ac6 (git-show: grok blobs,
> trees and tags, too, 2006-12-14) which clobbers the "pending" array in
> the case of "OBJ_COMMIT". That will need to be dealt with by some
> future follow-up work.
> 
> 1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/
> 
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>   builtin/log.c          | 20 ++++++++++++--------
>   t/t4126-apply-empty.sh |  2 --
>   2 files changed, 12 insertions(+), 10 deletions(-)
> 
> diff --git a/builtin/log.c b/builtin/log.c
> index 6f9928fabfe..c40ebe1c3f4 100644
> --- a/builtin/log.c
> +++ b/builtin/log.c
> @@ -295,6 +295,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
>   	cmd_log_init_finish(argc, argv, prefix, rev, opt);
>   }
>   
> +static int cmd_log_deinit(int ret, struct rev_info *rev)
> +{
> +	release_revisions(rev);
> +	return ret;
> +}


>   /*
>    * This gives a rough estimate for how many commits we
>    * will print out in the list.
> @@ -558,7 +564,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
>   	cmd_log_init(argc, argv, prefix, &rev, &opt);
>   	if (!rev.diffopt.output_format)
>   		rev.diffopt.output_format = DIFF_FORMAT_RAW;
> -	return cmd_log_walk(&rev);
> +	return cmd_log_deinit(cmd_log_walk(&rev), &rev);

This is a rather unusual pattern, at first I wondered if there were 
going to be more added to the body of cmd_log_deinit() in later commits 
but there isn't so why not just call release_revisions() here to be 
consistent with the other release_revisions() call that are added in 
other patches?


Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 252+ messages in thread

* [PATCH v5 00/27] revision.[ch]: add and use release_revisions()
  2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                         ` (26 preceding siblings ...)
  2022-03-31  1:11       ` [PATCH v4 27/27] revisions API: add a TODO for diff_free(&revs->diffopt) Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49       ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
                           ` (28 more replies)
  27 siblings, 29 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

This series reduces the memory leaks spotted by our tests by at least
half, see v3[1] for a more general overview.

Thanks a lot to Phillip Wood and Junio for the v4 review! I think this
v5 should address all of the things brought up in v4 in one way or
another, changes:

 * The "extra_headers" or "ref_message_ids" freeing is the same, but
   the commit message is altered to explain that commit.

 * The "revision.[ch]: split freeing of revs->commit into a function"
   is gone, instead I applied a coccinelle rule to free_commit_list()
   as suggested by Phillip Wood (well, the change in pattern, not the
   rule was the suggestion).

 * The release_revisions() is now simply using free_commit_list()
   without a NULL assignment to the member, and I changed some
   FREE_AND_NULL() that I'd added (for e.g. cmdline) to just free().

   The "and NULL" was just cargo-culted from elswhere, but for
   release_revisions() we not only aren't using the struct again (it's
   not a "reset"), but it it wouldn't have worked as a reset before
   anyway (some things free'd without NULL-ing).

   We could trivially have a "reset" variant of release_revisions(),
   but since no current API user wanted it let's leave it, leving the
   memory uninitialized will help to catch any invalid API usage.

 * I missed a object_array_clear() in builtin/stash.c that
   could/should be free'd by release_revisions().

 * An obscure memory leak in stash.c (on "master") around
   assert_stash_ref() has been fixed.

 * Add a "goto usage" pattern for a memory leak new in stash.c that
   this series introduced, i.e. because strvec_clear() was moved to a
   "cleanup" block if we invoked usage_with_options() between the two
   we'd exit without the cleanup. Now we'll cleanup on "usage" too.

 * Updated commit message & docs around REV_INFO_INIT, hopefully
   addressing the thread left off at
   https://lore.kernel.org/git/220401.86h77dnkpw.gmgdl@evledraar.gmail.com/

1. https://lore.kernel.org/git/cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com/

Ævar Arnfjörð Bjarmason (27):
  t/helper/test-fast-rebase.c: don't leak "struct strbuf"
  blame: use "goto cleanup" for cleanup_scoreboard()
  string_list API users: use string_list_init_{no,}dup
  format-patch: don't leak "extra_headers" or "ref_message_ids"
  cocci: add and apply free_commit_list() rules
  revision.[ch]: provide and start using a release_revisions()
  revisions API users: add straightforward release_revisions()
  revision.[ch]: document and move code declared around "init"
  revisions API users: use release_revisions() needing REV_INFO_INIT
  stash: always have the owner of "stash_info" free it
  revisions API users: add "goto cleanup" for release_revisions()
  revisions API users: use release_revisions() in http-push.c
  revisions API users: use release_revisions() in builtin/log.c
  revisions API users: use release_revisions() with UNLEAK()
  revisions API users: use release_revisions() for "prune_data" users
  revisions API: have release_revisions() release "commits"
  revisions API: have release_revisions() release "mailmap"
  revisions API: have release_revisions() release "cmdline"
  revisions API: have release_revisions() release "filter"
  revisions API: have release_revisions() release "grep_filter"
  revisions API: have release_revisions() release "prune_data"
  revisions API: clear "boundary_commits" in release_revisions()
  revisions API: release "reflog_info" in release revisions()
  revisions API: call diff_free(&revs->pruning) in revisions_release()
  revisions API: have release_revisions() release "date_mode"
  revisions API: have release_revisions() release "topo_walk_info"
  revisions API: add a TODO for diff_free(&revs->diffopt)

 add-interactive.c                          |   3 +-
 bisect.c                                   |  20 ++--
 builtin/add.c                              |   3 +-
 builtin/am.c                               |   3 +
 builtin/bisect--helper.c                   |   2 +
 builtin/blame.c                            |   4 +-
 builtin/checkout.c                         |   3 +-
 builtin/commit.c                           |   6 +-
 builtin/describe.c                         |   2 +
 builtin/diff-files.c                       |   8 +-
 builtin/diff-index.c                       |   5 +-
 builtin/diff.c                             |   2 +-
 builtin/fast-export.c                      |   1 +
 builtin/log.c                              |  36 ++++---
 builtin/merge.c                            |   2 +
 builtin/pack-objects.c                     |   2 +
 builtin/prune.c                            |   1 +
 builtin/reflog.c                           |   1 +
 builtin/rev-list.c                         |  25 +++--
 builtin/shortlog.c                         |   8 +-
 builtin/stash.c                            | 115 +++++++++++----------
 builtin/submodule--helper.c                |  14 ++-
 bundle.c                                   |  12 ++-
 commit.c                                   |  19 ++--
 contrib/coccinelle/free.cocci              |  27 +++++
 diff-lib.c                                 |   8 +-
 fmt-merge-msg.c                            |   1 +
 http-push.c                                |   3 +-
 merge-ort.c                                |   1 +
 merge-recursive.c                          |   5 +-
 midx.c                                     |   1 +
 pack-bitmap-write.c                        |   1 +
 range-diff.c                               |   2 +-
 ref-filter.c                               |   1 +
 reflog-walk.c                              |  24 ++++-
 reflog-walk.h                              |   1 +
 remote.c                                   |   1 +
 revision.c                                 |  72 ++++++++++---
 revision.h                                 |  73 ++++++++-----
 sequencer.c                                |  26 +++--
 shallow.c                                  |   1 +
 submodule.c                                |  11 +-
 t/helper/test-fast-rebase.c                |  23 +++--
 t/helper/test-revision-walking.c           |   1 +
 t/lib-git-svn.sh                           |   4 +
 t/t0056-git-C.sh                           |   1 +
 t/t0062-revision-walking.sh                |   1 +
 t/t0100-previous.sh                        |   1 +
 t/t0101-at-syntax.sh                       |   2 +
 t/t1001-read-tree-m-2way.sh                |   1 +
 t/t1002-read-tree-m-u-2way.sh              |   1 +
 t/t1060-object-corruption.sh               |   2 +
 t/t1401-symbolic-ref.sh                    |   2 +
 t/t1411-reflog-show.sh                     |   1 +
 t/t1412-reflog-loop.sh                     |   2 +
 t/t1415-worktree-refs.sh                   |   1 +
 t/t2015-checkout-unborn.sh                 |   1 +
 t/t2200-add-update.sh                      |   1 +
 t/t3302-notes-index-expensive.sh           |   1 +
 t/t3303-notes-subtrees.sh                  |   1 +
 t/t3305-notes-fanout.sh                    |   1 +
 t/t3408-rebase-multi-line.sh               |   1 +
 t/t4021-format-patch-numbered.sh           |   1 +
 t/t4027-diff-submodule.sh                  |   1 +
 t/t4028-format-patch-mime-headers.sh       |   2 +
 t/t4036-format-patch-signer-mime.sh        |   1 +
 t/t4039-diff-assume-unchanged.sh           |   1 +
 t/t4055-diff-context.sh                    |   1 +
 t/t4066-diff-emit-delay.sh                 |   1 +
 t/t4122-apply-symlink-inside.sh            |   1 +
 t/t4126-apply-empty.sh                     |   1 -
 t/t4128-apply-root.sh                      |   1 +
 t/t4206-log-follow-harder-copies.sh        |   2 +
 t/t4207-log-decoration-colors.sh           |   1 +
 t/t4212-log-corrupt.sh                     |   1 +
 t/t5301-sliding-window.sh                  |   2 +
 t/t5313-pack-bounds-checks.sh              |   2 +
 t/t5316-pack-delta-depth.sh                |   2 +
 t/t5320-delta-islands.sh                   |   2 +
 t/t5322-pack-objects-sparse.sh             |   1 +
 t/t5506-remote-groups.sh                   |   1 +
 t/t5513-fetch-track.sh                     |   1 +
 t/t5515-fetch-merge-logic.sh               |   1 +
 t/t5518-fetch-exit-status.sh               |   1 +
 t/t5532-fetch-proxy.sh                     |   2 +
 t/t5600-clone-fail-cleanup.sh              |   1 +
 t/t5900-repo-selection.sh                  |   2 +
 t/t6002-rev-list-bisect.sh                 |   1 +
 t/t6003-rev-list-topo-order.sh             |   1 +
 t/t6005-rev-list-count.sh                  |   1 +
 t/t6018-rev-list-glob.sh                   |   1 +
 t/t6100-rev-list-in-order.sh               |   1 +
 t/t6101-rev-parse-parents.sh               |   1 +
 t/t6110-rev-list-sparse.sh                 |   1 +
 t/t6114-keep-packs.sh                      |   2 +
 t/t6131-pathspec-icase.sh                  |   2 +
 t/t7008-filter-branch-null-sha1.sh         |   1 +
 t/t7702-repack-cyclic-alternate.sh         |   2 +
 t/t9001-send-email.sh                      |   1 +
 t/t9100-git-svn-basic.sh                   |   1 +
 t/t9101-git-svn-props.sh                   |   2 +
 t/t9104-git-svn-follow-parent.sh           |   2 +
 t/t9106-git-svn-commit-diff-clobber.sh     |   2 +
 t/t9115-git-svn-dcommit-funky-renames.sh   |   1 +
 t/t9116-git-svn-log.sh                     |   1 +
 t/t9122-git-svn-author.sh                  |   2 +
 t/t9127-git-svn-partial-rebuild.sh         |   1 +
 t/t9129-git-svn-i18n-commitencoding.sh     |   1 +
 t/t9132-git-svn-broken-symlink.sh          |   1 +
 t/t9139-git-svn-non-utf8-commitencoding.sh |   1 +
 t/t9146-git-svn-empty-dirs.sh              |   2 +
 t/t9148-git-svn-propset.sh                 |   1 +
 t/t9160-git-svn-preserve-empty-dirs.sh     |   1 +
 t/t9162-git-svn-dcommit-interactive.sh     |   2 +
 t/t9164-git-svn-dcommit-concurrent.sh      |   2 +
 t/t9501-gitweb-standalone-http-status.sh   |   1 +
 wt-status.c                                |  15 +--
 117 files changed, 502 insertions(+), 190 deletions(-)

Range-diff against v4:
 1:  b99a07f98ee =  1:  e20f951a64c t/helper/test-fast-rebase.c: don't leak "struct strbuf"
 2:  da5be507c0f =  2:  de2e08a14e6 blame: use "goto cleanup" for cleanup_scoreboard()
 3:  243f7c8df96 =  3:  364aa7d8990 string_list API users: use string_list_init_{no,}dup
 4:  69f0aabe38f !  4:  5d4ad92d47b format-patch: don't leak "extra_headers" or "ref_message_ids"
    @@ Commit message
         cmd_format_patch(). These two are unusual special-cases in being in
         the "struct rev_info", but not being "owned" by the code in
         revision.c. I.e. they're members of the struct so that this code in
    -    "builtin/log.c" can pass information code in log-tree.c.
    +    "builtin/log.c" can conveniently pass information code in
    +    "log-tree.c".
    +
    +    See e.g. the make_cover_letter() caller of log_write_email_headers()
    +    here in "builtin/log.c", and [1] for a demonstration of where the
    +    "extra_headers" and "ref_message_ids" struct members are used.
     
         See 20ff06805c6 (format-patch: resurrect extra headers from config,
         2006-06-02) and d1566f7883f (git-format-patch: Make the second and
    @@ Commit message
         variable to hold it, to avoid the eventual cast from "const char *"
         when we free() it.
     
    +    1. https://lore.kernel.org/git/220401.868rsoogxf.gmgdl@evledraar.gmail.com/
    +
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/log.c ##
 5:  2d0ed57ec2e <  -:  ----------- revision.[ch]: split freeing of revs->commit into a function
 -:  ----------- >  5:  c38db28f920 cocci: add and apply free_commit_list() rules
 6:  b9beaba16bf !  6:  b99f96599bb revision.[ch]: provide and start using a release_revisions()
    @@ Commit message
         turn adds a lot of noise when trying to mark up tests with
         "TEST_PASSES_SANITIZE_LEAK=true".
     
    -    The users of that API are largely one-shot, e.g. "git log", so
    -    arguably freeing the memory is a waste of time, but in many cases
    -    they've actually been trying to free the memory, and just doing that
    -    in a buggy manner.
    +    The users of that API are largely one-shot, e.g. "git rev-list" or
    +    "git log", or the "git checkout" and "git stash" being modified here
    +
    +    For these callers freeing the memory is arguably a waste of time, but
    +    in many cases they've actually been trying to free the memory, and
    +    just doing that in a buggy manner.
     
         Let's provide a release_revisions() function for these users, and
         start migrating them over per the plan outlined in [1]. Right now this
    @@ builtin/checkout.c: static void show_local_changes(struct object *head,
      static void describe_detached_head(const char *msg, struct commit *commit)
     
      ## builtin/stash.c ##
    +@@ builtin/stash.c: static int check_changes_tracked_files(const struct pathspec *ps)
    + 		goto done;
    + 	}
    + 
    +-	object_array_clear(&rev.pending);
    + 	result = run_diff_files(&rev, 0);
    + 	if (diff_result_code(&rev.diffopt, result)) {
    + 		ret = 1;
    +@@ builtin/stash.c: static int check_changes_tracked_files(const struct pathspec *ps)
    + 
    + done:
    + 	clear_pathspec(&rev.prune_data);
    ++	release_revisions(&rev);
    + 	return ret;
    + }
    + 
     @@ builtin/stash.c: static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
      
      done:
    @@ range-diff.c: int is_range_diff_range(const char *arg)
      }
     
      ## revision.c ##
    -@@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)
    - 	}
    +@@ revision.c: int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
    + 	return left;
      }
      
     +void release_revisions(struct rev_info *revs)
 7:  80ec9a131c0 =  7:  521f6967b64 revisions API users: add straightforward release_revisions()
 8:  086cec742b4 !  8:  003b507e0b7 revision.[ch]: document and move code declared around "init"
    @@ revision.h: void mark_trees_uninteresting_sparse(struct repository *r, struct oi
      void show_object_with_name(FILE *, struct object *, const char *);
      
     +/**
    -+ * Helpers to check if a "struct string_list" item matches wild
    ++ * Helpers to check if a "struct string_list" item matches with
     + * wildmatch().
     + */
     +int ref_excluded(struct string_list *, const char *path);
 9:  2f4e65fb534 !  9:  240307bc3c6 revisions API users: use release_revisions() needing REV_INFO_INIT
    @@ Commit message
         need to have their "struct rev_info" zero-initialized before we can
         start using it.
     
    -    To do this add a stub "REV_INFO_INIT" macro, ideally macro would be
    -    able to fully initialize a "struct rev_info", but all it does is the
    -    equivalent of assigning "{ 0 }" to the struct, the API user will still
    -    need to use repo_init_revisions(). In some future follow-up work we'll
    -    hopefully make REV_INFO_INIT be a "stand-alone" init likke STRBUF_INIT
    -    and other similar macros.
    -
         For the bundle.c code see the early exit case added in
         3bbbe467f29 (bundle verify: error out if called without an object
         database, 2019-05-27).
    @@ revision.h: struct rev_info {
     +/**
     + * Initialize the "struct rev_info" structure with a macro.
     + *
    -+ * This is not sufficient (yet!) to initialize a "struct rev_info",
    -+ * but it's OK (but redundant) to use it before a call to
    -+ * repo_init_revisions(), which does the real initialization. By using
    -+ * this it's safe to call release_revisions() on the "struct rev_info"
    -+ * without having called repo_init_revisions().
    ++ * This will not fully initialize a "struct rev_info", the
    ++ * repo_init_revisions() function needs to be called before
    ++ * setup_revisions() and any revision walking takes place.
    ++ *
    ++ * Use REV_INFO_INIT to make the "struct rev_info" safe for passing to
    ++ * release_revisions() when it's inconvenient (e.g. due to a "goto
    ++ * cleanup" pattern) to arrange for repo_init_revisions() to be called
    ++ * before release_revisions() is called.
    ++ *
    ++ * Initializing with this REV_INFO_INIT is redundant to invoking
    ++ * repo_init_revisions(). If repo_init_revisions() is guaranteed to be
    ++ * called before release_revisions() the "struct rev_info" can be left
    ++ * uninitialized.
     + */
     +#define REV_INFO_INIT { 0 }
     +
    @@ submodule.c: void show_submodule_diff_summary(struct diff_options *o, const char
      	struct commit_list *merge_bases = NULL;
      	struct repository *sub;
     @@ submodule.c: void show_submodule_diff_summary(struct diff_options *o, const char *path,
    - 	print_submodule_diff_summary(sub, &rev, o);
      
      out:
    + 	free_commit_list(merge_bases);
     +	release_revisions(&rev);
    - 	if (merge_bases)
    - 		free_commit_list(merge_bases);
      	clear_commit_marks(left, ~0);
    + 	clear_commit_marks(right, ~0);
    + 	if (sub) {
10:  145a0f74b6a ! 10:  4c5404912e9 stash: always have the owner of "stash_info" free it
    @@ Commit message
         pattern we'd otherwise free() uninitialized memory in some cases, as
         we only strbuf_init() the string in get_stash_info().
     
    -    So while it's the smallest possible change, let's convert all users of
    -    this pattern in the file while we're at it.
    +    So while it's not the smallest possible change, let's convert all
    +    users of this pattern in the file while we're at it.
     
         A good follow-up to this change would be to change all the "ret = -1;
         goto done;" in this file to instead use a "goto cleanup", and
    @@ Commit message
         would allow us to drop a lot of needless brace verbosity on two-line
         "if" statements, but let's leave that alone for now.
     
    +    To ensure that there's a 1=1 mapping between owners of the "struct
    +    stash_info" and free_stash_info() change the assert_stash_ref()
    +    function to be a trivial get_stash_info_assert() wrapper. The caller
    +    will call free_stash_info(), and by returning -1 we'll eventually (via
    +    !!ret) exit with status 1 anyway.
    +
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## builtin/stash.c ##
    @@ builtin/stash.c: static int apply_stash(int argc, const char **argv, const char
      	free_stash_info(&info);
      	return ret;
      }
    -@@ builtin/stash.c: static void assert_stash_ref(struct stash_info *info)
    +@@ builtin/stash.c: static int do_drop_stash(struct stash_info *info, int quiet)
    + 	return 0;
    + }
    + 
    +-static void assert_stash_ref(struct stash_info *info)
    ++static int get_stash_info_assert(struct stash_info *info, int argc,
    ++				 const char **argv)
    + {
    +-	if (!info->is_stash_ref) {
    +-		error(_("'%s' is not a stash reference"), info->revision.buf);
    +-		free_stash_info(info);
    +-		exit(1);
    +-	}
    ++	int ret = get_stash_info(info, argc, argv);
    ++
    ++	if (ret < 0)
    ++		return ret;
    ++
    ++	if (!info->is_stash_ref)
    ++		return error(_("'%s' is not a stash reference"), info->revision.buf);
    ++
    ++	return 0;
    + }
      
      static int drop_stash(int argc, const char **argv, const char *prefix)
      {
    @@ builtin/stash.c: static void assert_stash_ref(struct stash_info *info)
      		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
      		OPT_END()
     @@ builtin/stash.c: static int drop_stash(int argc, const char **argv, const char *prefix)
    + 	argc = parse_options(argc, argv, prefix, options,
      			     git_stash_drop_usage, 0);
      
    - 	if (get_stash_info(&info, argc, argv))
    +-	if (get_stash_info(&info, argc, argv))
     -		return -1;
    +-
    +-	assert_stash_ref(&info);
    ++	if (get_stash_info_assert(&info, argc, argv))
     +		goto cleanup;
      
    - 	assert_stash_ref(&info);
    - 
      	ret = do_drop_stash(&info, quiet);
     +cleanup:
      	free_stash_info(&info);
    @@ builtin/stash.c: static int drop_stash(int argc, const char **argv, const char *
      		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
      		OPT_BOOL(0, "index", &index,
     @@ builtin/stash.c: static int pop_stash(int argc, const char **argv, const char *prefix)
    + 	argc = parse_options(argc, argv, prefix, options,
      			     git_stash_pop_usage, 0);
      
    - 	if (get_stash_info(&info, argc, argv))
    +-	if (get_stash_info(&info, argc, argv))
     -		return -1;
    ++	if (get_stash_info_assert(&info, argc, argv))
     +		goto cleanup;
      
    - 	assert_stash_ref(&info);
    +-	assert_stash_ref(&info);
      	if ((ret = do_apply_stash(prefix, &info, index, quiet)))
    -@@ builtin/stash.c: static int pop_stash(int argc, const char **argv, const char *prefix)
    + 		printf_ln(_("The stash entry is kept in case "
    + 			    "you need it again."));
      	else
      		ret = do_drop_stash(&info, quiet);
      
    @@ builtin/stash.c: static void diff_include_untracked(const struct stash_info *inf
      	struct rev_info rev;
      	struct strvec stash_args = STRVEC_INIT;
      	struct strvec revision_args = STRVEC_INIT;
    +@@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *prefix)
    + 			      UNTRACKED_ONLY, PARSE_OPT_NONEG),
    + 		OPT_END()
    + 	};
    ++	int do_usage = 0;
    + 
    + 	init_diff_ui_defaults();
    + 	git_config(git_diff_ui_config, NULL);
     @@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *prefix)
      			strvec_push(&revision_args, argv[i]);
      	}
    @@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *
      		}
      	}
      
    + 	argc = setup_revisions(revision_args.nr, revision_args.v, &rev, NULL);
    +-	if (argc > 1) {
    +-		free_stash_info(&info);
    +-		usage_with_options(git_stash_show_usage, options);
    +-	}
    ++	if (argc > 1)
    ++		goto usage;
    + 	if (!rev.diffopt.output_format) {
    + 		rev.diffopt.output_format = DIFF_FORMAT_PATCH;
    + 		diff_setup_done(&rev.diffopt);
     @@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *prefix)
      	}
      	log_tree_diff_flush(&rev);
      
    -+	ret = diff_result_code(&rev.diffopt, 0);;
    ++	ret = diff_result_code(&rev.diffopt, 0);
     +cleanup:
     +	strvec_clear(&stash_args);
      	free_stash_info(&info);
     -	return diff_result_code(&rev.diffopt, 0);
    ++	if (do_usage)
    ++		usage_with_options(git_stash_show_usage, options);
     +	return ret;
    ++usage:
    ++	do_usage = 1;
    ++	goto cleanup;
      }
      
      static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,
    @@ builtin/stash.c: static int do_push_stash(const struct pathspec *ps, const char
      	struct strbuf patch = STRBUF_INIT;
      	struct strbuf stash_msg_buf = STRBUF_INIT;
      	struct strbuf untracked_files = STRBUF_INIT;
    +@@ builtin/stash.c: static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
    + 	}
    + 
    + done:
    ++	free_stash_info(&info);
    + 	strbuf_release(&stash_msg_buf);
    + 	return ret;
    + }
11:  c9898a5a96b ! 11:  4210232e3a1 revisions API users: add "goto cleanup" for release_revisions()
    @@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *
      	strvec_clear(&stash_args);
      	free_stash_info(&info);
     +	release_revisions(&rev);
    + 	if (do_usage)
    + 		usage_with_options(git_stash_show_usage, options);
      	return ret;
    - }
    - 
     
      ## builtin/submodule--helper.c ##
     @@ builtin/submodule--helper.c: static int compute_summary_module_list(struct object_id *head_oid,
12:  6e41d666223 = 12:  1918ca0a85d revisions API users: use release_revisions() in http-push.c
13:  02ca92660af = 13:  6a02b7aae4e revisions API users: use release_revisions() in builtin/log.c
14:  1aeb1127f34 = 14:  ddc7402b054 revisions API users: use release_revisions() with UNLEAK()
15:  c12b3d5a035 ! 15:  03e9bd73d4b revisions API users: use release_revisions() for "prune_data" users
    @@ builtin/add.c: int add_files_to_cache(const char *prefix,
      }
      
     
    - ## builtin/stash.c ##
    -@@ builtin/stash.c: static int check_changes_tracked_files(const struct pathspec *ps)
    - 
    - done:
    - 	clear_pathspec(&rev.prune_data);
    -+	release_revisions(&rev);
    - 	return ret;
    - }
    - 
    -
      ## diff-lib.c ##
     @@ diff-lib.c: int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
      	if (diff_cache(&revs, tree_oid, NULL, 1))
16:  2e351702677 <  -:  ----------- revisions API: have release_revisions() release "commits"
 -:  ----------- > 16:  285673e40bf revisions API: have release_revisions() release "commits"
17:  f67f1f5a575 ! 17:  ca659940ee5 revisions API: have release_revisions() release "mailmap"
    @@ builtin/commit.c: static const char *find_author_by_nickname(const char *name)
      	}
     
      ## revision.c ##
    -@@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)
    - 	}
    +@@ revision.c: int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
    + 	return left;
      }
      
     +static void release_revisions_mailmap(struct string_list *mailmap)
    @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)
     +
      void release_revisions(struct rev_info *revs)
      {
    - 	release_revisions_commit_list(revs);
    + 	free_commit_list(revs->commits);
      	object_array_clear(&revs->pending);
     +	release_revisions_mailmap(revs->mailmap);
      }
18:  1df61550e5a ! 18:  7987c748ba4 revisions API: have release_revisions() release "cmdline"
    @@ Commit message
         Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
     
      ## revision.c ##
    -@@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)
    - 	}
    +@@ revision.c: int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
    + 	return left;
      }
      
     +static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)
    @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)
     +		return;
     +	for (i = 0; i < cmdline->nr; i++)
     +		free((char *)cmdline->rev[i].name);
    -+	FREE_AND_NULL(cmdline->rev);
    ++	free(cmdline->rev);
     +}
     +
      static void release_revisions_mailmap(struct string_list *mailmap)
    @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)
      	if (!mailmap)
     @@ revision.c: void release_revisions(struct rev_info *revs)
      {
    - 	release_revisions_commit_list(revs);
    + 	free_commit_list(revs->commits);
      	object_array_clear(&revs->pending);
     +	release_revisions_cmdline(&revs->cmdline);
      	release_revisions_mailmap(revs->mailmap);
19:  065c3cc91b2 ! 19:  e6e87ce3608 revisions API: have release_revisions() release "filter"
    @@ Commit message
     
      ## revision.c ##
     @@ revision.c: void release_revisions(struct rev_info *revs)
    - 	release_revisions_commit_list(revs);
    + 	free_commit_list(revs->commits);
      	object_array_clear(&revs->pending);
      	release_revisions_cmdline(&revs->cmdline);
     +	list_objects_filter_release(&revs->filter);
20:  806fef06b87 = 20:  ebdcdc88d79 revisions API: have release_revisions() release "grep_filter"
21:  fabbe452a14 = 21:  84c8714571a revisions API: have release_revisions() release "prune_data"
22:  cb201fa1914 ! 22:  d96642d3502 revisions API: clear "boundary_commits" in release_revisions()
    @@ Commit message
      ## revision.c ##
     @@ revision.c: void release_revisions(struct rev_info *revs)
      {
    - 	release_revisions_commit_list(revs);
    + 	free_commit_list(revs->commits);
      	object_array_clear(&revs->pending);
     +	object_array_clear(&revs->boundary_commits);
      	release_revisions_cmdline(&revs->cmdline);
23:  0363a69073c = 23:  0e9745d9ee1 revisions API: release "reflog_info" in release revisions()
24:  bad359b2983 = 24:  c7f3e92a8d1 revisions API: call diff_free(&revs->pruning) in revisions_release()
25:  9a51c1ae1d1 = 25:  d428d752462 revisions API: have release_revisions() release "date_mode"
26:  b56db37940d ! 26:  fbf233ec768 revisions API: have release_revisions() release "topo_walk_info"
    @@ revision.c: static void release_revisions_mailmap(struct string_list *mailmap)
     +
      void release_revisions(struct rev_info *revs)
      {
    - 	release_revisions_commit_list(revs);
    + 	free_commit_list(revs->commits);
     @@ revision.c: void release_revisions(struct rev_info *revs)
      	free_grep_patterns(&revs->grep_filter);
      	diff_free(&revs->pruning);
27:  b02cb8fcded = 27:  ec2c77ce869 revisions API: add a TODO for diff_free(&revs->diffopt)
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply	[flat|nested] 252+ messages in thread

* [PATCH v5 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf"
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 02/27] blame: use "goto cleanup" for cleanup_scoreboard() Ævar Arnfjörð Bjarmason
                           ` (27 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Fix a memory leak that's been with us since f9500261e0a (fast-rebase:
write conflict state to working tree, index, and HEAD, 2021-05-20)
changed this code to move these strbuf_release() into an if/else
block.

We'll also add to "reflog_msg" in the "else" arm of the "if" block
being modified here, and we'll append to "branch_msg" in both
cases. But after f9500261e0a only the "if" block would free these two
"struct strbuf".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 t/helper/test-fast-rebase.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c
index fc2d4609043..993b90eaedd 100644
--- a/t/helper/test-fast-rebase.c
+++ b/t/helper/test-fast-rebase.c
@@ -201,8 +201,6 @@ int cmd__fast_rebase(int argc, const char **argv)
 		}
 		if (create_symref("HEAD", branch_name.buf, reflog_msg.buf) < 0)
 			die(_("unable to update HEAD"));
-		strbuf_release(&reflog_msg);
-		strbuf_release(&branch_name);
 
 		prime_cache_tree(the_repository, the_repository->index,
 				 result.tree);
@@ -221,5 +219,8 @@ int cmd__fast_rebase(int argc, const char **argv)
 	if (write_locked_index(&the_index, &lock,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("unable to write %s"), get_index_file());
+
+	strbuf_release(&reflog_msg);
+	strbuf_release(&branch_name);
 	return (result.clean == 0);
 }
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 02/27] blame: use "goto cleanup" for cleanup_scoreboard()
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 03/27] string_list API users: use string_list_init_{no,}dup Ævar Arnfjörð Bjarmason
                           ` (26 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Amend a freeing pattern added in 0906ac2b54b (blame: use changed-path
Bloom filters, 2020-04-16) to use a "goto cleanup", so that we can be
sure that we call cleanup_scoreboard().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/blame.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/builtin/blame.c b/builtin/blame.c
index 8d15b68afc9..885b381ab83 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1167,7 +1167,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 	if (!incremental)
 		setup_pager();
 	else
-		return 0;
+		goto cleanup;
 
 	blame_sort_final(&sb);
 
@@ -1201,6 +1201,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 		printf("num commits: %d\n", sb.num_commits);
 	}
 
+cleanup:
 	cleanup_scoreboard(&sb);
 	return 0;
 }
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 03/27] string_list API users: use string_list_init_{no,}dup
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 02/27] blame: use "goto cleanup" for cleanup_scoreboard() Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids" Ævar Arnfjörð Bjarmason
                           ` (25 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Follow-up on the introduction of string_list_init_nodup() and
string_list_init_dup() in the series merged in bd4232fac33 (Merge
branch 'ab/struct-init', 2021-07-16) and convert code that implicitly
relied on xcalloc() being equivalent to the initializer to use
xmalloc() and string_list_init_{no,}dup() instead.

In the case of get_unmerged() in merge-recursive.c we used the
combination of xcalloc() and assigning "1" to "strdup_strings" to get
what we'd get via string_list_init_dup(), let's use that instead.

Adjacent code in cmd_format_patch() will be changed in a subsequent
commit, since we're changing that let's change the other in-tree
patterns that do the same. Let's also convert a "x == NULL" to "!x"
per our CodingGuidelines, as we need to change the "if" line anyway.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/log.c      | 9 ++++++---
 builtin/shortlog.c | 6 ++++--
 merge-recursive.c  | 4 ++--
 3 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/builtin/log.c b/builtin/log.c
index c211d66d1d0..634dc782cce 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -231,7 +231,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
 	}
 
 	if (mailmap) {
-		rev->mailmap = xcalloc(1, sizeof(struct string_list));
+		rev->mailmap = xmalloc(sizeof(struct string_list));
+		string_list_init_nodup(rev->mailmap);
 		read_mailmap(rev->mailmap);
 	}
 
@@ -2173,8 +2174,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		prepare_bases(&bases, base, list, nr);
 	}
 
-	if (in_reply_to || thread || cover_letter)
-		rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
+	if (in_reply_to || thread || cover_letter) {
+		rev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));
+		string_list_init_nodup(rev.ref_message_ids);
+	}
 	if (in_reply_to) {
 		const char *msgid = clean_message_id(in_reply_to);
 		string_list_append(rev.ref_message_ids, msgid);
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 26c5c0cf935..fcde07c9367 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -81,8 +81,10 @@ static void insert_one_record(struct shortlog *log,
 		format_subject(&subject, oneline, " ");
 		buffer = strbuf_detach(&subject, NULL);
 
-		if (item->util == NULL)
-			item->util = xcalloc(1, sizeof(struct string_list));
+		if (!item->util) {
+			item->util = xmalloc(sizeof(struct string_list));
+			string_list_init_nodup(item->util);
+		}
 		string_list_append(item->util, buffer);
 	}
 }
diff --git a/merge-recursive.c b/merge-recursive.c
index 1ee6364e8b1..32bbba5fbb1 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -522,10 +522,10 @@ static struct stage_data *insert_stage_data(struct repository *r,
  */
 static struct string_list *get_unmerged(struct index_state *istate)
 {
-	struct string_list *unmerged = xcalloc(1, sizeof(struct string_list));
+	struct string_list *unmerged = xmalloc(sizeof(struct string_list));
 	int i;
 
-	unmerged->strdup_strings = 1;
+	string_list_init_dup(unmerged);
 
 	/* TODO: audit for interaction with sparse-index. */
 	ensure_full_index(istate);
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids"
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (2 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 03/27] string_list API users: use string_list_init_{no,}dup Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 05/27] cocci: add and apply free_commit_list() rules Ævar Arnfjörð Bjarmason
                           ` (24 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Fix two memory leaks in "struct rev_info" by freeing that memory in
cmd_format_patch(). These two are unusual special-cases in being in
the "struct rev_info", but not being "owned" by the code in
revision.c. I.e. they're members of the struct so that this code in
"builtin/log.c" can conveniently pass information code in
"log-tree.c".

See e.g. the make_cover_letter() caller of log_write_email_headers()
here in "builtin/log.c", and [1] for a demonstration of where the
"extra_headers" and "ref_message_ids" struct members are used.

See 20ff06805c6 (format-patch: resurrect extra headers from config,
2006-06-02) and d1566f7883f (git-format-patch: Make the second and
subsequent mails replies to the first, 2006-07-14) for the initial
introduction of "extra_headers" and "ref_message_ids".

We can count on repo_init_revisions() memset()-ing this data to 0
however, so we can count on it being either NULL or something we
allocated. In the case of "extra_headers" let's add a local "char *"
variable to hold it, to avoid the eventual cast from "const char *"
when we free() it.

1. https://lore.kernel.org/git/220401.868rsoogxf.gmgdl@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/log.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/builtin/log.c b/builtin/log.c
index 634dc782cce..6f9928fabfe 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1747,6 +1747,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	struct commit *commit;
 	struct commit **list = NULL;
 	struct rev_info rev;
+	char *to_free = NULL;
 	struct setup_revision_opt s_r_opt;
 	int nr = 0, total, i;
 	int use_stdout = 0;
@@ -1947,7 +1948,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		strbuf_addch(&buf, '\n');
 	}
 
-	rev.extra_headers = strbuf_detach(&buf, NULL);
+	rev.extra_headers = to_free = strbuf_detach(&buf, NULL);
 
 	if (from) {
 		if (split_ident_line(&rev.from_ident, from, strlen(from)))
@@ -2284,6 +2285,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	strbuf_release(&rdiff1);
 	strbuf_release(&rdiff2);
 	strbuf_release(&rdiff_title);
+	free(to_free);
+	if (rev.ref_message_ids)
+		string_list_clear(rev.ref_message_ids, 0);
+	free(rev.ref_message_ids);
 	UNLEAK(rev);
 	return 0;
 }
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 05/27] cocci: add and apply free_commit_list() rules
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (3 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids" Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 06/27] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
                           ` (23 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Add and apply coccinelle rules to remove "if (E)" before
"free_commit_list(E)", the function can accept NULL, and further
change cases where "E = NULL" followed to also be unconditionally.

The code changes in this commit were entirely made by the coccinelle
rule being added here, and applied with:

    make contrib/coccinelle/free.cocci.patch
    patch -p1 <contrib/coccinelle/free.cocci.patch

The only manual intervention here is that the the relevant code in
commit.c has been manually re-indented.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/rev-list.c            |  6 ++----
 commit.c                      | 19 ++++++++-----------
 contrib/coccinelle/free.cocci | 27 +++++++++++++++++++++++++++
 revision.c                    | 17 ++++++-----------
 submodule.c                   |  6 ++----
 5 files changed, 45 insertions(+), 30 deletions(-)

diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 572da1472e5..07c0ad11d8d 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -213,10 +213,8 @@ static void show_commit(struct commit *commit, void *data)
 
 static void finish_commit(struct commit *commit)
 {
-	if (commit->parents) {
-		free_commit_list(commit->parents);
-		commit->parents = NULL;
-	}
+	free_commit_list(commit->parents);
+	commit->parents = NULL;
 	free_commit_buffer(the_repository->parsed_objects,
 			   commit);
 }
diff --git a/commit.c b/commit.c
index 59b6c3e4552..9628716bf2f 100644
--- a/commit.c
+++ b/commit.c
@@ -407,17 +407,14 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b
 
 	if (item->object.parsed)
 		return 0;
-
-	if (item->parents) {
-		/*
-		 * Presumably this is leftover from an earlier failed parse;
-		 * clear it out in preparation for us re-parsing (we'll hit the
-		 * same error, but that's good, since it lets our caller know
-		 * the result cannot be trusted.
-		 */
-		free_commit_list(item->parents);
-		item->parents = NULL;
-	}
+	/*
+	 * Presumably this is leftover from an earlier failed parse;
+	 * clear it out in preparation for us re-parsing (we'll hit the
+	 * same error, but that's good, since it lets our caller know
+	 * the result cannot be trusted.
+	 */
+	free_commit_list(item->parents);
+	item->parents = NULL;
 
 	tail += size;
 	if (tail <= bufptr + tree_entry_len + 1 || memcmp(bufptr, "tree ", 5) ||
diff --git a/contrib/coccinelle/free.cocci b/contrib/coccinelle/free.cocci
index 4490069df96..6fb9eb6e883 100644
--- a/contrib/coccinelle/free.cocci
+++ b/contrib/coccinelle/free.cocci
@@ -2,13 +2,21 @@
 expression E;
 @@
 - if (E)
+(
   free(E);
+|
+  free_commit_list(E);
+)
 
 @@
 expression E;
 @@
 - if (!E)
+(
   free(E);
+|
+  free_commit_list(E);
+)
 
 @@
 expression E;
@@ -16,3 +24,22 @@ expression E;
 - free(E);
 + FREE_AND_NULL(E);
 - E = NULL;
+
+@@
+expression E;
+@@
+- if (E)
+- {
+  free_commit_list(E);
+  E = NULL;
+- }
+
+@@
+expression E;
+statement S;
+@@
+- if (E) {
++ if (E)
+  S
+  free_commit_list(E);
+- }
diff --git a/revision.c b/revision.c
index 7d435f80480..4963ba7def8 100644
--- a/revision.c
+++ b/revision.c
@@ -1456,10 +1456,9 @@ static int limit_list(struct rev_info *revs)
 	if (revs->left_only || revs->right_only)
 		limit_left_right(newlist, revs);
 
-	if (bottom) {
+	if (bottom)
 		limit_to_ancestry(bottom, newlist);
-		free_commit_list(bottom);
-	}
+	free_commit_list(bottom);
 
 	/*
 	 * Check if any commits have become TREESAME by some of their parents
@@ -4080,10 +4079,8 @@ static void create_boundary_commit_list(struct rev_info *revs)
 	 * boundary commits anyway.  (This is what the code has always
 	 * done.)
 	 */
-	if (revs->commits) {
-		free_commit_list(revs->commits);
-		revs->commits = NULL;
-	}
+	free_commit_list(revs->commits);
+	revs->commits = NULL;
 
 	/*
 	 * Put all of the actual boundary commits from revs->boundary_commits
@@ -4220,10 +4217,8 @@ struct commit *get_revision(struct rev_info *revs)
 		graph_update(revs->graph, c);
 	if (!c) {
 		free_saved_parents(revs);
-		if (revs->previous_parents) {
-			free_commit_list(revs->previous_parents);
-			revs->previous_parents = NULL;
-		}
+		free_commit_list(revs->previous_parents);
+		revs->previous_parents = NULL;
 	}
 	return c;
 }
diff --git a/submodule.c b/submodule.c
index 86c8f0f89db..9b715b4a5cb 100644
--- a/submodule.c
+++ b/submodule.c
@@ -645,8 +645,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,
 	print_submodule_diff_summary(sub, &rev, o);
 
 out:
-	if (merge_bases)
-		free_commit_list(merge_bases);
+	free_commit_list(merge_bases);
 	clear_commit_marks(left, ~0);
 	clear_commit_marks(right, ~0);
 	if (sub) {
@@ -735,8 +734,7 @@ void show_submodule_inline_diff(struct diff_options *o, const char *path,
 
 done:
 	strbuf_release(&sb);
-	if (merge_bases)
-		free_commit_list(merge_bases);
+	free_commit_list(merge_bases);
 	if (left)
 		clear_commit_marks(left, ~0);
 	if (right)
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 06/27] revision.[ch]: provide and start using a release_revisions()
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (4 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 05/27] cocci: add and apply free_commit_list() rules Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 07/27] revisions API users: add straightforward release_revisions() Ævar Arnfjörð Bjarmason
                           ` (22 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

The users of the revision.[ch] API's "struct rev_info" are a major
source of memory leaks in the test suite under SANITIZE=leak, which in
turn adds a lot of noise when trying to mark up tests with
"TEST_PASSES_SANITIZE_LEAK=true".

The users of that API are largely one-shot, e.g. "git rev-list" or
"git log", or the "git checkout" and "git stash" being modified here

For these callers freeing the memory is arguably a waste of time, but
in many cases they've actually been trying to free the memory, and
just doing that in a buggy manner.

Let's provide a release_revisions() function for these users, and
start migrating them over per the plan outlined in [1]. Right now this
only handles the "pending" member of the struct, but more will be
added in subsequent commits.

Even though we only clear the "pending" member now, let's not leave a
trap in code like the pre-image of index_differs_from(), where we'd
start doing the wrong thing as soon as the release_revisions() learned
to clear its "diffopt". I.e. we need to call release_revisions() after
we've inspected any state in "struct rev_info".

This leaves in place e.g. clear_pathspec(&rev.prune_data) in
stash_working_tree() in builtin/stash.c, subsequent commits will teach
release_revisions() to free "prune_data" and other members that in
some cases are individually cleared by users of "struct rev_info" by
reaching into its members. Those subsequent commits will remove the
relevant calls to e.g. clear_pathspec().

We avoid amending code in index_differs_from() in diff-lib.c as well
as wt_status_collect_changes_index(), has_unstaged_changes() and
has_uncommitted_changes() in wt-status.c in a way that assumes that we
are already clearing the "diffopt" member. That will be handled in a
subsequent commit.

1. https://lore.kernel.org/git/87a6k8daeu.fsf@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/checkout.c | 2 +-
 builtin/stash.c    | 5 ++---
 diff-lib.c         | 2 +-
 range-diff.c       | 2 +-
 revision.c         | 5 +++++
 revision.h         | 6 ++++++
 wt-status.c        | 5 +++--
 7 files changed, 19 insertions(+), 8 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 797681481d1..4d9e0bd3ac1 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -629,7 +629,7 @@ static void show_local_changes(struct object *head,
 	diff_setup_done(&rev.diffopt);
 	add_pending_object(&rev, head, NULL);
 	run_diff_index(&rev, 0);
-	object_array_clear(&rev.pending);
+	release_revisions(&rev);
 }
 
 static void describe_detached_head(const char *msg, struct commit *commit)
diff --git a/builtin/stash.c b/builtin/stash.c
index 0c7b6a95882..74fa810cf8c 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1047,7 +1047,6 @@ static int check_changes_tracked_files(const struct pathspec *ps)
 		goto done;
 	}
 
-	object_array_clear(&rev.pending);
 	result = run_diff_files(&rev, 0);
 	if (diff_result_code(&rev.diffopt, result)) {
 		ret = 1;
@@ -1056,6 +1055,7 @@ static int check_changes_tracked_files(const struct pathspec *ps)
 
 done:
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 	return ret;
 }
 
@@ -1266,9 +1266,8 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
 
 done:
 	discard_index(&istate);
-	UNLEAK(rev);
-	object_array_clear(&rev.pending);
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 	strbuf_release(&diff_output);
 	remove_path(stash_index_path.buf);
 	return ret;
diff --git a/diff-lib.c b/diff-lib.c
index ca085a03efc..d6800274bd5 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -662,7 +662,7 @@ int index_differs_from(struct repository *r,
 		diff_flags_or(&rev.diffopt.flags, flags);
 	rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
 	run_diff_index(&rev, 1);
-	object_array_clear(&rev.pending);
+	release_revisions(&rev);
 	return (rev.diffopt.flags.has_changes != 0);
 }
 
diff --git a/range-diff.c b/range-diff.c
index b72eb9fdbee..39cc010c628 100644
--- a/range-diff.c
+++ b/range-diff.c
@@ -596,6 +596,6 @@ int is_range_diff_range(const char *arg)
 	}
 
 	free(copy);
-	object_array_clear(&revs.pending);
+	release_revisions(&revs);
 	return negative > 0 && positive > 0;
 }
diff --git a/revision.c b/revision.c
index 4963ba7def8..5dd4b2e910c 100644
--- a/revision.c
+++ b/revision.c
@@ -2922,6 +2922,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 	return left;
 }
 
+void release_revisions(struct rev_info *revs)
+{
+	object_array_clear(&revs->pending);
+}
+
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
 {
 	struct commit_list *l = xcalloc(1, sizeof(*l));
diff --git a/revision.h b/revision.h
index 5bc59c7bfe1..61c780fc4cd 100644
--- a/revision.h
+++ b/revision.h
@@ -377,6 +377,12 @@ void repo_init_revisions(struct repository *r,
 int setup_revisions(int argc, const char **argv, struct rev_info *revs,
 		    struct setup_revision_opt *);
 
+/**
+ * Free data allocated in a "struct rev_info" after it's been
+ * initialized with repo_init_revisions().
+ */
+void release_revisions(struct rev_info *revs);
+
 void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
 			const struct option *options,
 			const char * const usagestr[]);
diff --git a/wt-status.c b/wt-status.c
index d33f9272b72..922cf787f95 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -662,7 +662,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)
 
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_index(&rev, 1);
-	object_array_clear(&rev.pending);
+	release_revisions(&rev);
 	clear_pathspec(&rev.prune_data);
 }
 
@@ -2545,6 +2545,7 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)
 	rev_info.diffopt.flags.quick = 1;
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_files(&rev_info, 0);
+	release_revisions(&rev_info);
 	return diff_result_code(&rev_info.diffopt, result);
 }
 
@@ -2577,7 +2578,7 @@ int has_uncommitted_changes(struct repository *r,
 
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_index(&rev_info, 1);
-	object_array_clear(&rev_info.pending);
+	release_revisions(&rev_info);
 	return diff_result_code(&rev_info.diffopt, result);
 }
 
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 07/27] revisions API users: add straightforward release_revisions()
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (5 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 06/27] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 08/27] revision.[ch]: document and move code declared around "init" Ævar Arnfjörð Bjarmason
                           ` (21 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Add a release_revisions() to various users of "struct rev_list" in
those straightforward cases where we only need to add the
release_revisions() call to the end of a block, and don't need to
e.g. refactor anything to use a "goto cleanup" pattern.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 add-interactive.c                | 1 +
 bisect.c                         | 2 ++
 builtin/add.c                    | 1 +
 builtin/am.c                     | 3 +++
 builtin/bisect--helper.c         | 2 ++
 builtin/blame.c                  | 1 +
 builtin/checkout.c               | 1 +
 builtin/commit.c                 | 1 +
 builtin/describe.c               | 2 ++
 builtin/fast-export.c            | 1 +
 builtin/merge.c                  | 2 ++
 builtin/pack-objects.c           | 2 ++
 builtin/prune.c                  | 1 +
 builtin/reflog.c                 | 1 +
 builtin/shortlog.c               | 2 ++
 builtin/submodule--helper.c      | 1 +
 fmt-merge-msg.c                  | 1 +
 merge-ort.c                      | 1 +
 merge-recursive.c                | 1 +
 midx.c                           | 1 +
 pack-bitmap-write.c              | 1 +
 ref-filter.c                     | 1 +
 remote.c                         | 1 +
 sequencer.c                      | 3 +++
 shallow.c                        | 1 +
 submodule.c                      | 2 ++
 t/helper/test-revision-walking.c | 1 +
 wt-status.c                      | 1 +
 28 files changed, 39 insertions(+)

diff --git a/add-interactive.c b/add-interactive.c
index 72472103017..54cdfc82017 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -570,6 +570,7 @@ static int get_modified_files(struct repository *r,
 
 		if (ps)
 			clear_pathspec(&rev.prune_data);
+		release_revisions(&rev);
 	}
 	hashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);
 	if (unmerged_count)
diff --git a/bisect.c b/bisect.c
index 9e6a2b7f201..cc6b8b6230d 100644
--- a/bisect.c
+++ b/bisect.c
@@ -884,6 +884,7 @@ static int check_ancestors(struct repository *r, int rev_nr,
 	/* Clean up objects used, as they will be reused. */
 	clear_commit_marks_many(rev_nr, rev, ALL_REV_FLAGS);
 
+	release_revisions(&revs);
 	return res;
 }
 
@@ -964,6 +965,7 @@ static void show_diff_tree(struct repository *r,
 
 	setup_revisions(ARRAY_SIZE(argv) - 1, argv, &opt, NULL);
 	log_tree_commit(&opt, commit);
+	release_revisions(&opt);
 }
 
 /*
diff --git a/builtin/add.c b/builtin/add.c
index 3ffb86a4338..f507d2191cd 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -340,6 +340,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
 
 	unlink(file);
 	free(file);
+	release_revisions(&rev);
 	return 0;
 }
 
diff --git a/builtin/am.c b/builtin/am.c
index 0f4111bafa0..93bec62afa9 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1397,6 +1397,7 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm
 	add_pending_object(&rev_info, &commit->object, "");
 	diff_setup_done(&rev_info.diffopt);
 	log_tree_commit(&rev_info, commit);
+	release_revisions(&rev_info);
 }
 
 /**
@@ -1429,6 +1430,7 @@ static void write_index_patch(const struct am_state *state)
 	add_pending_object(&rev_info, &tree->object, "");
 	diff_setup_done(&rev_info.diffopt);
 	run_diff_index(&rev_info, 1);
+	release_revisions(&rev_info);
 }
 
 /**
@@ -1582,6 +1584,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
 		add_pending_oid(&rev_info, "HEAD", &our_tree, 0);
 		diff_setup_done(&rev_info.diffopt);
 		run_diff_index(&rev_info, 1);
+		release_revisions(&rev_info);
 	}
 
 	if (run_apply(state, index_path))
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 8b2b259ff0d..e4d7b6779ae 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -555,6 +555,7 @@ static int bisect_skipped_commits(struct bisect_terms *terms)
 	reset_revision_walk();
 
 	strbuf_release(&commit_name);
+	release_revisions(&revs);
 	fclose(fp);
 	return 0;
 }
@@ -1041,6 +1042,7 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar
 						oid_to_hex(&commit->object.oid));
 
 			reset_revision_walk();
+			release_revisions(&revs);
 		} else {
 			strvec_push(&argv_state, argv[i]);
 		}
diff --git a/builtin/blame.c b/builtin/blame.c
index 885b381ab83..24bac822c56 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1203,5 +1203,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 
 cleanup:
 	cleanup_scoreboard(&sb);
+	release_revisions(&revs);
 	return 0;
 }
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 4d9e0bd3ac1..7ad4a7113c9 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1082,6 +1082,7 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne
 
 	/* Clean up objects used, as they will be reused. */
 	repo_clear_commit_marks(the_repository, ALL_REV_FLAGS);
+	release_revisions(&revs);
 }
 
 static int switch_branches(const struct checkout_opts *opts,
diff --git a/builtin/commit.c b/builtin/commit.c
index 009a1de0a3d..c7eda9bbb72 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1123,6 +1123,7 @@ static const char *find_author_by_nickname(const char *name)
 		strbuf_release(&buf);
 		format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
 		clear_mailmap(&mailmap);
+		release_revisions(&revs);
 		return strbuf_detach(&buf, NULL);
 	}
 	die(_("--author '%s' is not 'Name <email>' and matches no existing author"), name);
diff --git a/builtin/describe.c b/builtin/describe.c
index 42159cd26bd..a76f1a1a7a7 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -517,6 +517,7 @@ static void describe_blob(struct object_id oid, struct strbuf *dst)
 
 	traverse_commit_list(&revs, process_commit, process_object, &pcd);
 	reset_revision_walk();
+	release_revisions(&revs);
 }
 
 static void describe(const char *arg, int last_one)
@@ -667,6 +668,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
 				suffix = NULL;
 			else
 				suffix = dirty;
+			release_revisions(&revs);
 		}
 		describe("HEAD", 1);
 	} else if (dirty) {
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index a7d72697fba..f34ae451ee3 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -1275,6 +1275,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
 		printf("done\n");
 
 	refspec_clear(&refspecs);
+	release_revisions(&revs);
 
 	return 0;
 }
diff --git a/builtin/merge.c b/builtin/merge.c
index f178f5a3ee1..d9784d4891c 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -443,6 +443,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
 	}
 	write_file_buf(git_path_squash_msg(the_repository), out.buf, out.len);
 	strbuf_release(&out);
+	release_revisions(&rev);
 }
 
 static void finish(struct commit *head_commit,
@@ -998,6 +999,7 @@ static int evaluate_result(void)
 	 */
 	cnt += count_unmerged_entries();
 
+	release_revisions(&rev);
 	return cnt;
 }
 
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 014dcd4bc98..180fff03569 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -4177,11 +4177,13 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 		read_object_list_from_stdin();
 	} else if (pfd.have_revs) {
 		get_object_list(&pfd.revs, rp.nr, rp.v);
+		release_revisions(&pfd.revs);
 	} else {
 		struct rev_info revs;
 
 		repo_init_revisions(the_repository, &revs, NULL);
 		get_object_list(&revs, rp.nr, rp.v);
+		release_revisions(&revs);
 	}
 	cleanup_preferred_base();
 	if (include_tag && nr_result)
diff --git a/builtin/prune.c b/builtin/prune.c
index c2bcdc07db4..df376b2ed1e 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -196,5 +196,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
 		prune_shallow(show_only ? PRUNE_SHOW_ONLY : 0);
 	}
 
+	release_revisions(&revs);
 	return 0;
 }
diff --git a/builtin/reflog.c b/builtin/reflog.c
index 9407f835cb6..592d5d33442 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -248,6 +248,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 		if (verbose)
 			printf(_("Marking reachable objects..."));
 		mark_reachable_objects(&revs, 0, 0, NULL);
+		release_revisions(&revs);
 		if (verbose)
 			putchar('\n');
 	}
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index fcde07c9367..35825f075e3 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -422,6 +422,8 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 	else
 		get_from_rev(&rev, &log);
 
+	release_revisions(&rev);
+
 	shortlog_output(&log);
 	if (log.file != stdout)
 		fclose(log.file);
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 5301612d24b..24980863f68 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1269,6 +1269,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 		run_diff_files(&rev, 0);
 	prepare_submodule_summary(info, &list);
 	strvec_clear(&diff_args);
+	release_revisions(&rev);
 	return 0;
 }
 
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index baca57d5b64..f48f44f9cd1 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -699,6 +699,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 			shortlog(origins.items[i].string,
 				 origins.items[i].util,
 				 head, &rev, opts, out);
+		release_revisions(&rev);
 	}
 
 	strbuf_complete_line(out);
diff --git a/merge-ort.c b/merge-ort.c
index 8545354dafd..4c0d4eba19b 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -1594,6 +1594,7 @@ static int find_first_merges(struct repository *repo,
 	}
 
 	object_array_clear(&merges);
+	release_revisions(&revs);
 	return result->nr;
 }
 
diff --git a/merge-recursive.c b/merge-recursive.c
index 32bbba5fbb1..acd13b2b069 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -1160,6 +1160,7 @@ static int find_first_merges(struct repository *repo,
 	}
 
 	object_array_clear(&merges);
+	release_revisions(&revs);
 	return result->nr;
 }
 
diff --git a/midx.c b/midx.c
index 107365d2114..d5724df7d60 100644
--- a/midx.c
+++ b/midx.c
@@ -1061,6 +1061,7 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr
 	if (indexed_commits_nr_p)
 		*indexed_commits_nr_p = cb.commits_nr;
 
+	release_revisions(&revs);
 	return cb.commits;
 }
 
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index cf681547f2e..c43375bd344 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -326,6 +326,7 @@ static void bitmap_builder_init(struct bitmap_builder *bb,
 	trace2_data_intmax("pack-bitmap-write", the_repository,
 			   "num_maximal_commits", num_maximal);
 
+	release_revisions(&revs);
 	free_commit_list(reusable);
 }
 
diff --git a/ref-filter.c b/ref-filter.c
index 7838bd22b8d..a91688bbf17 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2392,6 +2392,7 @@ static void reach_filter(struct ref_array *array,
 		clear_commit_marks(merge_commit, ALL_REV_FLAGS);
 	}
 
+	release_revisions(&revs);
 	free(to_clear);
 }
 
diff --git a/remote.c b/remote.c
index 42a4e7106e1..fa3152a5d52 100644
--- a/remote.c
+++ b/remote.c
@@ -2172,6 +2172,7 @@ static int stat_branch_pair(const char *branch_name, const char *base,
 	clear_commit_marks(theirs, ALL_REV_FLAGS);
 
 	strvec_clear(&argv);
+	release_revisions(&revs);
 	return 1;
 }
 
diff --git a/sequencer.c b/sequencer.c
index a1bb39383db..f9d7acd1065 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1347,6 +1347,7 @@ void print_commit_summary(struct repository *r,
 		log_tree_commit(&rev, commit);
 	}
 
+	release_revisions(&rev);
 	strbuf_release(&format);
 }
 
@@ -3415,6 +3416,7 @@ static int make_patch(struct repository *r,
 		unuse_commit_buffer(commit, commit_buffer);
 	}
 	strbuf_release(&buf);
+	release_revisions(&log_tree_opt);
 
 	return res;
 }
@@ -4525,6 +4527,7 @@ static int pick_commits(struct repository *r,
 					      &log_tree_opt.diffopt);
 				log_tree_diff_flush(&log_tree_opt);
 			}
+			release_revisions(&log_tree_opt);
 		}
 		flush_rewritten_pending();
 		if (!stat(rebase_path_rewritten_list(), &st) &&
diff --git a/shallow.c b/shallow.c
index e158be58b05..797a593633f 100644
--- a/shallow.c
+++ b/shallow.c
@@ -262,6 +262,7 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,
 		if ((o->flags & both_flags) == both_flags)
 			o->flags &= ~not_shallow_flag;
 	}
+	release_revisions(&revs);
 	return result;
 }
 
diff --git a/submodule.c b/submodule.c
index 9b715b4a5cb..7b5d7753930 100644
--- a/submodule.c
+++ b/submodule.c
@@ -923,9 +923,11 @@ static void collect_changed_submodules(struct repository *r,
 		diff_rev.diffopt.format_callback_data = &data;
 		diff_rev.dense_combined_merges = 1;
 		diff_tree_combined_merge(commit, &diff_rev);
+		release_revisions(&diff_rev);
 	}
 
 	reset_revision_walk();
+	release_revisions(&rev);
 }
 
 static void free_submodules_data(struct string_list *submodules)
diff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c
index 625b2dbf822..4a45d5bac2a 100644
--- a/t/helper/test-revision-walking.c
+++ b/t/helper/test-revision-walking.c
@@ -43,6 +43,7 @@ static int run_revision_walk(void)
 	}
 
 	reset_revision_walk();
+	release_revisions(&rev);
 	return got_revision;
 }
 
diff --git a/wt-status.c b/wt-status.c
index 922cf787f95..f9100621375 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1152,6 +1152,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
 		rev.diffopt.b_prefix = "w/";
 		run_diff_files(&rev, 0);
 	}
+	release_revisions(&rev);
 }
 
 static void wt_longstatus_print_tracking(struct wt_status *s)
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 08/27] revision.[ch]: document and move code declared around "init"
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (6 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 07/27] revisions API users: add straightforward release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 09/27] revisions API users: use release_revisions() needing REV_INFO_INIT Ævar Arnfjörð Bjarmason
                           ` (20 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

A subsequent commit will add "REV_INFO_INIT" macro adjacent to
repo_init_revisions(), unfortunately between the "struct rev_info"
itself and that function we've added various miscellaneous code
between the two over the years.

Let's move that code either lower in revision.h, giving it API docs
while we're at it, or in cases where it wasn't public API at all move
it into revision.c No lines of code are changed here, only moved
around. The only changes are the addition of new API comments.

The "tree_difference" variable could also be declared like this, which
I think would be a lot clearer, but let's leave that for now to keep
this a move-only change:

	static enum {
		REV_TREE_SAME,
		REV_TREE_NEW, /* Only new files */
		REV_TREE_OLD, /* Only files removed */
		REV_TREE_DIFFERENT, /* Mixed changes */
	} tree_difference = REV_TREE_SAME;

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c |  4 ++++
 revision.h | 50 ++++++++++++++++++++++++--------------------------
 2 files changed, 28 insertions(+), 26 deletions(-)

diff --git a/revision.c b/revision.c
index 5dd4b2e910c..472fff1e0a7 100644
--- a/revision.c
+++ b/revision.c
@@ -606,6 +606,10 @@ static struct commit *one_relevant_parent(const struct rev_info *revs,
  *
  *   2. We saw anything except REV_TREE_NEW.
  */
+#define REV_TREE_SAME		0
+#define REV_TREE_NEW		1	/* Only new files */
+#define REV_TREE_OLD		2	/* Only files removed */
+#define REV_TREE_DIFFERENT	3	/* Mixed changes */
 static int tree_difference = REV_TREE_SAME;
 
 static void file_add_remove(struct diff_options *options,
diff --git a/revision.h b/revision.h
index 61c780fc4cd..b9070e43428 100644
--- a/revision.h
+++ b/revision.h
@@ -329,32 +329,6 @@ struct rev_info {
 	struct tmp_objdir *remerge_objdir;
 };
 
-int ref_excluded(struct string_list *, const char *path);
-void clear_ref_exclusion(struct string_list **);
-void add_ref_exclusion(struct string_list **, const char *exclude);
-
-
-#define REV_TREE_SAME		0
-#define REV_TREE_NEW		1	/* Only new files */
-#define REV_TREE_OLD		2	/* Only files removed */
-#define REV_TREE_DIFFERENT	3	/* Mixed changes */
-
-/* revision.c */
-typedef void (*show_early_output_fn_t)(struct rev_info *, struct commit_list *);
-extern volatile show_early_output_fn_t show_early_output;
-
-struct setup_revision_opt {
-	const char *def;
-	void (*tweak)(struct rev_info *, struct setup_revision_opt *);
-	unsigned int	assume_dashdash:1,
-			allow_exclude_promisor_objects:1;
-	unsigned revarg_opt;
-};
-
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define init_revisions(revs, prefix) repo_init_revisions(the_repository, revs, prefix)
-#endif
-
 /**
  * Initialize a rev_info structure with default values. The third parameter may
  * be NULL or can be prefix path, and then the `.prefix` variable will be set
@@ -366,6 +340,9 @@ struct setup_revision_opt {
 void repo_init_revisions(struct repository *r,
 			 struct rev_info *revs,
 			 const char *prefix);
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define init_revisions(revs, prefix) repo_init_revisions(the_repository, revs, prefix)
+#endif
 
 /**
  * Parse revision information, filling in the `rev_info` structure, and
@@ -374,6 +351,13 @@ void repo_init_revisions(struct repository *r,
  * head of the argument list. The last parameter is used in case no
  * parameter given by the first two arguments.
  */
+struct setup_revision_opt {
+	const char *def;
+	void (*tweak)(struct rev_info *, struct setup_revision_opt *);
+	unsigned int	assume_dashdash:1,
+			allow_exclude_promisor_objects:1;
+	unsigned revarg_opt;
+};
 int setup_revisions(int argc, const char **argv, struct rev_info *revs,
 		    struct setup_revision_opt *);
 
@@ -423,6 +407,14 @@ void mark_trees_uninteresting_sparse(struct repository *r, struct oidset *trees)
 
 void show_object_with_name(FILE *, struct object *, const char *);
 
+/**
+ * Helpers to check if a "struct string_list" item matches with
+ * wildmatch().
+ */
+int ref_excluded(struct string_list *, const char *path);
+void clear_ref_exclusion(struct string_list **);
+void add_ref_exclusion(struct string_list **, const char *exclude);
+
 /**
  * This function can be used if you want to add commit objects as revision
  * information. You can use the `UNINTERESTING` object flag to indicate if
@@ -478,4 +470,10 @@ int rewrite_parents(struct rev_info *revs,
  */
 struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit);
 
+/**
+ * Global for the (undocumented) "--early-output" flag for "git log".
+ */
+typedef void (*show_early_output_fn_t)(struct rev_info *, struct commit_list *);
+extern volatile show_early_output_fn_t show_early_output;
+
 #endif
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 09/27] revisions API users: use release_revisions() needing REV_INFO_INIT
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (7 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 08/27] revision.[ch]: document and move code declared around "init" Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 10/27] stash: always have the owner of "stash_info" free it Ævar Arnfjörð Bjarmason
                           ` (19 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Use release_revisions() to various users of "struct rev_list" which
need to have their "struct rev_info" zero-initialized before we can
start using it.

For the bundle.c code see the early exit case added in
3bbbe467f29 (bundle verify: error out if called without an object
database, 2019-05-27).

For the relevant bisect.c code see 45b6370812c (bisect: libify
`check_good_are_ancestors_of_bad` and its dependents, 2020-02-17).

For the submodule.c code see the "goto" on "(!left || !right || !sub)"
added in 8e6df65015f (submodule: refactor show_submodule_summary with
helper function, 2016-08-31).

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 bisect.c                    | 18 ++++++++++++------
 builtin/submodule--helper.c |  3 ++-
 bundle.c                    | 12 ++++++++----
 revision.h                  | 21 ++++++++++++++++++++-
 submodule.c                 |  3 ++-
 5 files changed, 44 insertions(+), 13 deletions(-)

diff --git a/bisect.c b/bisect.c
index cc6b8b6230d..b63669cc9d7 100644
--- a/bisect.c
+++ b/bisect.c
@@ -1010,7 +1010,7 @@ void read_bisect_terms(const char **read_bad, const char **read_good)
  */
 enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 {
-	struct rev_info revs;
+	struct rev_info revs = REV_INFO_INIT;
 	struct commit_list *tried;
 	int reaches = 0, all = 0, nr, steps;
 	enum bisect_error res = BISECT_OK;
@@ -1035,7 +1035,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 
 	res = check_good_are_ancestors_of_bad(r, prefix, no_checkout);
 	if (res)
-		return res;
+		goto cleanup;
 
 	bisect_rev_setup(r, &revs, prefix, "%s", "^%s", 1);
 
@@ -1060,14 +1060,16 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 		       term_good,
 		       term_bad);
 
-		return BISECT_FAILED;
+		res = BISECT_FAILED;
+		goto cleanup;
 	}
 
 	if (!all) {
 		fprintf(stderr, _("No testable commit found.\n"
 			"Maybe you started with bad path arguments?\n"));
 
-		return BISECT_NO_TESTABLE_COMMIT;
+		res = BISECT_NO_TESTABLE_COMMIT;
+		goto cleanup;
 	}
 
 	bisect_rev = &revs.commits->item->object.oid;
@@ -1087,7 +1089,8 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 		 * for negative return values for early returns up
 		 * until the cmd_bisect__helper() caller.
 		 */
-		return BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;
+		res = BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;
+		goto cleanup;
 	}
 
 	nr = all - reaches - 1;
@@ -1106,7 +1109,10 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 	/* Clean up objects used, as they will be reused. */
 	repo_clear_commit_marks(r, ALL_REV_FLAGS);
 
-	return bisect_checkout(bisect_rev, no_checkout);
+	res = bisect_checkout(bisect_rev, no_checkout);
+cleanup:
+	release_revisions(&revs);
+	return res;
 }
 
 static inline int log2i(int n)
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 24980863f68..cda33ee4d2b 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -766,7 +766,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
 {
 	char *displaypath;
 	struct strvec diff_files_args = STRVEC_INIT;
-	struct rev_info rev;
+	struct rev_info rev = REV_INFO_INIT;
 	int diff_files_result;
 	struct strbuf buf = STRBUF_INIT;
 	const char *git_dir;
@@ -853,6 +853,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
 cleanup:
 	strvec_clear(&diff_files_args);
 	free(displaypath);
+	release_revisions(&rev);
 }
 
 static void status_submodule_cb(const struct cache_entry *list_item,
diff --git a/bundle.c b/bundle.c
index d50cfb5aa7e..6a870a6edb7 100644
--- a/bundle.c
+++ b/bundle.c
@@ -196,14 +196,16 @@ int verify_bundle(struct repository *r,
 	 * to be verbose about the errors
 	 */
 	struct string_list *p = &header->prerequisites;
-	struct rev_info revs;
+	struct rev_info revs = REV_INFO_INIT;
 	const char *argv[] = {NULL, "--all", NULL};
 	struct commit *commit;
 	int i, ret = 0, req_nr;
 	const char *message = _("Repository lacks these prerequisite commits:");
 
-	if (!r || !r->objects || !r->objects->odb)
-		return error(_("need a repository to verify a bundle"));
+	if (!r || !r->objects || !r->objects->odb) {
+		ret = error(_("need a repository to verify a bundle"));
+		goto cleanup;
+	}
 
 	repo_init_revisions(r, &revs, NULL);
 	for (i = 0; i < p->nr; i++) {
@@ -221,7 +223,7 @@ int verify_bundle(struct repository *r,
 		error("%s %s", oid_to_hex(oid), name);
 	}
 	if (revs.pending.nr != p->nr)
-		return ret;
+		goto cleanup;
 	req_nr = revs.pending.nr;
 	setup_revisions(2, argv, &revs, NULL);
 
@@ -284,6 +286,8 @@ int verify_bundle(struct repository *r,
 			printf_ln("The bundle uses this filter: %s",
 				  list_objects_filter_spec(&header->filter));
 	}
+cleanup:
+	release_revisions(&revs);
 	return ret;
 }
 
diff --git a/revision.h b/revision.h
index b9070e43428..2621eb6d65a 100644
--- a/revision.h
+++ b/revision.h
@@ -329,6 +329,25 @@ struct rev_info {
 	struct tmp_objdir *remerge_objdir;
 };
 
+/**
+ * Initialize the "struct rev_info" structure with a macro.
+ *
+ * This will not fully initialize a "struct rev_info", the
+ * repo_init_revisions() function needs to be called before
+ * setup_revisions() and any revision walking takes place.
+ *
+ * Use REV_INFO_INIT to make the "struct rev_info" safe for passing to
+ * release_revisions() when it's inconvenient (e.g. due to a "goto
+ * cleanup" pattern) to arrange for repo_init_revisions() to be called
+ * before release_revisions() is called.
+ *
+ * Initializing with this REV_INFO_INIT is redundant to invoking
+ * repo_init_revisions(). If repo_init_revisions() is guaranteed to be
+ * called before release_revisions() the "struct rev_info" can be left
+ * uninitialized.
+ */
+#define REV_INFO_INIT { 0 }
+
 /**
  * Initialize a rev_info structure with default values. The third parameter may
  * be NULL or can be prefix path, and then the `.prefix` variable will be set
@@ -363,7 +382,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs,
 
 /**
  * Free data allocated in a "struct rev_info" after it's been
- * initialized with repo_init_revisions().
+ * initialized with repo_init_revisions() or REV_INFO_INIT.
  */
 void release_revisions(struct rev_info *revs);
 
diff --git a/submodule.c b/submodule.c
index 7b5d7753930..7923e951e1c 100644
--- a/submodule.c
+++ b/submodule.c
@@ -619,7 +619,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,
 		struct object_id *one, struct object_id *two,
 		unsigned dirty_submodule)
 {
-	struct rev_info rev;
+	struct rev_info rev = REV_INFO_INIT;
 	struct commit *left = NULL, *right = NULL;
 	struct commit_list *merge_bases = NULL;
 	struct repository *sub;
@@ -646,6 +646,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,
 
 out:
 	free_commit_list(merge_bases);
+	release_revisions(&rev);
 	clear_commit_marks(left, ~0);
 	clear_commit_marks(right, ~0);
 	if (sub) {
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 10/27] stash: always have the owner of "stash_info" free it
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (8 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 09/27] revisions API users: use release_revisions() needing REV_INFO_INIT Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 11/27] revisions API users: add "goto cleanup" for release_revisions() Ævar Arnfjörð Bjarmason
                           ` (18 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Change the initialization of the "revision" member of "struct
stash_info" to be initialized vi a macro, and more importantly that
that initializing function be tasked to free it, usually via a "goto
cleanup" pattern.

Despite the "revision" name (and the topic of the series containing
this commit) the "stash info" has nothing to do with the "struct
rev_info". I'm making this change because in the subsequent commit
when we do want to free the "struct rev_info" via a "goto cleanup"
pattern we'd otherwise free() uninitialized memory in some cases, as
we only strbuf_init() the string in get_stash_info().

So while it's not the smallest possible change, let's convert all
users of this pattern in the file while we're at it.

A good follow-up to this change would be to change all the "ret = -1;
goto done;" in this file to instead use a "goto cleanup", and
initialize "int ret = -1" at the start of the relevant functions. That
would allow us to drop a lot of needless brace verbosity on two-line
"if" statements, but let's leave that alone for now.

To ensure that there's a 1=1 mapping between owners of the "struct
stash_info" and free_stash_info() change the assert_stash_ref()
function to be a trivial get_stash_info_assert() wrapper. The caller
will call free_stash_info(), and by returning -1 we'll eventually (via
!!ret) exit with status 1 anyway.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/stash.c | 107 ++++++++++++++++++++++++++----------------------
 1 file changed, 59 insertions(+), 48 deletions(-)

diff --git a/builtin/stash.c b/builtin/stash.c
index 74fa810cf8c..ba030238939 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -116,6 +116,10 @@ struct stash_info {
 	int has_u;
 };
 
+#define STASH_INFO_INIT { \
+	.revision = STRBUF_INIT, \
+}
+
 static void free_stash_info(struct stash_info *info)
 {
 	strbuf_release(&info->revision);
@@ -157,10 +161,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
 	if (argc == 1)
 		commit = argv[0];
 
-	strbuf_init(&info->revision, 0);
 	if (!commit) {
 		if (!ref_exists(ref_stash)) {
-			free_stash_info(info);
 			fprintf_ln(stderr, _("No stash entries found."));
 			return -1;
 		}
@@ -174,11 +176,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
 
 	revision = info->revision.buf;
 
-	if (get_oid(revision, &info->w_commit)) {
-		error(_("%s is not a valid reference"), revision);
-		free_stash_info(info);
-		return -1;
-	}
+	if (get_oid(revision, &info->w_commit))
+		return error(_("%s is not a valid reference"), revision);
 
 	assert_stash_like(info, revision);
 
@@ -197,7 +196,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
 		info->is_stash_ref = !strcmp(expanded_ref, ref_stash);
 		break;
 	default: /* Invalid or ambiguous */
-		free_stash_info(info);
+		break;
 	}
 
 	free(expanded_ref);
@@ -598,10 +597,10 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
 
 static int apply_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret;
+	int ret = -1;
 	int quiet = 0;
 	int index = 0;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_BOOL(0, "index", &index,
@@ -613,9 +612,10 @@ static int apply_stash(int argc, const char **argv, const char *prefix)
 			     git_stash_apply_usage, 0);
 
 	if (get_stash_info(&info, argc, argv))
-		return -1;
+		goto cleanup;
 
 	ret = do_apply_stash(prefix, &info, index, quiet);
+cleanup:
 	free_stash_info(&info);
 	return ret;
 }
@@ -651,20 +651,25 @@ static int do_drop_stash(struct stash_info *info, int quiet)
 	return 0;
 }
 
-static void assert_stash_ref(struct stash_info *info)
+static int get_stash_info_assert(struct stash_info *info, int argc,
+				 const char **argv)
 {
-	if (!info->is_stash_ref) {
-		error(_("'%s' is not a stash reference"), info->revision.buf);
-		free_stash_info(info);
-		exit(1);
-	}
+	int ret = get_stash_info(info, argc, argv);
+
+	if (ret < 0)
+		return ret;
+
+	if (!info->is_stash_ref)
+		return error(_("'%s' is not a stash reference"), info->revision.buf);
+
+	return 0;
 }
 
 static int drop_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret;
+	int ret = -1;
 	int quiet = 0;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_END()
@@ -673,22 +678,21 @@ static int drop_stash(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, options,
 			     git_stash_drop_usage, 0);
 
-	if (get_stash_info(&info, argc, argv))
-		return -1;
-
-	assert_stash_ref(&info);
+	if (get_stash_info_assert(&info, argc, argv))
+		goto cleanup;
 
 	ret = do_drop_stash(&info, quiet);
+cleanup:
 	free_stash_info(&info);
 	return ret;
 }
 
 static int pop_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret;
+	int ret = -1;
 	int index = 0;
 	int quiet = 0;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_BOOL(0, "index", &index,
@@ -699,25 +703,25 @@ static int pop_stash(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, options,
 			     git_stash_pop_usage, 0);
 
-	if (get_stash_info(&info, argc, argv))
-		return -1;
+	if (get_stash_info_assert(&info, argc, argv))
+		goto cleanup;
 
-	assert_stash_ref(&info);
 	if ((ret = do_apply_stash(prefix, &info, index, quiet)))
 		printf_ln(_("The stash entry is kept in case "
 			    "you need it again."));
 	else
 		ret = do_drop_stash(&info, quiet);
 
+cleanup:
 	free_stash_info(&info);
 	return ret;
 }
 
 static int branch_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret;
+	int ret = -1;
 	const char *branch = NULL;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct child_process cp = CHILD_PROCESS_INIT;
 	struct option options[] = {
 		OPT_END()
@@ -734,7 +738,7 @@ static int branch_stash(int argc, const char **argv, const char *prefix)
 	branch = argv[0];
 
 	if (get_stash_info(&info, argc - 1, argv + 1))
-		return -1;
+		goto cleanup;
 
 	cp.git_cmd = 1;
 	strvec_pushl(&cp.args, "checkout", "-b", NULL);
@@ -746,8 +750,8 @@ static int branch_stash(int argc, const char **argv, const char *prefix)
 	if (!ret && info.is_stash_ref)
 		ret = do_drop_stash(&info, 0);
 
+cleanup:
 	free_stash_info(&info);
-
 	return ret;
 }
 
@@ -825,8 +829,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op
 static int show_stash(int argc, const char **argv, const char *prefix)
 {
 	int i;
-	int ret = 0;
-	struct stash_info info;
+	int ret = -1;
+	struct stash_info info = STASH_INFO_INIT;
 	struct rev_info rev;
 	struct strvec stash_args = STRVEC_INIT;
 	struct strvec revision_args = STRVEC_INIT;
@@ -844,6 +848,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 			      UNTRACKED_ONLY, PARSE_OPT_NONEG),
 		OPT_END()
 	};
+	int do_usage = 0;
 
 	init_diff_ui_defaults();
 	git_config(git_diff_ui_config, NULL);
@@ -861,10 +866,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 			strvec_push(&revision_args, argv[i]);
 	}
 
-	ret = get_stash_info(&info, stash_args.nr, stash_args.v);
-	strvec_clear(&stash_args);
-	if (ret)
-		return -1;
+	if (get_stash_info(&info, stash_args.nr, stash_args.v))
+		goto cleanup;
 
 	/*
 	 * The config settings are applied only if there are not passed
@@ -878,16 +881,14 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 			rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
 
 		if (!show_stat && !show_patch) {
-			free_stash_info(&info);
-			return 0;
+			ret = 0;
+			goto cleanup;
 		}
 	}
 
 	argc = setup_revisions(revision_args.nr, revision_args.v, &rev, NULL);
-	if (argc > 1) {
-		free_stash_info(&info);
-		usage_with_options(git_stash_show_usage, options);
-	}
+	if (argc > 1)
+		goto usage;
 	if (!rev.diffopt.output_format) {
 		rev.diffopt.output_format = DIFF_FORMAT_PATCH;
 		diff_setup_done(&rev.diffopt);
@@ -912,8 +913,16 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 	}
 	log_tree_diff_flush(&rev);
 
+	ret = diff_result_code(&rev.diffopt, 0);
+cleanup:
+	strvec_clear(&stash_args);
 	free_stash_info(&info);
-	return diff_result_code(&rev.diffopt, 0);
+	if (do_usage)
+		usage_with_options(git_stash_show_usage, options);
+	return ret;
+usage:
+	do_usage = 1;
+	goto cleanup;
 }
 
 static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,
@@ -1409,9 +1418,9 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
 
 static int create_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret = 0;
+	int ret;
 	struct strbuf stash_msg_buf = STRBUF_INIT;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct pathspec ps;
 
 	/* Starting with argv[1], since argv[0] is "create" */
@@ -1426,6 +1435,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)
 	if (!ret)
 		printf_ln("%s", oid_to_hex(&info.w_commit));
 
+	free_stash_info(&info);
 	strbuf_release(&stash_msg_buf);
 	return ret;
 }
@@ -1434,7 +1444,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
 			 int keep_index, int patch_mode, int include_untracked, int only_staged)
 {
 	int ret = 0;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct strbuf patch = STRBUF_INIT;
 	struct strbuf stash_msg_buf = STRBUF_INIT;
 	struct strbuf untracked_files = STRBUF_INIT;
@@ -1633,6 +1643,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
 	}
 
 done:
+	free_stash_info(&info);
 	strbuf_release(&stash_msg_buf);
 	return ret;
 }
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 11/27] revisions API users: add "goto cleanup" for release_revisions()
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (9 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 10/27] stash: always have the owner of "stash_info" free it Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 12/27] revisions API users: use release_revisions() in http-push.c Ævar Arnfjörð Bjarmason
                           ` (17 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Add a release_revisions() to various users of "struct rev_info" which
requires a minor refactoring to a "goto cleanup" pattern to use that
function.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/diff-files.c        |  8 ++++++--
 builtin/rev-list.c          | 19 ++++++++++++-------
 builtin/stash.c             |  1 +
 builtin/submodule--helper.c | 10 +++++++---
 sequencer.c                 | 23 ++++++++++++++++-------
 t/helper/test-fast-rebase.c | 18 +++++++++++++-----
 6 files changed, 55 insertions(+), 24 deletions(-)

diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index 70103c40952..2bfaf9ba7ae 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -77,8 +77,12 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
 
 	if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
 		perror("read_cache_preload");
-		return -1;
+		result = -1;
+		goto cleanup;
 	}
+cleanup:
 	result = run_diff_files(&rev, options);
-	return diff_result_code(&rev.diffopt, result);
+	result = diff_result_code(&rev.diffopt, result);
+	release_revisions(&rev);
+	return result;
 }
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 07c0ad11d8d..30fd8e83eaf 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -500,6 +500,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 	int use_bitmap_index = 0;
 	int filter_provided_objects = 0;
 	const char *show_progress = NULL;
+	int ret = 0;
 
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage(rev_list_usage);
@@ -583,7 +584,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 		}
 		if (!strcmp(arg, "--test-bitmap")) {
 			test_bitmap_walk(&revs);
-			return 0;
+			goto cleanup;
 		}
 		if (skip_prefix(arg, "--progress=", &arg)) {
 			show_progress = arg;
@@ -672,11 +673,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 
 	if (use_bitmap_index) {
 		if (!try_bitmap_count(&revs, filter_provided_objects))
-			return 0;
+			goto cleanup;
 		if (!try_bitmap_disk_usage(&revs, filter_provided_objects))
-			return 0;
+			goto cleanup;
 		if (!try_bitmap_traversal(&revs, filter_provided_objects))
-			return 0;
+			goto cleanup;
 	}
 
 	if (prepare_revision_walk(&revs))
@@ -696,8 +697,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 
 		find_bisection(&revs.commits, &reaches, &all, bisect_flags);
 
-		if (bisect_show_vars)
-			return show_bisect_vars(&info, reaches, all);
+		if (bisect_show_vars) {
+			ret = show_bisect_vars(&info, reaches, all);
+			goto cleanup;
+		}
 	}
 
 	if (filter_provided_objects) {
@@ -752,5 +755,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 	if (show_disk_usage)
 		printf("%"PRIuMAX"\n", (uintmax_t)total_disk_usage);
 
-	return 0;
+cleanup:
+	release_revisions(&revs);
+	return ret;
 }
diff --git a/builtin/stash.c b/builtin/stash.c
index ba030238939..fce4393d123 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -917,6 +917,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 cleanup:
 	strvec_clear(&stash_args);
 	free_stash_info(&info);
+	release_revisions(&rev);
 	if (do_usage)
 		usage_with_options(git_stash_show_usage, options);
 	return ret;
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index cda33ee4d2b..1bd31c85946 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1232,6 +1232,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 	struct strvec diff_args = STRVEC_INIT;
 	struct rev_info rev;
 	struct module_cb_list list = MODULE_CB_LIST_INIT;
+	int ret = 0;
 
 	strvec_push(&diff_args, get_diff_cmd(diff_cmd));
 	if (info->cached)
@@ -1257,11 +1258,13 @@ static int compute_summary_module_list(struct object_id *head_oid,
 			setup_work_tree();
 		if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
 			perror("read_cache_preload");
-			return -1;
+			ret = -1;
+			goto cleanup;
 		}
 	} else if (read_cache() < 0) {
 		perror("read_cache");
-		return -1;
+		ret = -1;
+		goto cleanup;
 	}
 
 	if (diff_cmd == DIFF_INDEX)
@@ -1269,9 +1272,10 @@ static int compute_summary_module_list(struct object_id *head_oid,
 	else
 		run_diff_files(&rev, 0);
 	prepare_submodule_summary(info, &list);
+cleanup:
 	strvec_clear(&diff_args);
 	release_revisions(&rev);
-	return 0;
+	return ret;
 }
 
 static int module_summary(int argc, const char **argv, const char *prefix)
diff --git a/sequencer.c b/sequencer.c
index f9d7acd1065..41ae5e25278 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -5354,6 +5354,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
 	int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
 	int reapply_cherry_picks = flags & TODO_LIST_REAPPLY_CHERRY_PICKS;
 	int skipped_commit = 0;
+	int ret = 0;
 
 	repo_init_revisions(r, &revs, NULL);
 	revs.verbose_header = 1;
@@ -5377,14 +5378,20 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
 	pp.fmt = revs.commit_format;
 	pp.output_encoding = get_log_output_encoding();
 
-	if (setup_revisions(argc, argv, &revs, NULL) > 1)
-		return error(_("make_script: unhandled options"));
+	if (setup_revisions(argc, argv, &revs, NULL) > 1) {
+		ret = error(_("make_script: unhandled options"));
+		goto cleanup;
+	}
 
-	if (prepare_revision_walk(&revs) < 0)
-		return error(_("make_script: error preparing revisions"));
+	if (prepare_revision_walk(&revs) < 0) {
+		ret = error(_("make_script: error preparing revisions"));
+		goto cleanup;
+	}
 
-	if (rebase_merges)
-		return make_script_with_merges(&pp, &revs, out, flags);
+	if (rebase_merges) {
+		ret = make_script_with_merges(&pp, &revs, out, flags);
+		goto cleanup;
+	}
 
 	while ((commit = get_revision(&revs))) {
 		int is_empty = is_original_commit_empty(commit);
@@ -5408,7 +5415,9 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
 	if (skipped_commit)
 		advise_if_enabled(ADVICE_SKIPPED_CHERRY_PICKS,
 				  _("use --reapply-cherry-picks to include skipped commits"));
-	return 0;
+cleanup:
+	release_revisions(&revs);
+	return ret;
 }
 
 /*
diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c
index 993b90eaedd..4e5553e2024 100644
--- a/t/helper/test-fast-rebase.c
+++ b/t/helper/test-fast-rebase.c
@@ -99,6 +99,7 @@ int cmd__fast_rebase(int argc, const char **argv)
 	struct merge_result result;
 	struct strbuf reflog_msg = STRBUF_INIT;
 	struct strbuf branch_name = STRBUF_INIT;
+	int ret = 0;
 
 	/*
 	 * test-tool stuff doesn't set up the git directory by default; need to
@@ -137,13 +138,17 @@ int cmd__fast_rebase(int argc, const char **argv)
 	revs.topo_order = 1;
 	strvec_pushl(&rev_walk_args, "", argv[4], "--not", argv[3], NULL);
 
-	if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1)
-		return error(_("unhandled options"));
+	if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) {
+		ret = error(_("unhandled options"));
+		goto cleanup;
+	}
 
 	strvec_clear(&rev_walk_args);
 
-	if (prepare_revision_walk(&revs) < 0)
-		return error(_("error preparing revisions"));
+	if (prepare_revision_walk(&revs) < 0) {
+		ret = error(_("error preparing revisions"));
+		goto cleanup;
+	}
 
 	init_merge_options(&merge_opt, the_repository);
 	memset(&result, 0, sizeof(result));
@@ -220,7 +225,10 @@ int cmd__fast_rebase(int argc, const char **argv)
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("unable to write %s"), get_index_file());
 
+	ret = (result.clean == 0);
+cleanup:
 	strbuf_release(&reflog_msg);
 	strbuf_release(&branch_name);
-	return (result.clean == 0);
+	release_revisions(&revs);
+	return ret;
 }
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 12/27] revisions API users: use release_revisions() in http-push.c
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (10 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 11/27] revisions API users: add "goto cleanup" for release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 13/27] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
                           ` (16 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

In the case of cmd_main() in http-push.c we need to move the
deceleration of the "struct rev-list" into the loop over the
"remote_refs" when adding a release_revisions().

We'd previously set up the "revs" for each remote, but would
potentially leak memory on each one.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 http-push.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/http-push.c b/http-push.c
index f0c044dcf76..01e7c2ac5c8 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1689,7 +1689,6 @@ int cmd_main(int argc, const char **argv)
 	struct refspec rs = REFSPEC_INIT_PUSH;
 	struct remote_lock *ref_lock = NULL;
 	struct remote_lock *info_ref_lock = NULL;
-	struct rev_info revs;
 	int delete_branch = 0;
 	int force_delete = 0;
 	int objects_to_send;
@@ -1825,6 +1824,7 @@ int cmd_main(int argc, const char **argv)
 
 	new_refs = 0;
 	for (ref = remote_refs; ref; ref = ref->next) {
+		struct rev_info revs;
 		struct strvec commit_argv = STRVEC_INIT;
 
 		if (!ref->peer_ref)
@@ -1941,6 +1941,7 @@ int cmd_main(int argc, const char **argv)
 		unlock_remote(ref_lock);
 		check_locks();
 		strvec_clear(&commit_argv);
+		release_revisions(&revs);
 	}
 
 	/* Update remote server info if appropriate */
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 13/27] revisions API users: use release_revisions() in builtin/log.c
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (11 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 12/27] revisions API users: use release_revisions() in http-push.c Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 14/27] revisions API users: use release_revisions() with UNLEAK() Ævar Arnfjörð Bjarmason
                           ` (15 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

In preparation for having the "log" family of functions make wider use
of release_revisions() let's have them call it just before
exiting. This changes the "log", "whatchanged", "show",
"format-patch", etc. commands, all of which live in this file.

The release_revisions() API still only frees the "pending" member, but
will learn to release more members of "struct rev_info" in subsequent
commits.

In the case of "format-patch" revert the addition of UNLEAK() in
dee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16),
which will cause several tests that previously passed under
"TEST_PASSES_SANITIZE_LEAK=true" to start failing.

In subsequent commits we'll now be able to use those tests to check
whether that part of the API is really leaking memory, and will fix
all of those memory leaks. Removing the UNLEAK() allows us to make
incremental progress in that direction. See [1] for further details
about this approach.

Note that the release_revisions() will not be sufficient to deal with
the code in cmd_show() added in 5d7eeee2ac6 (git-show: grok blobs,
trees and tags, too, 2006-12-14) which clobbers the "pending" array in
the case of "OBJ_COMMIT". That will need to be dealt with by some
future follow-up work.

1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/log.c          | 20 ++++++++++++--------
 t/t4126-apply-empty.sh |  2 --
 2 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/builtin/log.c b/builtin/log.c
index 6f9928fabfe..c40ebe1c3f4 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -295,6 +295,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
 	cmd_log_init_finish(argc, argv, prefix, rev, opt);
 }
 
+static int cmd_log_deinit(int ret, struct rev_info *rev)
+{
+	release_revisions(rev);
+	return ret;
+}
+
 /*
  * This gives a rough estimate for how many commits we
  * will print out in the list.
@@ -558,7 +564,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
 	cmd_log_init(argc, argv, prefix, &rev, &opt);
 	if (!rev.diffopt.output_format)
 		rev.diffopt.output_format = DIFF_FORMAT_RAW;
-	return cmd_log_walk(&rev);
+	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 }
 
 static void show_tagger(const char *buf, struct rev_info *rev)
@@ -677,7 +683,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
 	cmd_log_init(argc, argv, prefix, &rev, &opt);
 
 	if (!rev.no_walk)
-		return cmd_log_walk(&rev);
+		return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 
 	count = rev.pending.nr;
 	objects = rev.pending.objects;
@@ -732,8 +738,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
 			ret = error(_("unknown type: %d"), o->type);
 		}
 	}
-	free(objects);
-	return ret;
+	return cmd_log_deinit(ret, &rev);
 }
 
 /*
@@ -761,7 +766,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
 	rev.always_show_header = 1;
 	cmd_log_init_finish(argc, argv, prefix, &rev, &opt);
 
-	return cmd_log_walk(&rev);
+	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 }
 
 static void log_setup_revisions_tweak(struct rev_info *rev,
@@ -792,7 +797,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)
 	opt.revarg_opt = REVARG_COMMITTISH;
 	opt.tweak = log_setup_revisions_tweak;
 	cmd_log_init(argc, argv, prefix, &rev, &opt);
-	return cmd_log_walk(&rev);
+	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 }
 
 /* format-patch */
@@ -2289,8 +2294,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	if (rev.ref_message_ids)
 		string_list_clear(rev.ref_message_ids, 0);
 	free(rev.ref_message_ids);
-	UNLEAK(rev);
-	return 0;
+	return cmd_log_deinit(0, &rev);
 }
 
 static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
diff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh
index 33860d38290..66a7ba8ab8f 100755
--- a/t/t4126-apply-empty.sh
+++ b/t/t4126-apply-empty.sh
@@ -2,8 +2,6 @@
 
 test_description='apply empty'
 
-
-TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 14/27] revisions API users: use release_revisions() with UNLEAK()
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (12 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 13/27] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-03  9:27           ` Phillip Wood
  2022-04-02 10:49         ` [PATCH v5 15/27] revisions API users: use release_revisions() for "prune_data" users Ævar Arnfjörð Bjarmason
                           ` (14 subsequent siblings)
  28 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Use a release_revisions() with those "struct rev_list" users which
already "UNLEAK" the struct. It may seem odd to simultaneously attempt
to free() memory, but also to explicitly ignore whether we have memory
leaks in the same.

As explained in preceding commits this is being done to use the
built-in commands as a guinea pig for whether the release_revisions()
function works as expected, we'd like to test e.g. whether we segfault
as we change it. In subsequent commits we'll then remove these
UNLEAK() as the function is made to free the memory that caused us to
add them in the first place.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/diff-index.c | 4 +++-
 builtin/diff.c       | 1 +
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 5fd23ab5b6c..3a83183c312 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -71,5 +71,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
 	}
 	result = run_diff_index(&rev, option);
 	UNLEAK(rev);
-	return diff_result_code(&rev.diffopt, result);
+	result = diff_result_code(&rev.diffopt, result);
+	release_revisions(&rev);
+	return result;
 }
diff --git a/builtin/diff.c b/builtin/diff.c
index bb7fafca618..dd48336da56 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -595,6 +595,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
 	if (1 < rev.diffopt.skip_stat_unmatch)
 		refresh_index_quietly();
 	UNLEAK(rev);
+	release_revisions(&rev);
 	UNLEAK(ent);
 	UNLEAK(blob);
 	return result;
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 15/27] revisions API users: use release_revisions() for "prune_data" users
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (13 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 14/27] revisions API users: use release_revisions() with UNLEAK() Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 16/27] revisions API: have release_revisions() release "commits" Ævar Arnfjörð Bjarmason
                           ` (13 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Use release_revisions() for users of "struct rev_list" that reach into
the "struct rev_info" and clear the "prune_data" already.

In a subsequent commit we'll teach release_revisions() to clear this
itself, but in the meantime let's invoke release_revisions() here to
clear anything else we may have missed, and for reasons of having
consistent boilerplate.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/add.c | 1 +
 diff-lib.c    | 1 +
 wt-status.c   | 1 +
 3 files changed, 3 insertions(+)

diff --git a/builtin/add.c b/builtin/add.c
index f507d2191cd..115a26ea633 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -143,6 +143,7 @@ int add_files_to_cache(const char *prefix,
 	rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
 	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 	return !!data.add_errors;
 }
 
diff --git a/diff-lib.c b/diff-lib.c
index d6800274bd5..0f16281253f 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -642,6 +642,7 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
 	if (diff_cache(&revs, tree_oid, NULL, 1))
 		exit(128);
 	clear_pathspec(&revs.prune_data);
+	release_revisions(&revs);
 	return 0;
 }
 
diff --git a/wt-status.c b/wt-status.c
index f9100621375..a14fad1e03a 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -617,6 +617,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_files(&rev, 0);
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 }
 
 static void wt_status_collect_changes_index(struct wt_status *s)
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 16/27] revisions API: have release_revisions() release "commits"
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (14 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 15/27] revisions API users: use release_revisions() for "prune_data" users Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 17/27] revisions API: have release_revisions() release "mailmap" Ævar Arnfjörð Bjarmason
                           ` (12 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"commits" in the "struct rev_info".

We don't expect to use this "struct rev_info" again, so there's no
reason to NULL out revs->commits, as e.g. simplify_merges() and
create_boundary_commit_list() do.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/revision.c b/revision.c
index 472fff1e0a7..553f7de8250 100644
--- a/revision.c
+++ b/revision.c
@@ -2928,6 +2928,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 
 void release_revisions(struct rev_info *revs)
 {
+	free_commit_list(revs->commits);
 	object_array_clear(&revs->pending);
 }
 
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 17/27] revisions API: have release_revisions() release "mailmap"
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (15 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 16/27] revisions API: have release_revisions() release "commits" Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-03  9:33           ` Phillip Wood
  2022-04-02 10:49         ` [PATCH v5 18/27] revisions API: have release_revisions() release "cmdline" Ævar Arnfjörð Bjarmason
                           ` (11 subsequent siblings)
  28 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"mailmap" in the "struct rev_info".

The log family of functions now calls the clear_mailmap() function
added in fa8afd18e5a (revisions API: provide and use a
release_revisions(), 2021-09-19), allowing us to whitelist some tests
with "TEST_PASSES_SANITIZE_LEAK=true".

Unfortunately having a pointer to a mailmap in "struct rev_info"
instead of an embedded member that we "own" get a bit messy, as can be
seen in the change to builtin/commit.c.

When we free() this data we won't be able to tell apart a pointer to a
"mailmap" on the heap from one on the stack. As seen in
ea57bc0d41b (log: add --use-mailmap option, 2013-01-05) the "log"
family allocates it on the heap, but in the find_author_by_nickname()
code added in ea16794e430 (commit: search author pattern against
mailmap, 2013-08-23) we allocated it on the stack instead.

Ideally we'd simply change that member to a "struct string_list
mailmap" and never free() the "mailmap" itself, but that would be a
much larger change to the revisions API.

We have code that needs to hand an existing "mailmap" to a "struct
rev_info", while we could change all of that, let's not go there
now.

The complexity isn't in the ownership of the "mailmap" per-se, but
that various things assume a "rev_info.mailmap == NULL" means "doesn't
want mailmap", if we changed that to an init'd "struct string_list
we'd need to carefully refactor things to change those assumptions.

Let's instead always free() it, and simply declare that if you add
such a "mailmap" it must be allocated on the heap. Any modern libc
will correctly panic if we free() a stack variable, so this should be
safe going forward.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/commit.c                   | 5 ++---
 revision.c                         | 9 +++++++++
 t/t0056-git-C.sh                   | 1 +
 t/t3302-notes-index-expensive.sh   | 1 +
 t/t4055-diff-context.sh            | 1 +
 t/t4066-diff-emit-delay.sh         | 1 +
 t/t7008-filter-branch-null-sha1.sh | 1 +
 7 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index c7eda9bbb72..cd6cebcf8c8 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1100,7 +1100,6 @@ static const char *find_author_by_nickname(const char *name)
 	struct rev_info revs;
 	struct commit *commit;
 	struct strbuf buf = STRBUF_INIT;
-	struct string_list mailmap = STRING_LIST_INIT_NODUP;
 	const char *av[20];
 	int ac = 0;
 
@@ -1111,7 +1110,8 @@ static const char *find_author_by_nickname(const char *name)
 	av[++ac] = buf.buf;
 	av[++ac] = NULL;
 	setup_revisions(ac, av, &revs, NULL);
-	revs.mailmap = &mailmap;
+	revs.mailmap = xmalloc(sizeof(struct string_list));
+	string_list_init_nodup(revs.mailmap);
 	read_mailmap(revs.mailmap);
 
 	if (prepare_revision_walk(&revs))
@@ -1122,7 +1122,6 @@ static const char *find_author_by_nickname(const char *name)
 		ctx.date_mode.type = DATE_NORMAL;
 		strbuf_release(&buf);
 		format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
-		clear_mailmap(&mailmap);
 		release_revisions(&revs);
 		return strbuf_detach(&buf, NULL);
 	}
diff --git a/revision.c b/revision.c
index 553f7de8250..622f0faecc4 100644
--- a/revision.c
+++ b/revision.c
@@ -2926,10 +2926,19 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 	return left;
 }
 
+static void release_revisions_mailmap(struct string_list *mailmap)
+{
+	if (!mailmap)
+		return;
+	clear_mailmap(mailmap);
+	free(mailmap);
+}
+
 void release_revisions(struct rev_info *revs)
 {
 	free_commit_list(revs->commits);
 	object_array_clear(&revs->pending);
+	release_revisions_mailmap(revs->mailmap);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
diff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh
index 2630e756dab..752aa8c9454 100755
--- a/t/t0056-git-C.sh
+++ b/t/t0056-git-C.sh
@@ -2,6 +2,7 @@
 
 test_description='"-C <path>" option and its effects on other path-related options'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success '"git -C <path>" runs git from the directory <path>' '
diff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh
index bb5fea02a03..d0c4d38b4d4 100755
--- a/t/t3302-notes-index-expensive.sh
+++ b/t/t3302-notes-index-expensive.sh
@@ -8,6 +8,7 @@ test_description='Test commit notes index (expensive!)'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 create_repo () {
diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh
index 741e0803c1a..73048d0a526 100755
--- a/t/t4055-diff-context.sh
+++ b/t/t4055-diff-context.sh
@@ -5,6 +5,7 @@
 
 test_description='diff.context configuration'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t4066-diff-emit-delay.sh b/t/t4066-diff-emit-delay.sh
index a1de63b77f8..0ecb3915412 100755
--- a/t/t4066-diff-emit-delay.sh
+++ b/t/t4066-diff-emit-delay.sh
@@ -4,6 +4,7 @@ test_description='test combined/stat/moved interaction'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # This test covers a weird 3-way interaction between "--cc -p", which will run
diff --git a/t/t7008-filter-branch-null-sha1.sh b/t/t7008-filter-branch-null-sha1.sh
index 9ba9f24ad2f..93fbc92b8db 100755
--- a/t/t7008-filter-branch-null-sha1.sh
+++ b/t/t7008-filter-branch-null-sha1.sh
@@ -1,6 +1,7 @@
 #!/bin/sh
 
 test_description='filter-branch removal of trees with null sha1'
+
 . ./test-lib.sh
 
 test_expect_success 'setup: base commits' '
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 18/27] revisions API: have release_revisions() release "cmdline"
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (16 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 17/27] revisions API: have release_revisions() release "mailmap" Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-03 12:54           ` Phillip Wood
  2022-04-02 10:49         ` [PATCH v5 19/27] revisions API: have release_revisions() release "filter" Ævar Arnfjörð Bjarmason
                           ` (10 subsequent siblings)
  28 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"cmdline" in the "struct rev_info". This in combination with a
preceding change to free "commits" and "mailmap" means that we can
whitelist another test under "TEST_PASSES_SANITIZE_LEAK=true".

There was a proposal in [1] to do away with xstrdup()-ing this
add_rev_cmdline(), perhaps that would be worthwhile, but for now let's
just free() it.

We could also make that a "char *" in "struct rev_cmdline_entry"
itself, but since we own it let's expose it as a constant to outside
callers. I proposed that in [2] but have since changed my mind. See
14d30cdfc04 (ref-filter: fix memory leak in `free_array_item()`,
2019-07-10), c514c62a4fd (checkout: fix leak of non-existent branch
names, 2020-08-14) and other log history hits for "free((char *)" for
prior art.

This includes the tests we had false-positive passes on before my
6798b08e848 (perl Git.pm: don't ignore signalled failure in
_cmd_close(), 2022-02-01), now they pass for real.

Since there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to
list those that don't pass than to touch most of those 66. So let's
introduce a "TEST_FAILS_SANITIZE_LEAK=true", which if set in the tests
won't cause lib-git-svn.sh to set "TEST_PASSES_SANITIZE_LEAK=true.

This change also marks all the tests that we removed
"TEST_FAILS_SANITIZE_LEAK=true" from in an earlier commit due to
removing the UNLEAK() from cmd_format_patch(), we can now assert that
its API use doesn't leak any "struct rev_info" memory.

This change also made commit "t5503-tagfollow.sh" pass on current
master, but that would regress when combined with
ps/fetch-atomic-fixup's de004e848a9 (t5503: simplify setup of test
which exercises failure of backfill, 2022-03-03) (through no fault of
that topic, that change started using "git clone" in the test, which
has an outstanding leak). Let's leave that test out for now to avoid
in-flight semantic conflicts.

1. https://lore.kernel.org/git/YUj%2FgFRh6pwrZalY@carlos-mbp.lan/
2. https://lore.kernel.org/git/87o88obkb1.fsf@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c                                 | 12 ++++++++++++
 t/lib-git-svn.sh                           |  4 ++++
 t/t0062-revision-walking.sh                |  1 +
 t/t0101-at-syntax.sh                       |  2 ++
 t/t1060-object-corruption.sh               |  1 +
 t/t3303-notes-subtrees.sh                  |  1 +
 t/t3305-notes-fanout.sh                    |  1 +
 t/t3408-rebase-multi-line.sh               |  1 +
 t/t4027-diff-submodule.sh                  |  1 +
 t/t4128-apply-root.sh                      |  1 +
 t/t4212-log-corrupt.sh                     |  1 +
 t/t5515-fetch-merge-logic.sh               |  1 +
 t/t5518-fetch-exit-status.sh               |  1 +
 t/t6002-rev-list-bisect.sh                 |  1 +
 t/t6003-rev-list-topo-order.sh             |  1 +
 t/t6005-rev-list-count.sh                  |  1 +
 t/t6018-rev-list-glob.sh                   |  1 +
 t/t6100-rev-list-in-order.sh               |  1 +
 t/t9100-git-svn-basic.sh                   |  1 +
 t/t9101-git-svn-props.sh                   |  2 ++
 t/t9104-git-svn-follow-parent.sh           |  2 ++
 t/t9106-git-svn-commit-diff-clobber.sh     |  2 ++
 t/t9115-git-svn-dcommit-funky-renames.sh   |  1 +
 t/t9116-git-svn-log.sh                     |  2 ++
 t/t9122-git-svn-author.sh                  |  2 ++
 t/t9127-git-svn-partial-rebuild.sh         |  2 ++
 t/t9129-git-svn-i18n-commitencoding.sh     |  1 +
 t/t9132-git-svn-broken-symlink.sh          |  1 +
 t/t9139-git-svn-non-utf8-commitencoding.sh |  1 +
 t/t9146-git-svn-empty-dirs.sh              |  2 ++
 t/t9148-git-svn-propset.sh                 |  1 +
 t/t9151-svn-mergeinfo.sh                   |  1 +
 t/t9160-git-svn-preserve-empty-dirs.sh     |  1 +
 t/t9162-git-svn-dcommit-interactive.sh     |  2 ++
 t/t9164-git-svn-dcommit-concurrent.sh      |  2 ++
 t/t9501-gitweb-standalone-http-status.sh   |  1 +
 36 files changed, 60 insertions(+)

diff --git a/revision.c b/revision.c
index 622f0faecc4..c81506fa22f 100644
--- a/revision.c
+++ b/revision.c
@@ -2926,6 +2926,17 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 	return left;
 }
 
+static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)
+{
+	unsigned int i;
+
+	if (!cmdline)
+		return;
+	for (i = 0; i < cmdline->nr; i++)
+		free((char *)cmdline->rev[i].name);
+	free(cmdline->rev);
+}
+
 static void release_revisions_mailmap(struct string_list *mailmap)
 {
 	if (!mailmap)
@@ -2938,6 +2949,7 @@ void release_revisions(struct rev_info *revs)
 {
 	free_commit_list(revs->commits);
 	object_array_clear(&revs->pending);
+	release_revisions_cmdline(&revs->cmdline);
 	release_revisions_mailmap(revs->mailmap);
 }
 
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 2fde2353fd3..ea28971e8ee 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -1,3 +1,7 @@
+if test -z "$TEST_FAILS_SANITIZE_LEAK"
+then
+	TEST_PASSES_SANITIZE_LEAK=true
+fi
 . ./test-lib.sh
 
 if test -n "$NO_SVN_TESTS"
diff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh
index 8e215867b8c..b9480c81781 100755
--- a/t/t0062-revision-walking.sh
+++ b/t/t0062-revision-walking.sh
@@ -5,6 +5,7 @@
 
 test_description='Test revision walking api'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 cat >run_twice_expected <<-EOF
diff --git a/t/t0101-at-syntax.sh b/t/t0101-at-syntax.sh
index a1998b558f9..878aadd64c9 100755
--- a/t/t0101-at-syntax.sh
+++ b/t/t0101-at-syntax.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='various @{whatever} syntax tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh
index bc89371f534..e8a58b15897 100755
--- a/t/t1060-object-corruption.sh
+++ b/t/t1060-object-corruption.sh
@@ -1,6 +1,7 @@
 #!/bin/sh
 
 test_description='see how we handle various forms of corruption'
+
 . ./test-lib.sh
 
 # convert "1234abcd" to ".git/objects/12/34abcd"
diff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh
index eac193757bf..bc9b791d1b9 100755
--- a/t/t3303-notes-subtrees.sh
+++ b/t/t3303-notes-subtrees.sh
@@ -5,6 +5,7 @@ test_description='Test commit notes organized in subtrees'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 number_of_commits=100
diff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh
index 9976d787f47..64a9915761a 100755
--- a/t/t3305-notes-fanout.sh
+++ b/t/t3305-notes-fanout.sh
@@ -2,6 +2,7 @@
 
 test_description='Test that adding/removing many notes triggers automatic fanout restructuring'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 path_has_fanout() {
diff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh
index cde3562e3a6..7b4607d72f2 100755
--- a/t/t3408-rebase-multi-line.sh
+++ b/t/t3408-rebase-multi-line.sh
@@ -5,6 +5,7 @@ test_description='rebasing a commit with multi-line first paragraph.'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
index 295da987cce..40164ae07d2 100755
--- a/t/t4027-diff-submodule.sh
+++ b/t/t4027-diff-submodule.sh
@@ -2,6 +2,7 @@
 
 test_description='difference in submodules'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh
 
diff --git a/t/t4128-apply-root.sh b/t/t4128-apply-root.sh
index f6db5a79dd9..ed94c90204e 100755
--- a/t/t4128-apply-root.sh
+++ b/t/t4128-apply-root.sh
@@ -2,6 +2,7 @@
 
 test_description='apply same filename'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh
index 0244888a5a7..30a219894bb 100755
--- a/t/t4212-log-corrupt.sh
+++ b/t/t4212-log-corrupt.sh
@@ -2,6 +2,7 @@
 
 test_description='git log with invalid commit headers'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh
index 320d26796d2..c100a809c5e 100755
--- a/t/t5515-fetch-merge-logic.sh
+++ b/t/t5515-fetch-merge-logic.sh
@@ -14,6 +14,7 @@ export GIT_TEST_PROTOCOL_VERSION
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 build_script () {
diff --git a/t/t5518-fetch-exit-status.sh b/t/t5518-fetch-exit-status.sh
index 5c4ac2556e7..c13120088fa 100755
--- a/t/t5518-fetch-exit-status.sh
+++ b/t/t5518-fetch-exit-status.sh
@@ -8,6 +8,7 @@ test_description='fetch exit status test'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index b95a0212adf..162cf50778d 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -4,6 +4,7 @@
 #
 test_description='Tests git rev-list --bisect functionality'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
 
diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh
index 24d1836f417..1f7d7dd20c1 100755
--- a/t/t6003-rev-list-topo-order.sh
+++ b/t/t6003-rev-list-topo-order.sh
@@ -5,6 +5,7 @@
 
 test_description='Tests git rev-list --topo-order functionality'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
 
diff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh
index e960049f647..0729f800c3c 100755
--- a/t/t6005-rev-list-count.sh
+++ b/t/t6005-rev-list-count.sh
@@ -2,6 +2,7 @@
 
 test_description='git rev-list --max-count and --skip test'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
index 24b34add833..e1abc5c2b32 100755
--- a/t/t6018-rev-list-glob.sh
+++ b/t/t6018-rev-list-glob.sh
@@ -5,6 +5,7 @@ test_description='rev-list/rev-parse --glob'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 commit () {
diff --git a/t/t6100-rev-list-in-order.sh b/t/t6100-rev-list-in-order.sh
index e934bc239c5..88ed7bd75a7 100755
--- a/t/t6100-rev-list-in-order.sh
+++ b/t/t6100-rev-list-in-order.sh
@@ -2,6 +2,7 @@
 
 test_description='rev-list testing in-commit-order'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a commit history with trees, blobs' '
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index fea41b3c360..7c5b847f584 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -8,6 +8,7 @@ test_description='git svn basic tests'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 prepare_utf8_locale
diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
index 8b5681dd68a..d043e80fc34 100755
--- a/t/t9101-git-svn-props.sh
+++ b/t/t9101-git-svn-props.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn property tests'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 mkdir import
diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
index c7d8e0bf00f..5cf2ef4b8b0 100755
--- a/t/t9104-git-svn-follow-parent.sh
+++ b/t/t9104-git-svn-follow-parent.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn fetching'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
index aec45bca3b7..3cab0b9720a 100755
--- a/t/t9106-git-svn-commit-diff-clobber.sh
+++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -2,6 +2,8 @@
 #
 # Copyright (c) 2006 Eric Wong
 test_description='git svn commit-diff clobber'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh
index 743fbe1fe46..419f055721d 100755
--- a/t/t9115-git-svn-dcommit-funky-renames.sh
+++ b/t/t9115-git-svn-dcommit-funky-renames.sh
@@ -5,6 +5,7 @@
 
 test_description='git svn dcommit can commit renames of files with ugly names'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'load repository with strange names' '
diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh
index 0a9f1ef366d..34f6c80dea3 100755
--- a/t/t9116-git-svn-log.sh
+++ b/t/t9116-git-svn-log.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn log tests'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup repository and import' '
diff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh
index 9e8fe38e7ef..527ba3d2932 100755
--- a/t/t9122-git-svn-author.sh
+++ b/t/t9122-git-svn-author.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='git svn authorship'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup svn repository' '
diff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh
index 2e4789d061f..90b1b30dde5 100755
--- a/t/t9127-git-svn-partial-rebuild.sh
+++ b/t/t9127-git-svn-partial-rebuild.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn partial-rebuild tests'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize svnrepo' '
diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh
index 01e1e8a8f76..185248a4cd7 100755
--- a/t/t9129-git-svn-i18n-commitencoding.sh
+++ b/t/t9129-git-svn-i18n-commitencoding.sh
@@ -4,6 +4,7 @@
 
 test_description='git svn honors i18n.commitEncoding in config'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 compare_git_head_with () {
diff --git a/t/t9132-git-svn-broken-symlink.sh b/t/t9132-git-svn-broken-symlink.sh
index aeceffaf7b0..4d8d0584b79 100755
--- a/t/t9132-git-svn-broken-symlink.sh
+++ b/t/t9132-git-svn-broken-symlink.sh
@@ -2,6 +2,7 @@
 
 test_description='test that git handles an svn repository with empty symlinks'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 test_expect_success 'load svn dumpfile' '
 	svnadmin load "$rawsvnrepo" <<EOF
diff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh
index 22d80b0be2b..b7f756b2b7f 100755
--- a/t/t9139-git-svn-non-utf8-commitencoding.sh
+++ b/t/t9139-git-svn-non-utf8-commitencoding.sh
@@ -4,6 +4,7 @@
 
 test_description='git svn refuses to dcommit non-UTF8 messages'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 # ISO-2022-JP can pass for valid UTF-8, so skipping that in this test
diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh
index 80cb55fee70..79c26ed69c1 100755
--- a/t/t9146-git-svn-empty-dirs.sh
+++ b/t/t9146-git-svn-empty-dirs.sh
@@ -3,6 +3,8 @@
 # Copyright (c) 2009 Eric Wong
 
 test_description='git svn creates empty directories'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9148-git-svn-propset.sh b/t/t9148-git-svn-propset.sh
index aebb28995e5..6cc76a07b39 100755
--- a/t/t9148-git-svn-propset.sh
+++ b/t/t9148-git-svn-propset.sh
@@ -5,6 +5,7 @@
 
 test_description='git svn propset tests'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup propset via import' '
diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh
index c93a5beab25..85221d439bd 100755
--- a/t/t9151-svn-mergeinfo.sh
+++ b/t/t9151-svn-mergeinfo.sh
@@ -5,6 +5,7 @@
 
 test_description='git-svn svn mergeinfo properties'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'load svn dump' "
diff --git a/t/t9160-git-svn-preserve-empty-dirs.sh b/t/t9160-git-svn-preserve-empty-dirs.sh
index 36c6b1a12ff..9cf7a1427ab 100755
--- a/t/t9160-git-svn-preserve-empty-dirs.sh
+++ b/t/t9160-git-svn-preserve-empty-dirs.sh
@@ -9,6 +9,7 @@ This test uses git to clone a Subversion repository that contains empty
 directories, and checks that corresponding directories are created in the
 local Git repository with placeholder files.'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 GIT_REPO=git-svn-repo
diff --git a/t/t9162-git-svn-dcommit-interactive.sh b/t/t9162-git-svn-dcommit-interactive.sh
index e38d9fa37b5..e2aa8ed88a9 100755
--- a/t/t9162-git-svn-dcommit-interactive.sh
+++ b/t/t9162-git-svn-dcommit-interactive.sh
@@ -3,6 +3,8 @@
 # Copyright (c) 2011 Frédéric Heitzmann
 
 test_description='git svn dcommit --interactive series'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9164-git-svn-dcommit-concurrent.sh b/t/t9164-git-svn-dcommit-concurrent.sh
index 8466269bf50..1465156072e 100755
--- a/t/t9164-git-svn-dcommit-concurrent.sh
+++ b/t/t9164-git-svn-dcommit-concurrent.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='concurrent git svn dcommit'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 
diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh
index 32814e75df5..c900231079c 100755
--- a/t/t9501-gitweb-standalone-http-status.sh
+++ b/t/t9501-gitweb-standalone-http-status.sh
@@ -13,6 +13,7 @@ code and message.'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./lib-gitweb.sh
 
 #
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 19/27] revisions API: have release_revisions() release "filter"
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (17 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 18/27] revisions API: have release_revisions() release "cmdline" Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 20/27] revisions API: have release_revisions() release "grep_filter" Ævar Arnfjörð Bjarmason
                           ` (9 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"filter" in the "struct rev_info". This in combination with a
preceding change to free "cmdline" means that we can mark another set
of tests as passing under "TEST_PASSES_SANITIZE_LEAK=true".

The "filter" member was added recently in ffaa137f646 (revision: put
object filter into struct rev_info, 2022-03-09), and this fixes leaks
intruded in the subsequent leak 7940941de1f (pack-objects: use
rev.filter when possible, 2022-03-09) and 105c6f14ad3 (bundle: parse
filter capability, 2022-03-09).

The "builtin/pack-objects.c" leak in 7940941de1f was effectively with
us already, but the variable was referred to by a "static" file-scoped
variable. The "bundle.c " leak in 105c6f14ad3 was newly introduced
with the new "filter" feature for bundles.

The "t5600-clone-fail-cleanup.sh" change here to add
"TEST_PASSES_SANITIZE_LEAK=true" is one of the cases where
run-command.c in not carrying the abort() exit code upwards would have
had that test passing before, but now it *actually* passes[1]. We
should fix the lack of 1=1 mapping of SANITIZE=leak testing to actual
leaks some other time, but it's an existing edge case, let's just mark
the really-passing test as passing for now.

1. https://lore.kernel.org/git/220303.86fsnz5o9w.gmgdl@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c                         | 1 +
 t/t1060-object-corruption.sh       | 1 +
 t/t2015-checkout-unborn.sh         | 1 +
 t/t4207-log-decoration-colors.sh   | 1 +
 t/t5301-sliding-window.sh          | 2 ++
 t/t5313-pack-bounds-checks.sh      | 2 ++
 t/t5316-pack-delta-depth.sh        | 2 ++
 t/t5320-delta-islands.sh           | 2 ++
 t/t5322-pack-objects-sparse.sh     | 1 +
 t/t5506-remote-groups.sh           | 1 +
 t/t5513-fetch-track.sh             | 1 +
 t/t5532-fetch-proxy.sh             | 2 ++
 t/t5600-clone-fail-cleanup.sh      | 1 +
 t/t5900-repo-selection.sh          | 2 ++
 t/t6101-rev-parse-parents.sh       | 1 +
 t/t6114-keep-packs.sh              | 2 ++
 t/t7702-repack-cyclic-alternate.sh | 2 ++
 t/t9127-git-svn-partial-rebuild.sh | 1 -
 18 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/revision.c b/revision.c
index c81506fa22f..2484e14d26b 100644
--- a/revision.c
+++ b/revision.c
@@ -2950,6 +2950,7 @@ void release_revisions(struct rev_info *revs)
 	free_commit_list(revs->commits);
 	object_array_clear(&revs->pending);
 	release_revisions_cmdline(&revs->cmdline);
+	list_objects_filter_release(&revs->filter);
 	release_revisions_mailmap(revs->mailmap);
 }
 
diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh
index e8a58b15897..5b8e47e346c 100755
--- a/t/t1060-object-corruption.sh
+++ b/t/t1060-object-corruption.sh
@@ -2,6 +2,7 @@
 
 test_description='see how we handle various forms of corruption'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # convert "1234abcd" to ".git/objects/12/34abcd"
diff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh
index a9721215fab..9425aae6395 100755
--- a/t/t2015-checkout-unborn.sh
+++ b/t/t2015-checkout-unborn.sh
@@ -4,6 +4,7 @@ test_description='checkout from unborn branch'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh
index b8709424981..36ac6aff1e4 100755
--- a/t/t4207-log-decoration-colors.sh
+++ b/t/t4207-log-decoration-colors.sh
@@ -8,6 +8,7 @@ test_description='Test for "git log --decorate" colors'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t5301-sliding-window.sh b/t/t5301-sliding-window.sh
index 76f9798ab95..3ccaaeb3977 100755
--- a/t/t5301-sliding-window.sh
+++ b/t/t5301-sliding-window.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='mmap sliding window tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success \
diff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh
index 535313e4dc8..cc4cfaa9d37 100755
--- a/t/t5313-pack-bounds-checks.sh
+++ b/t/t5313-pack-bounds-checks.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='bounds-checking of access to mmapped on-disk file formats'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 clear_base () {
diff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh
index e9045009a11..eb4ef3dda4d 100755
--- a/t/t5316-pack-delta-depth.sh
+++ b/t/t5316-pack-delta-depth.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='pack-objects breaks long cross-pack delta chains'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # This mirrors a repeated push setup:
diff --git a/t/t5320-delta-islands.sh b/t/t5320-delta-islands.sh
index fea92a5777f..124d47603df 100755
--- a/t/t5320-delta-islands.sh
+++ b/t/t5320-delta-islands.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='exercise delta islands'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # returns true iff $1 is a delta based on $2
diff --git a/t/t5322-pack-objects-sparse.sh b/t/t5322-pack-objects-sparse.sh
index d39958c066d..770695c9278 100755
--- a/t/t5322-pack-objects-sparse.sh
+++ b/t/t5322-pack-objects-sparse.sh
@@ -4,6 +4,7 @@ test_description='pack-objects object selection using sparse algorithm'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup repo' '
diff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh
index 8f150c0793e..5bac03ede81 100755
--- a/t/t5506-remote-groups.sh
+++ b/t/t5506-remote-groups.sh
@@ -4,6 +4,7 @@ test_description='git remote group handling'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 mark() {
diff --git a/t/t5513-fetch-track.sh b/t/t5513-fetch-track.sh
index 65d1e05bd62..c46c4dbaefc 100755
--- a/t/t5513-fetch-track.sh
+++ b/t/t5513-fetch-track.sh
@@ -2,6 +2,7 @@
 
 test_description='fetch follows remote-tracking branches correctly'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh
index 9c2798603b4..d664912799b 100755
--- a/t/t5532-fetch-proxy.sh
+++ b/t/t5532-fetch-proxy.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='fetching via git:// using core.gitproxy'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup remote repo' '
diff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh
index 34b3df40275..c814afa5656 100755
--- a/t/t5600-clone-fail-cleanup.sh
+++ b/t/t5600-clone-fail-cleanup.sh
@@ -13,6 +13,7 @@ Unless the directory already exists, in which case we clean up only what we
 wrote.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 corrupt_repo () {
diff --git a/t/t5900-repo-selection.sh b/t/t5900-repo-selection.sh
index 14e59c5b3e4..a84faac242d 100755
--- a/t/t5900-repo-selection.sh
+++ b/t/t5900-repo-selection.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='selecting remote repo in ambiguous cases'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 reset() {
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
index c571fa51797..a3a41c7a3e4 100755
--- a/t/t6101-rev-parse-parents.sh
+++ b/t/t6101-rev-parse-parents.sh
@@ -8,6 +8,7 @@ test_description='Test git rev-parse with different parent options'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_cmp_rev_output () {
diff --git a/t/t6114-keep-packs.sh b/t/t6114-keep-packs.sh
index 9239d8aa46d..44246f8a63e 100755
--- a/t/t6114-keep-packs.sh
+++ b/t/t6114-keep-packs.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='rev-list with .keep packs'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t7702-repack-cyclic-alternate.sh b/t/t7702-repack-cyclic-alternate.sh
index 93b74867ac8..f3cdb98eec2 100755
--- a/t/t7702-repack-cyclic-alternate.sh
+++ b/t/t7702-repack-cyclic-alternate.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='repack involving cyclic alternate'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh
index 90b1b30dde5..97f495bd49b 100755
--- a/t/t9127-git-svn-partial-rebuild.sh
+++ b/t/t9127-git-svn-partial-rebuild.sh
@@ -5,7 +5,6 @@
 
 test_description='git svn partial-rebuild tests'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize svnrepo' '
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 20/27] revisions API: have release_revisions() release "grep_filter"
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (18 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 19/27] revisions API: have release_revisions() release "filter" Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 21/27] revisions API: have release_revisions() release "prune_data" Ævar Arnfjörð Bjarmason
                           ` (8 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"grep_filter" in the "struct rev_info".This allows us to mark a test
as passing under "TEST_PASSES_SANITIZE_LEAK=true".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c               | 1 +
 t/t9151-svn-mergeinfo.sh | 1 -
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/revision.c b/revision.c
index 2484e14d26b..2c23de60093 100644
--- a/revision.c
+++ b/revision.c
@@ -2952,6 +2952,7 @@ void release_revisions(struct rev_info *revs)
 	release_revisions_cmdline(&revs->cmdline);
 	list_objects_filter_release(&revs->filter);
 	release_revisions_mailmap(revs->mailmap);
+	free_grep_patterns(&revs->grep_filter);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh
index 85221d439bd..c93a5beab25 100755
--- a/t/t9151-svn-mergeinfo.sh
+++ b/t/t9151-svn-mergeinfo.sh
@@ -5,7 +5,6 @@
 
 test_description='git-svn svn mergeinfo properties'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'load svn dump' "
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 21/27] revisions API: have release_revisions() release "prune_data"
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (19 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 20/27] revisions API: have release_revisions() release "grep_filter" Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 22/27] revisions API: clear "boundary_commits" in release_revisions() Ævar Arnfjörð Bjarmason
                           ` (7 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"prune_data" in the "struct rev_info". This means that any code that
calls "release_revisions()" already can get rid of adjacent calls to
clear_pathspec().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 add-interactive.c | 2 --
 builtin/add.c     | 1 -
 builtin/stash.c   | 2 --
 diff-lib.c        | 1 -
 revision.c        | 1 +
 wt-status.c       | 2 --
 6 files changed, 1 insertion(+), 8 deletions(-)

diff --git a/add-interactive.c b/add-interactive.c
index 54cdfc82017..6047e8f6489 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -568,8 +568,6 @@ static int get_modified_files(struct repository *r,
 			run_diff_files(&rev, 0);
 		}
 
-		if (ps)
-			clear_pathspec(&rev.prune_data);
 		release_revisions(&rev);
 	}
 	hashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);
diff --git a/builtin/add.c b/builtin/add.c
index 115a26ea633..fc729e14c17 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -142,7 +142,6 @@ int add_files_to_cache(const char *prefix,
 	rev.diffopt.flags.override_submodule_config = 1;
 	rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
 	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 	return !!data.add_errors;
 }
diff --git a/builtin/stash.c b/builtin/stash.c
index fce4393d123..16aad4b4d35 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1064,7 +1064,6 @@ static int check_changes_tracked_files(const struct pathspec *ps)
 	}
 
 done:
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 	return ret;
 }
@@ -1276,7 +1275,6 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
 
 done:
 	discard_index(&istate);
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 	strbuf_release(&diff_output);
 	remove_path(stash_index_path.buf);
diff --git a/diff-lib.c b/diff-lib.c
index 0f16281253f..298265e5b54 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -641,7 +641,6 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
 
 	if (diff_cache(&revs, tree_oid, NULL, 1))
 		exit(128);
-	clear_pathspec(&revs.prune_data);
 	release_revisions(&revs);
 	return 0;
 }
diff --git a/revision.c b/revision.c
index 2c23de60093..9fb18924cbf 100644
--- a/revision.c
+++ b/revision.c
@@ -2951,6 +2951,7 @@ void release_revisions(struct rev_info *revs)
 	object_array_clear(&revs->pending);
 	release_revisions_cmdline(&revs->cmdline);
 	list_objects_filter_release(&revs->filter);
+	clear_pathspec(&revs->prune_data);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
 }
diff --git a/wt-status.c b/wt-status.c
index a14fad1e03a..61e0c1022f5 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -616,7 +616,6 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
 	rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_files(&rev, 0);
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 }
 
@@ -664,7 +663,6 @@ static void wt_status_collect_changes_index(struct wt_status *s)
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_index(&rev, 1);
 	release_revisions(&rev);
-	clear_pathspec(&rev.prune_data);
 }
 
 static int add_file_to_list(const struct object_id *oid,
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 22/27] revisions API: clear "boundary_commits" in release_revisions()
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (20 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 21/27] revisions API: have release_revisions() release "prune_data" Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 23/27] revisions API: release "reflog_info" in release revisions() Ævar Arnfjörð Bjarmason
                           ` (6 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Clear the "boundary_commits" object_array in release_revisions(). This
makes a few more tests pass under SANITIZE=leak, including
"t/t4126-apply-empty.sh" which started failed as an UNLEAK() in
cmd_format_patch() was removed in a preceding commit.

This also re-marks the various tests relying on "git format-patch" as
passing under "SANITIZE=leak", in the preceding "revisions API users:
use release_revisions() in builtin/log.c" commit those were marked as
failing as we removed the UNLEAK(rev) from cmd_format_patch() in
"builtin/log.c".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c                           | 1 +
 t/t4021-format-patch-numbered.sh     | 1 +
 t/t4028-format-patch-mime-headers.sh | 2 ++
 t/t4036-format-patch-signer-mime.sh  | 1 +
 t/t4122-apply-symlink-inside.sh      | 1 +
 t/t4126-apply-empty.sh               | 1 +
 t/t6110-rev-list-sparse.sh           | 1 +
 t/t9001-send-email.sh                | 1 +
 t/t9116-git-svn-log.sh               | 1 -
 9 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/revision.c b/revision.c
index 9fb18924cbf..40f59c441f7 100644
--- a/revision.c
+++ b/revision.c
@@ -2949,6 +2949,7 @@ void release_revisions(struct rev_info *revs)
 {
 	free_commit_list(revs->commits);
 	object_array_clear(&revs->pending);
+	object_array_clear(&revs->boundary_commits);
 	release_revisions_cmdline(&revs->cmdline);
 	list_objects_filter_release(&revs->filter);
 	clear_pathspec(&revs->prune_data);
diff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh
index 9be65fd4440..1219aa226dc 100755
--- a/t/t4021-format-patch-numbered.sh
+++ b/t/t4021-format-patch-numbered.sh
@@ -5,6 +5,7 @@
 
 test_description='Format-patch numbering options'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4028-format-patch-mime-headers.sh b/t/t4028-format-patch-mime-headers.sh
index 204ba673cb5..60cb819c42e 100755
--- a/t/t4028-format-patch-mime-headers.sh
+++ b/t/t4028-format-patch-mime-headers.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='format-patch mime headers and extra headers do not conflict'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create commit with utf-8 body' '
diff --git a/t/t4036-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh
index 98d9713d8b2..48655bcc789 100755
--- a/t/t4036-format-patch-signer-mime.sh
+++ b/t/t4036-format-patch-signer-mime.sh
@@ -2,6 +2,7 @@
 
 test_description='format-patch -s should force MIME encoding as needed'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh
index aa52de401b9..96965373036 100755
--- a/t/t4122-apply-symlink-inside.sh
+++ b/t/t4122-apply-symlink-inside.sh
@@ -4,6 +4,7 @@ test_description='apply to deeper directory without getting fooled with symlink'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh
index 66a7ba8ab8f..ece9fae207d 100755
--- a/t/t4126-apply-empty.sh
+++ b/t/t4126-apply-empty.sh
@@ -2,6 +2,7 @@
 
 test_description='apply empty'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t6110-rev-list-sparse.sh b/t/t6110-rev-list-sparse.sh
index 13c1da53528..ddefc7f24ee 100755
--- a/t/t6110-rev-list-sparse.sh
+++ b/t/t6110-rev-list-sparse.sh
@@ -4,6 +4,7 @@ test_description='operations that cull histories in unusual ways'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 42694fe5841..01c74b8b075 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -4,6 +4,7 @@ test_description='git send-email'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # May be altered later in the test
diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh
index 34f6c80dea3..d74d7b2de68 100755
--- a/t/t9116-git-svn-log.sh
+++ b/t/t9116-git-svn-log.sh
@@ -5,7 +5,6 @@
 
 test_description='git svn log tests'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup repository and import' '
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 23/27] revisions API: release "reflog_info" in release revisions()
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (21 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 22/27] revisions API: clear "boundary_commits" in release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-03 15:19           ` Phillip Wood
  2022-04-02 10:49         ` [PATCH v5 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release() Ævar Arnfjörð Bjarmason
                           ` (5 subsequent siblings)
  28 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Add a missing reflog_walk_info_release() to "reflog-walk.c" and use it
in release_revisions().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 reflog-walk.c            | 24 +++++++++++++++++++++++-
 reflog-walk.h            |  1 +
 revision.c               |  1 +
 t/t0100-previous.sh      |  1 +
 t/t1401-symbolic-ref.sh  |  2 ++
 t/t1411-reflog-show.sh   |  1 +
 t/t1412-reflog-loop.sh   |  2 ++
 t/t1415-worktree-refs.sh |  1 +
 8 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/reflog-walk.c b/reflog-walk.c
index 8ac4b284b6b..7aa6595a51f 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -8,7 +8,7 @@
 
 struct complete_reflogs {
 	char *ref;
-	const char *short_ref;
+	char *short_ref;
 	struct reflog_info {
 		struct object_id ooid, noid;
 		char *email;
@@ -51,9 +51,16 @@ static void free_complete_reflog(struct complete_reflogs *array)
 	}
 	free(array->items);
 	free(array->ref);
+	free(array->short_ref);
 	free(array);
 }
 
+static void complete_reflogs_clear(void *util, const char *str)
+{
+	struct complete_reflogs *array = util;
+	free_complete_reflog(array);
+}
+
 static struct complete_reflogs *read_complete_reflog(const char *ref)
 {
 	struct complete_reflogs *reflogs =
@@ -116,6 +123,21 @@ void init_reflog_walk(struct reflog_walk_info **info)
 	(*info)->complete_reflogs.strdup_strings = 1;
 }
 
+void reflog_walk_info_release(struct reflog_walk_info *info)
+{
+	size_t i;
+
+	if (!info)
+		return;
+
+	for (i = 0; i < info->nr; i++)
+		free(info->logs[i]);
+	string_list_clear_func(&info->complete_reflogs,
+			       complete_reflogs_clear);
+	free(info->logs);
+	free(info);
+}
+
 int add_reflog_for_walk(struct reflog_walk_info *info,
 		struct commit *commit, const char *name)
 {
diff --git a/reflog-walk.h b/reflog-walk.h
index e9e00ffd479..8076f10d9fb 100644
--- a/reflog-walk.h
+++ b/reflog-walk.h
@@ -8,6 +8,7 @@ struct reflog_walk_info;
 struct date_mode;
 
 void init_reflog_walk(struct reflog_walk_info **info);
+void reflog_walk_info_release(struct reflog_walk_info *info);
 int add_reflog_for_walk(struct reflog_walk_info *info,
 			struct commit *commit, const char *name);
 void show_reflog_message(struct reflog_walk_info *info, int,
diff --git a/revision.c b/revision.c
index 40f59c441f7..e972addd8fc 100644
--- a/revision.c
+++ b/revision.c
@@ -2955,6 +2955,7 @@ void release_revisions(struct rev_info *revs)
 	clear_pathspec(&revs->prune_data);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
+	reflog_walk_info_release(revs->reflog_info);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh
index 69beb59f627..a16cc3d2983 100755
--- a/t/t0100-previous.sh
+++ b/t/t0100-previous.sh
@@ -5,6 +5,7 @@ test_description='previous branch syntax @{-n}'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'branch -d @{-1}' '
diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh
index 132a1b885ac..9fb0b90f252 100755
--- a/t/t1401-symbolic-ref.sh
+++ b/t/t1401-symbolic-ref.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='basic symbolic-ref tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # If the tests munging HEAD fail, they can break detection of
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
index 0bb319b944a..3770ceffafd 100755
--- a/t/t1411-reflog-show.sh
+++ b/t/t1411-reflog-show.sh
@@ -4,6 +4,7 @@ test_description='Test reflog display routines'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t1412-reflog-loop.sh b/t/t1412-reflog-loop.sh
index 977603f7f1c..ff30874f940 100755
--- a/t/t1412-reflog-loop.sh
+++ b/t/t1412-reflog-loop.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='reflog walk shows repeated commits again'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup commits' '
diff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh
index a3e6ea08088..3b531842dd4 100755
--- a/t/t1415-worktree-refs.sh
+++ b/t/t1415-worktree-refs.sh
@@ -2,6 +2,7 @@
 
 test_description='per-worktree refs'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release()
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (22 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 23/27] revisions API: release "reflog_info" in release revisions() Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-03 13:00           ` Phillip Wood
  2022-04-02 10:49         ` [PATCH v5 25/27] revisions API: have release_revisions() release "date_mode" Ævar Arnfjörð Bjarmason
                           ` (4 subsequent siblings)
  28 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Call diff_free() on the "pruning" member of "struct rev_info".  Doing
so makes several tests pass under SANITIZE=leak.

This was also the last missing piece that allows us to remove the
UNLEAK() in "cmd_diff" and "cmd_diff_index", which allows us to use
those commands as a canary for general leaks in the revisions API. See
[1] for further rationale, and 886e1084d78 (builtin/: add UNLEAKs,
2017-10-01) for the commit that added the UNLEAK() there.

1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/diff-index.c                | 1 -
 builtin/diff.c                      | 1 -
 revision.c                          | 1 +
 t/t1001-read-tree-m-2way.sh         | 1 +
 t/t1002-read-tree-m-u-2way.sh       | 1 +
 t/t2200-add-update.sh               | 1 +
 t/t4039-diff-assume-unchanged.sh    | 1 +
 t/t4206-log-follow-harder-copies.sh | 2 ++
 t/t6131-pathspec-icase.sh           | 2 ++
 9 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 3a83183c312..7d158af6b6d 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -70,7 +70,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
 		return -1;
 	}
 	result = run_diff_index(&rev, option);
-	UNLEAK(rev);
 	result = diff_result_code(&rev.diffopt, result);
 	release_revisions(&rev);
 	return result;
diff --git a/builtin/diff.c b/builtin/diff.c
index dd48336da56..f539132ac68 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -594,7 +594,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
 	result = diff_result_code(&rev.diffopt, result);
 	if (1 < rev.diffopt.skip_stat_unmatch)
 		refresh_index_quietly();
-	UNLEAK(rev);
 	release_revisions(&rev);
 	UNLEAK(ent);
 	UNLEAK(blob);
diff --git a/revision.c b/revision.c
index e972addd8fc..8bc777da828 100644
--- a/revision.c
+++ b/revision.c
@@ -2955,6 +2955,7 @@ void release_revisions(struct rev_info *revs)
 	clear_pathspec(&revs->prune_data);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
+	diff_free(&revs->pruning);
 	reflog_walk_info_release(revs->reflog_info);
 }
 
diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
index 0710b1fb1e9..516a6112fdc 100755
--- a/t/t1001-read-tree-m-2way.sh
+++ b/t/t1001-read-tree-m-2way.sh
@@ -21,6 +21,7 @@ In the test, these paths are used:
 	yomin   - not in H or M
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 
diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh
index 46cbd5514a6..bd5313caec9 100755
--- a/t/t1002-read-tree-m-u-2way.sh
+++ b/t/t1002-read-tree-m-u-2way.sh
@@ -9,6 +9,7 @@ This is identical to t1001, but uses -u to update the work tree as well.
 
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index 0c38f8e3569..be394f1131a 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -14,6 +14,7 @@ only the updates to dir/sub.
 Also tested are "git add -u" without limiting, and "git add -u"
 without contents changes, and other conditions'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh
index 0eb0314a8b3..78090e6852d 100755
--- a/t/t4039-diff-assume-unchanged.sh
+++ b/t/t4039-diff-assume-unchanged.sh
@@ -2,6 +2,7 @@
 
 test_description='diff with assume-unchanged entries'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # external diff has been tested in t4020-diff-external.sh
diff --git a/t/t4206-log-follow-harder-copies.sh b/t/t4206-log-follow-harder-copies.sh
index 4871a5dc92f..33ecf82c7f9 100755
--- a/t/t4206-log-follow-harder-copies.sh
+++ b/t/t4206-log-follow-harder-copies.sh
@@ -6,6 +6,8 @@
 test_description='Test --follow should always find copies hard in git log.
 
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh
 
diff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh
index 39fc3f6769b..770cce026cb 100755
--- a/t/t6131-pathspec-icase.sh
+++ b/t/t6131-pathspec-icase.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test case insensitive pathspec limiting'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 if test_have_prereq CASE_INSENSITIVE_FS
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 25/27] revisions API: have release_revisions() release "date_mode"
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (23 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release() Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 26/27] revisions API: have release_revisions() release "topo_walk_info" Ævar Arnfjörð Bjarmason
                           ` (3 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"date_mode" in the "struct ref_info".

This uses the date_mode_release() function added in 974c919d36d (date
API: add and use a date_mode_release(), 2022-02-16). As that commit
notes "t7004-tag.sh" tests for the leaks that are being fixed
here. That test now fails "only" 44 tests, instead of the 46 it failed
before this change.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/revision.c b/revision.c
index 8bc777da828..ef98e4711f3 100644
--- a/revision.c
+++ b/revision.c
@@ -2953,6 +2953,7 @@ void release_revisions(struct rev_info *revs)
 	release_revisions_cmdline(&revs->cmdline);
 	list_objects_filter_release(&revs->filter);
 	clear_pathspec(&revs->prune_data);
+	date_mode_release(&revs->date_mode);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
 	diff_free(&revs->pruning);
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 26/27] revisions API: have release_revisions() release "topo_walk_info"
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (24 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 25/27] revisions API: have release_revisions() release "date_mode" Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-02 10:49         ` [PATCH v5 27/27] revisions API: add a TODO for diff_free(&revs->diffopt) Ævar Arnfjörð Bjarmason
                           ` (2 subsequent siblings)
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Refactor the existing reset_topo_walk() into a thin wrapper for a
release_revisions_topo_walk_info() + resetting the member to "NULL",
and call release_revisions_topo_walk_info() from release_revisions().

This fixes memory leaks that have been with us ever since
"topo_walk_info" was added to revision.[ch] in
f0d9cc4196a (revision.c: begin refactoring --topo-order logic,
2018-11-01).

Due to various other leaks this makes no tests pass in their entirety,
but e.g. before this running this on git.git:

    ./git -P log --pretty=tformat:"%P   %H | %s" --parents --full-history --topo-order -3 -- README.md

Would report under SANITIZE=leak:

    SUMMARY: LeakSanitizer: 531064 byte(s) leaked in 6 allocation(s).

Now we'll free all of that memory.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/revision.c b/revision.c
index ef98e4711f3..28c3c3ff9b4 100644
--- a/revision.c
+++ b/revision.c
@@ -2945,6 +2945,8 @@ static void release_revisions_mailmap(struct string_list *mailmap)
 	free(mailmap);
 }
 
+static void release_revisions_topo_walk_info(struct topo_walk_info *info);
+
 void release_revisions(struct rev_info *revs)
 {
 	free_commit_list(revs->commits);
@@ -2958,6 +2960,7 @@ void release_revisions(struct rev_info *revs)
 	free_grep_patterns(&revs->grep_filter);
 	diff_free(&revs->pruning);
 	reflog_walk_info_release(revs->reflog_info);
+	release_revisions_topo_walk_info(revs->topo_walk_info);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
@@ -3470,17 +3473,22 @@ static void compute_indegrees_to_depth(struct rev_info *revs,
 		indegree_walk_step(revs);
 }
 
-static void reset_topo_walk(struct rev_info *revs)
+static void release_revisions_topo_walk_info(struct topo_walk_info *info)
 {
-	struct topo_walk_info *info = revs->topo_walk_info;
-
+	if (!info)
+		return;
 	clear_prio_queue(&info->explore_queue);
 	clear_prio_queue(&info->indegree_queue);
 	clear_prio_queue(&info->topo_queue);
 	clear_indegree_slab(&info->indegree);
 	clear_author_date_slab(&info->author_date);
+	free(info);
+}
 
-	FREE_AND_NULL(revs->topo_walk_info);
+static void reset_topo_walk(struct rev_info *revs)
+{
+	release_revisions_topo_walk_info(revs->topo_walk_info);
+	revs->topo_walk_info = NULL;
 }
 
 static void init_topo_walk(struct rev_info *revs)
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v5 27/27] revisions API: add a TODO for diff_free(&revs->diffopt)
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (25 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 26/27] revisions API: have release_revisions() release "topo_walk_info" Ævar Arnfjörð Bjarmason
@ 2022-04-02 10:49         ` Ævar Arnfjörð Bjarmason
  2022-04-03 13:07         ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Phillip Wood
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
  28 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-02 10:49 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Add a TODO comment indicating that we should release "diffopt" in
release_revisions(). In a preceding commit we started releasing the
"pruning" member of the same type, but handling "diffopt" will require
us to untangle the "no_free" conditions I added in e900d494dcf (diff:
add an API for deferred freeing, 2021-02-11).

Let's leave a TODO comment to that effect, and so that we don't forget
refactor code that was changed to use release_revisions() in earlier
commits to stop using the "diffopt" member after a call to
release_revisions(). This works currently, but would become a logic
error as soon as we started freeing "diffopt". Doing that change now
doesn't harm anything, and future-proofs us against a later change to
release_revisions().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 diff-lib.c  | 4 +++-
 revision.c  | 1 +
 wt-status.c | 6 ++++--
 3 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/diff-lib.c b/diff-lib.c
index 298265e5b54..7eb66a417aa 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -651,6 +651,7 @@ int index_differs_from(struct repository *r,
 {
 	struct rev_info rev;
 	struct setup_revision_opt opt;
+	unsigned has_changes;
 
 	repo_init_revisions(r, &rev, NULL);
 	memset(&opt, 0, sizeof(opt));
@@ -662,8 +663,9 @@ int index_differs_from(struct repository *r,
 		diff_flags_or(&rev.diffopt.flags, flags);
 	rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
 	run_diff_index(&rev, 1);
+	has_changes = rev.diffopt.flags.has_changes;
 	release_revisions(&rev);
-	return (rev.diffopt.flags.has_changes != 0);
+	return (has_changes != 0);
 }
 
 static struct strbuf *idiff_prefix_cb(struct diff_options *opt, void *data)
diff --git a/revision.c b/revision.c
index 28c3c3ff9b4..812bf709bc3 100644
--- a/revision.c
+++ b/revision.c
@@ -2958,6 +2958,7 @@ void release_revisions(struct rev_info *revs)
 	date_mode_release(&revs->date_mode);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
+	/* TODO (need to handle "no_free"): diff_free(&revs->diffopt) */
 	diff_free(&revs->pruning);
 	reflog_walk_info_release(revs->reflog_info);
 	release_revisions_topo_walk_info(revs->topo_walk_info);
diff --git a/wt-status.c b/wt-status.c
index 61e0c1022f5..102d904adcb 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -2545,8 +2545,9 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)
 	rev_info.diffopt.flags.quick = 1;
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_files(&rev_info, 0);
+	result = diff_result_code(&rev_info.diffopt, result);
 	release_revisions(&rev_info);
-	return diff_result_code(&rev_info.diffopt, result);
+	return result;
 }
 
 /**
@@ -2578,8 +2579,9 @@ int has_uncommitted_changes(struct repository *r,
 
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_index(&rev_info, 1);
+	result = diff_result_code(&rev_info.diffopt, result);
 	release_revisions(&rev_info);
-	return diff_result_code(&rev_info.diffopt, result);
+	return result;
 }
 
 /**
-- 
2.35.1.1585.gd85f8dcb745


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* Re: [PATCH v5 14/27] revisions API users: use release_revisions() with UNLEAK()
  2022-04-02 10:49         ` [PATCH v5 14/27] revisions API users: use release_revisions() with UNLEAK() Ævar Arnfjörð Bjarmason
@ 2022-04-03  9:27           ` Phillip Wood
  2022-04-03 13:55             ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 252+ messages in thread
From: Phillip Wood @ 2022-04-03  9:27 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Hi Ævar

[continuing with v5 from where I left off with v4]

On 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:
> Use a release_revisions() with those "struct rev_list" users which
> already "UNLEAK" the struct. It may seem odd to simultaneously attempt
> to free() memory, but also to explicitly ignore whether we have memory
> leaks in the same.
> 
> As explained in preceding commits this is being done to use the
> built-in commands as a guinea pig for whether the release_revisions()
> function works as expected, we'd like to test e.g. whether we segfault
> as we change it. In subsequent commits we'll then remove these
> UNLEAK() as the function is made to free the memory that caused us to
> add them in the first place.

I'm a bit confused by this, the previous commit argued in favor of 
removing UNLEAK() so would could see the leaks and fix them, this is 
saying we should hide the leaks.

Best Wishes

Phillip

> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>   builtin/diff-index.c | 4 +++-
>   builtin/diff.c       | 1 +
>   2 files changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/builtin/diff-index.c b/builtin/diff-index.c
> index 5fd23ab5b6c..3a83183c312 100644
> --- a/builtin/diff-index.c
> +++ b/builtin/diff-index.c
> @@ -71,5 +71,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
>   	}
>   	result = run_diff_index(&rev, option);
>   	UNLEAK(rev);
> -	return diff_result_code(&rev.diffopt, result);
> +	result = diff_result_code(&rev.diffopt, result);
> +	release_revisions(&rev);
> +	return result;
>   }
> diff --git a/builtin/diff.c b/builtin/diff.c
> index bb7fafca618..dd48336da56 100644
> --- a/builtin/diff.c
> +++ b/builtin/diff.c
> @@ -595,6 +595,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
>   	if (1 < rev.diffopt.skip_stat_unmatch)
>   		refresh_index_quietly();
>   	UNLEAK(rev);
> +	release_revisions(&rev);
>   	UNLEAK(ent);
>   	UNLEAK(blob);
>   	return result;

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v5 17/27] revisions API: have release_revisions() release "mailmap"
  2022-04-02 10:49         ` [PATCH v5 17/27] revisions API: have release_revisions() release "mailmap" Ævar Arnfjörð Bjarmason
@ 2022-04-03  9:33           ` Phillip Wood
  2022-04-03 13:57             ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 252+ messages in thread
From: Phillip Wood @ 2022-04-03  9:33 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Hi Ævar

On 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:
> Extend the the release_revisions() function so that it frees the
> "mailmap" in the "struct rev_info".
> 
> The log family of functions now calls the clear_mailmap() function
> added in fa8afd18e5a (revisions API: provide and use a
> release_revisions(), 2021-09-19), allowing us to whitelist some tests
> with "TEST_PASSES_SANITIZE_LEAK=true".
> 
> Unfortunately having a pointer to a mailmap in "struct rev_info"
> instead of an embedded member that we "own" get a bit messy, as can be
> seen in the change to builtin/commit.c.
> 
> When we free() this data we won't be able to tell apart a pointer to a
> "mailmap" on the heap from one on the stack. As seen in
> ea57bc0d41b (log: add --use-mailmap option, 2013-01-05) the "log"
> family allocates it on the heap, but in the find_author_by_nickname()
> code added in ea16794e430 (commit: search author pattern against
> mailmap, 2013-08-23) we allocated it on the stack instead.
> 
> Ideally we'd simply change that member to a "struct string_list
> mailmap" and never free() the "mailmap" itself, but that would be a
> much larger change to the revisions API.

I agree it makes sense to leave that for now

> We have code that needs to hand an existing "mailmap" to a "struct
> rev_info", while we could change all of that, let's not go there
> now.
> 
> The complexity isn't in the ownership of the "mailmap" per-se, but
> that various things assume a "rev_info.mailmap == NULL" means "doesn't
> want mailmap", if we changed that to an init'd "struct string_list
> we'd need to carefully refactor things to change those assumptions.
> 
> Let's instead always free() it, and simply declare that if you add
> such a "mailmap" it must be allocated on the heap. Any modern libc
> will correctly panic if we free() a stack variable, so this should be
> safe going forward.
> 
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>   builtin/commit.c                   | 5 ++---
>   revision.c                         | 9 +++++++++
>   t/t0056-git-C.sh                   | 1 +
>   t/t3302-notes-index-expensive.sh   | 1 +
>   t/t4055-diff-context.sh            | 1 +
>   t/t4066-diff-emit-delay.sh         | 1 +
>   t/t7008-filter-branch-null-sha1.sh | 1 +
>   7 files changed, 16 insertions(+), 3 deletions(-)
> 
> diff --git a/builtin/commit.c b/builtin/commit.c
> index c7eda9bbb72..cd6cebcf8c8 100644
> --- a/builtin/commit.c
> +++ b/builtin/commit.c
> @@ -1100,7 +1100,6 @@ static const char *find_author_by_nickname(const char *name)
>   	struct rev_info revs;
>   	struct commit *commit;
>   	struct strbuf buf = STRBUF_INIT;
> -	struct string_list mailmap = STRING_LIST_INIT_NODUP;
>   	const char *av[20];
>   	int ac = 0;
>   
> @@ -1111,7 +1110,8 @@ static const char *find_author_by_nickname(const char *name)
>   	av[++ac] = buf.buf;
>   	av[++ac] = NULL;
>   	setup_revisions(ac, av, &revs, NULL);
> -	revs.mailmap = &mailmap;
> +	revs.mailmap = xmalloc(sizeof(struct string_list));
> +	string_list_init_nodup(revs.mailmap);

This is a common pattern in one of the previous patches, is it worth 
adding helpers to allocate and initialize a struct string_list? Maybe 
string_list_new_nodup() and string_list_new_dup().

>   	read_mailmap(revs.mailmap);
>   
>   	if (prepare_revision_walk(&revs))
> @@ -1122,7 +1122,6 @@ static const char *find_author_by_nickname(const char *name)
>   		ctx.date_mode.type = DATE_NORMAL;
>   		strbuf_release(&buf);
>   		format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
> -		clear_mailmap(&mailmap);
>   		release_revisions(&revs);
>   		return strbuf_detach(&buf, NULL);
>   	}
> diff --git a/revision.c b/revision.c
> index 553f7de8250..622f0faecc4 100644
> --- a/revision.c
> +++ b/revision.c
> @@ -2926,10 +2926,19 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
>   	return left;
>   }
>   
> +static void release_revisions_mailmap(struct string_list *mailmap)
> +{
> +	if (!mailmap)
> +		return;
> +	clear_mailmap(mailmap);
> +	free(mailmap);
> +}

It's not a big issue but if there are no other users of this then it 
could just go inside release_revisions, my impression is that this 
series builds a collection of very small functions whose only caller is 
release_revisions()

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v5 18/27] revisions API: have release_revisions() release "cmdline"
  2022-04-02 10:49         ` [PATCH v5 18/27] revisions API: have release_revisions() release "cmdline" Ævar Arnfjörð Bjarmason
@ 2022-04-03 12:54           ` Phillip Wood
  2022-04-03 14:01             ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 252+ messages in thread
From: Phillip Wood @ 2022-04-03 12:54 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Hi Ævar

On 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:
> Extend the the release_revisions() function so that it frees the
> "cmdline" in the "struct rev_info". This in combination with a
> preceding change to free "commits" and "mailmap" means that we can
> whitelist another test under "TEST_PASSES_SANITIZE_LEAK=true".
> 
> There was a proposal in [1] to do away with xstrdup()-ing this
> add_rev_cmdline(), perhaps that would be worthwhile, but for now let's
> just free() it.
> 
> We could also make that a "char *" in "struct rev_cmdline_entry"
> itself, but since we own it let's expose it as a constant to outside
> callers. I proposed that in [2] but have since changed my mind. See
> 14d30cdfc04 (ref-filter: fix memory leak in `free_array_item()`,
> 2019-07-10), c514c62a4fd (checkout: fix leak of non-existent branch
> names, 2020-08-14) and other log history hits for "free((char *)" for
> prior art.
> 
> This includes the tests we had false-positive passes on before my
> 6798b08e848 (perl Git.pm: don't ignore signalled failure in
> _cmd_close(), 2022-02-01), now they pass for real.
> 
> Since there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to
> list those that don't pass than to touch most of those 66. So let's
> introduce a "TEST_FAILS_SANITIZE_LEAK=true", which if set in the tests
> won't cause lib-git-svn.sh to set "TEST_PASSES_SANITIZE_LEAK=true.
> 
> This change also marks all the tests that we removed
> "TEST_FAILS_SANITIZE_LEAK=true" from in an earlier commit due to
> removing the UNLEAK() from cmd_format_patch(), we can now assert that
> its API use doesn't leak any "struct rev_info" memory.
> 
> This change also made commit "t5503-tagfollow.sh" pass on current
> master, but that would regress when combined with
> ps/fetch-atomic-fixup's de004e848a9 (t5503: simplify setup of test
> which exercises failure of backfill, 2022-03-03) (through no fault of
> that topic, that change started using "git clone" in the test, which
> has an outstanding leak). Let's leave that test out for now to avoid
> in-flight semantic conflicts.
> 
> 1. https://lore.kernel.org/git/YUj%2FgFRh6pwrZalY@carlos-mbp.lan/
> 2. https://lore.kernel.org/git/87o88obkb1.fsf@evledraar.gmail.com/
> 
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>   revision.c                                 | 12 ++++++++++++
>   t/lib-git-svn.sh                           |  4 ++++
>   t/t0062-revision-walking.sh                |  1 +
>   t/t0101-at-syntax.sh                       |  2 ++
>   t/t1060-object-corruption.sh               |  1 +
>   t/t3303-notes-subtrees.sh                  |  1 +
>   t/t3305-notes-fanout.sh                    |  1 +
>   t/t3408-rebase-multi-line.sh               |  1 +
>   t/t4027-diff-submodule.sh                  |  1 +
>   t/t4128-apply-root.sh                      |  1 +
>   t/t4212-log-corrupt.sh                     |  1 +
>   t/t5515-fetch-merge-logic.sh               |  1 +
>   t/t5518-fetch-exit-status.sh               |  1 +
>   t/t6002-rev-list-bisect.sh                 |  1 +
>   t/t6003-rev-list-topo-order.sh             |  1 +
>   t/t6005-rev-list-count.sh                  |  1 +
>   t/t6018-rev-list-glob.sh                   |  1 +
>   t/t6100-rev-list-in-order.sh               |  1 +
>   t/t9100-git-svn-basic.sh                   |  1 +
>   t/t9101-git-svn-props.sh                   |  2 ++
>   t/t9104-git-svn-follow-parent.sh           |  2 ++
>   t/t9106-git-svn-commit-diff-clobber.sh     |  2 ++
>   t/t9115-git-svn-dcommit-funky-renames.sh   |  1 +
>   t/t9116-git-svn-log.sh                     |  2 ++
>   t/t9122-git-svn-author.sh                  |  2 ++
>   t/t9127-git-svn-partial-rebuild.sh         |  2 ++
>   t/t9129-git-svn-i18n-commitencoding.sh     |  1 +
>   t/t9132-git-svn-broken-symlink.sh          |  1 +
>   t/t9139-git-svn-non-utf8-commitencoding.sh |  1 +
>   t/t9146-git-svn-empty-dirs.sh              |  2 ++
>   t/t9148-git-svn-propset.sh                 |  1 +
>   t/t9151-svn-mergeinfo.sh                   |  1 +
>   t/t9160-git-svn-preserve-empty-dirs.sh     |  1 +
>   t/t9162-git-svn-dcommit-interactive.sh     |  2 ++
>   t/t9164-git-svn-dcommit-concurrent.sh      |  2 ++
>   t/t9501-gitweb-standalone-http-status.sh   |  1 +
>   36 files changed, 60 insertions(+)
> 
> diff --git a/revision.c b/revision.c
> index 622f0faecc4..c81506fa22f 100644
> --- a/revision.c
> +++ b/revision.c
> @@ -2926,6 +2926,17 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
>   	return left;
>   }
>   
> +static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)
> +{
> +	unsigned int i;
> +
> +	if (!cmdline)
> +		return;

I don't think we need this guard, the only instances of struct 
rev_cmdline_info exist within struct rev_info, as far as I can see it is 
never created on its own.

> +	for (i = 0; i < cmdline->nr; i++)
> +		free((char *)cmdline->rev[i].name);
> +	free(cmdline->rev);

This could just be in release_revisions()

Best Wishes

Phillip
> +}
> +
>   static void release_revisions_mailmap(struct string_list *mailmap)
>   {
>   	if (!mailmap)
> @@ -2938,6 +2949,7 @@ void release_revisions(struct rev_info *revs)
>   {
>   	free_commit_list(revs->commits);
>   	object_array_clear(&revs->pending);
> +	release_revisions_cmdline(&revs->cmdline);
>   	release_revisions_mailmap(revs->mailmap);
>   }
>   
> diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
> index 2fde2353fd3..ea28971e8ee 100644
> --- a/t/lib-git-svn.sh
> +++ b/t/lib-git-svn.sh
> @@ -1,3 +1,7 @@
> +if test -z "$TEST_FAILS_SANITIZE_LEAK"
> +then
> +	TEST_PASSES_SANITIZE_LEAK=true
> +fi
>   . ./test-lib.sh
>   
>   if test -n "$NO_SVN_TESTS"
> diff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh
> index 8e215867b8c..b9480c81781 100755
> --- a/t/t0062-revision-walking.sh
> +++ b/t/t0062-revision-walking.sh
> @@ -5,6 +5,7 @@
>   
>   test_description='Test revision walking api'
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   
>   cat >run_twice_expected <<-EOF
> diff --git a/t/t0101-at-syntax.sh b/t/t0101-at-syntax.sh
> index a1998b558f9..878aadd64c9 100755
> --- a/t/t0101-at-syntax.sh
> +++ b/t/t0101-at-syntax.sh
> @@ -1,6 +1,8 @@
>   #!/bin/sh
>   
>   test_description='various @{whatever} syntax tests'
> +
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   
>   test_expect_success 'setup' '
> diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh
> index bc89371f534..e8a58b15897 100755
> --- a/t/t1060-object-corruption.sh
> +++ b/t/t1060-object-corruption.sh
> @@ -1,6 +1,7 @@
>   #!/bin/sh
>   
>   test_description='see how we handle various forms of corruption'
> +
>   . ./test-lib.sh
>   
>   # convert "1234abcd" to ".git/objects/12/34abcd"
> diff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh
> index eac193757bf..bc9b791d1b9 100755
> --- a/t/t3303-notes-subtrees.sh
> +++ b/t/t3303-notes-subtrees.sh
> @@ -5,6 +5,7 @@ test_description='Test commit notes organized in subtrees'
>   GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
>   export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   
>   number_of_commits=100
> diff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh
> index 9976d787f47..64a9915761a 100755
> --- a/t/t3305-notes-fanout.sh
> +++ b/t/t3305-notes-fanout.sh
> @@ -2,6 +2,7 @@
>   
>   test_description='Test that adding/removing many notes triggers automatic fanout restructuring'
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   
>   path_has_fanout() {
> diff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh
> index cde3562e3a6..7b4607d72f2 100755
> --- a/t/t3408-rebase-multi-line.sh
> +++ b/t/t3408-rebase-multi-line.sh
> @@ -5,6 +5,7 @@ test_description='rebasing a commit with multi-line first paragraph.'
>   GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
>   export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   
>   test_expect_success setup '
> diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
> index 295da987cce..40164ae07d2 100755
> --- a/t/t4027-diff-submodule.sh
> +++ b/t/t4027-diff-submodule.sh
> @@ -2,6 +2,7 @@
>   
>   test_description='difference in submodules'
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   . "$TEST_DIRECTORY"/lib-diff.sh
>   
> diff --git a/t/t4128-apply-root.sh b/t/t4128-apply-root.sh
> index f6db5a79dd9..ed94c90204e 100755
> --- a/t/t4128-apply-root.sh
> +++ b/t/t4128-apply-root.sh
> @@ -2,6 +2,7 @@
>   
>   test_description='apply same filename'
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   
>   test_expect_success 'setup' '
> diff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh
> index 0244888a5a7..30a219894bb 100755
> --- a/t/t4212-log-corrupt.sh
> +++ b/t/t4212-log-corrupt.sh
> @@ -2,6 +2,7 @@
>   
>   test_description='git log with invalid commit headers'
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   
>   test_expect_success 'setup' '
> diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh
> index 320d26796d2..c100a809c5e 100755
> --- a/t/t5515-fetch-merge-logic.sh
> +++ b/t/t5515-fetch-merge-logic.sh
> @@ -14,6 +14,7 @@ export GIT_TEST_PROTOCOL_VERSION
>   GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
>   export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   
>   build_script () {
> diff --git a/t/t5518-fetch-exit-status.sh b/t/t5518-fetch-exit-status.sh
> index 5c4ac2556e7..c13120088fa 100755
> --- a/t/t5518-fetch-exit-status.sh
> +++ b/t/t5518-fetch-exit-status.sh
> @@ -8,6 +8,7 @@ test_description='fetch exit status test'
>   GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
>   export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   
>   test_expect_success setup '
> diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
> index b95a0212adf..162cf50778d 100755
> --- a/t/t6002-rev-list-bisect.sh
> +++ b/t/t6002-rev-list-bisect.sh
> @@ -4,6 +4,7 @@
>   #
>   test_description='Tests git rev-list --bisect functionality'
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   . "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
>   
> diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh
> index 24d1836f417..1f7d7dd20c1 100755
> --- a/t/t6003-rev-list-topo-order.sh
> +++ b/t/t6003-rev-list-topo-order.sh
> @@ -5,6 +5,7 @@
>   
>   test_description='Tests git rev-list --topo-order functionality'
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   . "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
>   
> diff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh
> index e960049f647..0729f800c3c 100755
> --- a/t/t6005-rev-list-count.sh
> +++ b/t/t6005-rev-list-count.sh
> @@ -2,6 +2,7 @@
>   
>   test_description='git rev-list --max-count and --skip test'
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   
>   test_expect_success 'setup' '
> diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
> index 24b34add833..e1abc5c2b32 100755
> --- a/t/t6018-rev-list-glob.sh
> +++ b/t/t6018-rev-list-glob.sh
> @@ -5,6 +5,7 @@ test_description='rev-list/rev-parse --glob'
>   GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
>   export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   
>   commit () {
> diff --git a/t/t6100-rev-list-in-order.sh b/t/t6100-rev-list-in-order.sh
> index e934bc239c5..88ed7bd75a7 100755
> --- a/t/t6100-rev-list-in-order.sh
> +++ b/t/t6100-rev-list-in-order.sh
> @@ -2,6 +2,7 @@
>   
>   test_description='rev-list testing in-commit-order'
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   
>   test_expect_success 'setup a commit history with trees, blobs' '
> diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
> index fea41b3c360..7c5b847f584 100755
> --- a/t/t9100-git-svn-basic.sh
> +++ b/t/t9100-git-svn-basic.sh
> @@ -8,6 +8,7 @@ test_description='git svn basic tests'
>   GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
>   export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
>   
> +TEST_FAILS_SANITIZE_LEAK=true
>   . ./lib-git-svn.sh
>   
>   prepare_utf8_locale
> diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
> index 8b5681dd68a..d043e80fc34 100755
> --- a/t/t9101-git-svn-props.sh
> +++ b/t/t9101-git-svn-props.sh
> @@ -4,6 +4,8 @@
>   #
>   
>   test_description='git svn property tests'
> +
> +TEST_FAILS_SANITIZE_LEAK=true
>   . ./lib-git-svn.sh
>   
>   mkdir import
> diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
> index c7d8e0bf00f..5cf2ef4b8b0 100755
> --- a/t/t9104-git-svn-follow-parent.sh
> +++ b/t/t9104-git-svn-follow-parent.sh
> @@ -4,6 +4,8 @@
>   #
>   
>   test_description='git svn fetching'
> +
> +TEST_FAILS_SANITIZE_LEAK=true
>   . ./lib-git-svn.sh
>   
>   test_expect_success 'initialize repo' '
> diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
> index aec45bca3b7..3cab0b9720a 100755
> --- a/t/t9106-git-svn-commit-diff-clobber.sh
> +++ b/t/t9106-git-svn-commit-diff-clobber.sh
> @@ -2,6 +2,8 @@
>   #
>   # Copyright (c) 2006 Eric Wong
>   test_description='git svn commit-diff clobber'
> +
> +TEST_FAILS_SANITIZE_LEAK=true
>   . ./lib-git-svn.sh
>   
>   test_expect_success 'initialize repo' '
> diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh
> index 743fbe1fe46..419f055721d 100755
> --- a/t/t9115-git-svn-dcommit-funky-renames.sh
> +++ b/t/t9115-git-svn-dcommit-funky-renames.sh
> @@ -5,6 +5,7 @@
>   
>   test_description='git svn dcommit can commit renames of files with ugly names'
>   
> +TEST_FAILS_SANITIZE_LEAK=true
>   . ./lib-git-svn.sh
>   
>   test_expect_success 'load repository with strange names' '
> diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh
> index 0a9f1ef366d..34f6c80dea3 100755
> --- a/t/t9116-git-svn-log.sh
> +++ b/t/t9116-git-svn-log.sh
> @@ -4,6 +4,8 @@
>   #
>   
>   test_description='git svn log tests'
> +
> +TEST_FAILS_SANITIZE_LEAK=true
>   . ./lib-git-svn.sh
>   
>   test_expect_success 'setup repository and import' '
> diff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh
> index 9e8fe38e7ef..527ba3d2932 100755
> --- a/t/t9122-git-svn-author.sh
> +++ b/t/t9122-git-svn-author.sh
> @@ -1,6 +1,8 @@
>   #!/bin/sh
>   
>   test_description='git svn authorship'
> +
> +TEST_FAILS_SANITIZE_LEAK=true
>   . ./lib-git-svn.sh
>   
>   test_expect_success 'setup svn repository' '
> diff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh
> index 2e4789d061f..90b1b30dde5 100755
> --- a/t/t9127-git-svn-partial-rebuild.sh
> +++ b/t/t9127-git-svn-partial-rebuild.sh
> @@ -4,6 +4,8 @@
>   #
>   
>   test_description='git svn partial-rebuild tests'
> +
> +TEST_FAILS_SANITIZE_LEAK=true
>   . ./lib-git-svn.sh
>   
>   test_expect_success 'initialize svnrepo' '
> diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh
> index 01e1e8a8f76..185248a4cd7 100755
> --- a/t/t9129-git-svn-i18n-commitencoding.sh
> +++ b/t/t9129-git-svn-i18n-commitencoding.sh
> @@ -4,6 +4,7 @@
>   
>   test_description='git svn honors i18n.commitEncoding in config'
>   
> +TEST_FAILS_SANITIZE_LEAK=true
>   . ./lib-git-svn.sh
>   
>   compare_git_head_with () {
> diff --git a/t/t9132-git-svn-broken-symlink.sh b/t/t9132-git-svn-broken-symlink.sh
> index aeceffaf7b0..4d8d0584b79 100755
> --- a/t/t9132-git-svn-broken-symlink.sh
> +++ b/t/t9132-git-svn-broken-symlink.sh
> @@ -2,6 +2,7 @@
>   
>   test_description='test that git handles an svn repository with empty symlinks'
>   
> +TEST_FAILS_SANITIZE_LEAK=true
>   . ./lib-git-svn.sh
>   test_expect_success 'load svn dumpfile' '
>   	svnadmin load "$rawsvnrepo" <<EOF
> diff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh
> index 22d80b0be2b..b7f756b2b7f 100755
> --- a/t/t9139-git-svn-non-utf8-commitencoding.sh
> +++ b/t/t9139-git-svn-non-utf8-commitencoding.sh
> @@ -4,6 +4,7 @@
>   
>   test_description='git svn refuses to dcommit non-UTF8 messages'
>   
> +TEST_FAILS_SANITIZE_LEAK=true
>   . ./lib-git-svn.sh
>   
>   # ISO-2022-JP can pass for valid UTF-8, so skipping that in this test
> diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh
> index 80cb55fee70..79c26ed69c1 100755
> --- a/t/t9146-git-svn-empty-dirs.sh
> +++ b/t/t9146-git-svn-empty-dirs.sh
> @@ -3,6 +3,8 @@
>   # Copyright (c) 2009 Eric Wong
>   
>   test_description='git svn creates empty directories'
> +
> +TEST_FAILS_SANITIZE_LEAK=true
>   . ./lib-git-svn.sh
>   
>   test_expect_success 'initialize repo' '
> diff --git a/t/t9148-git-svn-propset.sh b/t/t9148-git-svn-propset.sh
> index aebb28995e5..6cc76a07b39 100755
> --- a/t/t9148-git-svn-propset.sh
> +++ b/t/t9148-git-svn-propset.sh
> @@ -5,6 +5,7 @@
>   
>   test_description='git svn propset tests'
>   
> +TEST_FAILS_SANITIZE_LEAK=true
>   . ./lib-git-svn.sh
>   
>   test_expect_success 'setup propset via import' '
> diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh
> index c93a5beab25..85221d439bd 100755
> --- a/t/t9151-svn-mergeinfo.sh
> +++ b/t/t9151-svn-mergeinfo.sh
> @@ -5,6 +5,7 @@
>   
>   test_description='git-svn svn mergeinfo properties'
>   
> +TEST_FAILS_SANITIZE_LEAK=true
>   . ./lib-git-svn.sh
>   
>   test_expect_success 'load svn dump' "
> diff --git a/t/t9160-git-svn-preserve-empty-dirs.sh b/t/t9160-git-svn-preserve-empty-dirs.sh
> index 36c6b1a12ff..9cf7a1427ab 100755
> --- a/t/t9160-git-svn-preserve-empty-dirs.sh
> +++ b/t/t9160-git-svn-preserve-empty-dirs.sh
> @@ -9,6 +9,7 @@ This test uses git to clone a Subversion repository that contains empty
>   directories, and checks that corresponding directories are created in the
>   local Git repository with placeholder files.'
>   
> +TEST_FAILS_SANITIZE_LEAK=true
>   . ./lib-git-svn.sh
>   
>   GIT_REPO=git-svn-repo
> diff --git a/t/t9162-git-svn-dcommit-interactive.sh b/t/t9162-git-svn-dcommit-interactive.sh
> index e38d9fa37b5..e2aa8ed88a9 100755
> --- a/t/t9162-git-svn-dcommit-interactive.sh
> +++ b/t/t9162-git-svn-dcommit-interactive.sh
> @@ -3,6 +3,8 @@
>   # Copyright (c) 2011 Frédéric Heitzmann
>   
>   test_description='git svn dcommit --interactive series'
> +
> +TEST_FAILS_SANITIZE_LEAK=true
>   . ./lib-git-svn.sh
>   
>   test_expect_success 'initialize repo' '
> diff --git a/t/t9164-git-svn-dcommit-concurrent.sh b/t/t9164-git-svn-dcommit-concurrent.sh
> index 8466269bf50..1465156072e 100755
> --- a/t/t9164-git-svn-dcommit-concurrent.sh
> +++ b/t/t9164-git-svn-dcommit-concurrent.sh
> @@ -4,6 +4,8 @@
>   #
>   
>   test_description='concurrent git svn dcommit'
> +
> +TEST_FAILS_SANITIZE_LEAK=true
>   . ./lib-git-svn.sh
>   
>   
> diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh
> index 32814e75df5..c900231079c 100755
> --- a/t/t9501-gitweb-standalone-http-status.sh
> +++ b/t/t9501-gitweb-standalone-http-status.sh
> @@ -13,6 +13,7 @@ code and message.'
>   GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
>   export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./lib-gitweb.sh
>   
>   #

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v5 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release()
  2022-04-02 10:49         ` [PATCH v5 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release() Ævar Arnfjörð Bjarmason
@ 2022-04-03 13:00           ` Phillip Wood
  2022-04-03 14:04             ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 252+ messages in thread
From: Phillip Wood @ 2022-04-03 13:00 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Hi Ævar

On 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:
> Call diff_free() on the "pruning" member of "struct rev_info".  Doing
> so makes several tests pass under SANITIZE=leak.
> 
> This was also the last missing piece that allows us to remove the
> UNLEAK() in "cmd_diff" and "cmd_diff_index", which allows us to use
> those commands as a canary for general leaks in the revisions API. See
> [1] for further rationale, and 886e1084d78 (builtin/: add UNLEAKs,
> 2017-10-01) for the commit that added the UNLEAK() there.

Oh is the the answer to my confusion about patch 14? I wonder if the 
change could come earlier so we can remove the UNLEAK()s all at once?

Best Wishes

Phillip

> 1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/
> 
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>   builtin/diff-index.c                | 1 -
>   builtin/diff.c                      | 1 -
>   revision.c                          | 1 +
>   t/t1001-read-tree-m-2way.sh         | 1 +
>   t/t1002-read-tree-m-u-2way.sh       | 1 +
>   t/t2200-add-update.sh               | 1 +
>   t/t4039-diff-assume-unchanged.sh    | 1 +
>   t/t4206-log-follow-harder-copies.sh | 2 ++
>   t/t6131-pathspec-icase.sh           | 2 ++
>   9 files changed, 9 insertions(+), 2 deletions(-)
> 
> diff --git a/builtin/diff-index.c b/builtin/diff-index.c
> index 3a83183c312..7d158af6b6d 100644
> --- a/builtin/diff-index.c
> +++ b/builtin/diff-index.c
> @@ -70,7 +70,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
>   		return -1;
>   	}
>   	result = run_diff_index(&rev, option);
> -	UNLEAK(rev);
>   	result = diff_result_code(&rev.diffopt, result);
>   	release_revisions(&rev);
>   	return result;
> diff --git a/builtin/diff.c b/builtin/diff.c
> index dd48336da56..f539132ac68 100644
> --- a/builtin/diff.c
> +++ b/builtin/diff.c
> @@ -594,7 +594,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
>   	result = diff_result_code(&rev.diffopt, result);
>   	if (1 < rev.diffopt.skip_stat_unmatch)
>   		refresh_index_quietly();
> -	UNLEAK(rev);
>   	release_revisions(&rev);
>   	UNLEAK(ent);
>   	UNLEAK(blob);
> diff --git a/revision.c b/revision.c
> index e972addd8fc..8bc777da828 100644
> --- a/revision.c
> +++ b/revision.c
> @@ -2955,6 +2955,7 @@ void release_revisions(struct rev_info *revs)
>   	clear_pathspec(&revs->prune_data);
>   	release_revisions_mailmap(revs->mailmap);
>   	free_grep_patterns(&revs->grep_filter);
> +	diff_free(&revs->pruning);
>   	reflog_walk_info_release(revs->reflog_info);
>   }
>   
> diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
> index 0710b1fb1e9..516a6112fdc 100755
> --- a/t/t1001-read-tree-m-2way.sh
> +++ b/t/t1001-read-tree-m-2way.sh
> @@ -21,6 +21,7 @@ In the test, these paths are used:
>   	yomin   - not in H or M
>   '
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   . "$TEST_DIRECTORY"/lib-read-tree.sh
>   
> diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh
> index 46cbd5514a6..bd5313caec9 100755
> --- a/t/t1002-read-tree-m-u-2way.sh
> +++ b/t/t1002-read-tree-m-u-2way.sh
> @@ -9,6 +9,7 @@ This is identical to t1001, but uses -u to update the work tree as well.
>   
>   '
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   . "$TEST_DIRECTORY"/lib-read-tree.sh
>   
> diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
> index 0c38f8e3569..be394f1131a 100755
> --- a/t/t2200-add-update.sh
> +++ b/t/t2200-add-update.sh
> @@ -14,6 +14,7 @@ only the updates to dir/sub.
>   Also tested are "git add -u" without limiting, and "git add -u"
>   without contents changes, and other conditions'
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   
>   test_expect_success setup '
> diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh
> index 0eb0314a8b3..78090e6852d 100755
> --- a/t/t4039-diff-assume-unchanged.sh
> +++ b/t/t4039-diff-assume-unchanged.sh
> @@ -2,6 +2,7 @@
>   
>   test_description='diff with assume-unchanged entries'
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   
>   # external diff has been tested in t4020-diff-external.sh
> diff --git a/t/t4206-log-follow-harder-copies.sh b/t/t4206-log-follow-harder-copies.sh
> index 4871a5dc92f..33ecf82c7f9 100755
> --- a/t/t4206-log-follow-harder-copies.sh
> +++ b/t/t4206-log-follow-harder-copies.sh
> @@ -6,6 +6,8 @@
>   test_description='Test --follow should always find copies hard in git log.
>   
>   '
> +
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   . "$TEST_DIRECTORY"/lib-diff.sh
>   
> diff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh
> index 39fc3f6769b..770cce026cb 100755
> --- a/t/t6131-pathspec-icase.sh
> +++ b/t/t6131-pathspec-icase.sh
> @@ -1,6 +1,8 @@
>   #!/bin/sh
>   
>   test_description='test case insensitive pathspec limiting'
> +
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   
>   if test_have_prereq CASE_INSENSITIVE_FS

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v5 00/27] revision.[ch]: add and use release_revisions()
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (26 preceding siblings ...)
  2022-04-02 10:49         ` [PATCH v5 27/27] revisions API: add a TODO for diff_free(&revs->diffopt) Ævar Arnfjörð Bjarmason
@ 2022-04-03 13:07         ` Phillip Wood
  2022-04-03 14:18           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
  28 siblings, 1 reply; 252+ messages in thread
From: Phillip Wood @ 2022-04-03 13:07 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Hi Ævar

I've left a few comments on this series for the patches that I had not 
looked at in v4. There's nothing major, the changes mostly make sense to 
me (though I'm not familiar with the revisions api)

On 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:
> This series reduces the memory leaks spotted by our tests by at least
> half, see v3[1] for a more general overview.
> 
> Thanks a lot to Phillip Wood and Junio for the v4 review! I think this
> v5 should address all of the things brought up in v4 in one way or
> another, changes:
> 
>   * The "extra_headers" or "ref_message_ids" freeing is the same, but
>     the commit message is altered to explain that commit.

I'm still not that convinced that these are owned by the code in 
builtin/log.c but lets leave that for now. The rest of the changes look 
good. Thanks for working on this, when I use LSAN on my patches before 
submitting them I often see a lot of existing leaks coming from struct 
rev_info.

Best Wishes

Phillip

>   * The "revision.[ch]: split freeing of revs->commit into a function"
>     is gone, instead I applied a coccinelle rule to free_commit_list()
>     as suggested by Phillip Wood (well, the change in pattern, not the
>     rule was the suggestion).
> 
>   * The release_revisions() is now simply using free_commit_list()
>     without a NULL assignment to the member, and I changed some
>     FREE_AND_NULL() that I'd added (for e.g. cmdline) to just free().
> 
>     The "and NULL" was just cargo-culted from elswhere, but for
>     release_revisions() we not only aren't using the struct again (it's
>     not a "reset"), but it it wouldn't have worked as a reset before
>     anyway (some things free'd without NULL-ing).
> 
>     We could trivially have a "reset" variant of release_revisions(),
>     but since no current API user wanted it let's leave it, leving the
>     memory uninitialized will help to catch any invalid API usage.
> 
>   * I missed a object_array_clear() in builtin/stash.c that
>     could/should be free'd by release_revisions().
> 
>   * An obscure memory leak in stash.c (on "master") around
>     assert_stash_ref() has been fixed.
> 
>   * Add a "goto usage" pattern for a memory leak new in stash.c that
>     this series introduced, i.e. because strvec_clear() was moved to a
>     "cleanup" block if we invoked usage_with_options() between the two
>     we'd exit without the cleanup. Now we'll cleanup on "usage" too.
> 
>   * Updated commit message & docs around REV_INFO_INIT, hopefully
>     addressing the thread left off at
>     https://lore.kernel.org/git/220401.86h77dnkpw.gmgdl@evledraar.gmail.com/
> 
> 1. https://lore.kernel.org/git/cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com/
> 
> Ævar Arnfjörð Bjarmason (27):
>    t/helper/test-fast-rebase.c: don't leak "struct strbuf"
>    blame: use "goto cleanup" for cleanup_scoreboard()
>    string_list API users: use string_list_init_{no,}dup
>    format-patch: don't leak "extra_headers" or "ref_message_ids"
>    cocci: add and apply free_commit_list() rules
>    revision.[ch]: provide and start using a release_revisions()
>    revisions API users: add straightforward release_revisions()
>    revision.[ch]: document and move code declared around "init"
>    revisions API users: use release_revisions() needing REV_INFO_INIT
>    stash: always have the owner of "stash_info" free it
>    revisions API users: add "goto cleanup" for release_revisions()
>    revisions API users: use release_revisions() in http-push.c
>    revisions API users: use release_revisions() in builtin/log.c
>    revisions API users: use release_revisions() with UNLEAK()
>    revisions API users: use release_revisions() for "prune_data" users
>    revisions API: have release_revisions() release "commits"
>    revisions API: have release_revisions() release "mailmap"
>    revisions API: have release_revisions() release "cmdline"
>    revisions API: have release_revisions() release "filter"
>    revisions API: have release_revisions() release "grep_filter"
>    revisions API: have release_revisions() release "prune_data"
>    revisions API: clear "boundary_commits" in release_revisions()
>    revisions API: release "reflog_info" in release revisions()
>    revisions API: call diff_free(&revs->pruning) in revisions_release()
>    revisions API: have release_revisions() release "date_mode"
>    revisions API: have release_revisions() release "topo_walk_info"
>    revisions API: add a TODO for diff_free(&revs->diffopt)
> 
>   add-interactive.c                          |   3 +-
>   bisect.c                                   |  20 ++--
>   builtin/add.c                              |   3 +-
>   builtin/am.c                               |   3 +
>   builtin/bisect--helper.c                   |   2 +
>   builtin/blame.c                            |   4 +-
>   builtin/checkout.c                         |   3 +-
>   builtin/commit.c                           |   6 +-
>   builtin/describe.c                         |   2 +
>   builtin/diff-files.c                       |   8 +-
>   builtin/diff-index.c                       |   5 +-
>   builtin/diff.c                             |   2 +-
>   builtin/fast-export.c                      |   1 +
>   builtin/log.c                              |  36 ++++---
>   builtin/merge.c                            |   2 +
>   builtin/pack-objects.c                     |   2 +
>   builtin/prune.c                            |   1 +
>   builtin/reflog.c                           |   1 +
>   builtin/rev-list.c                         |  25 +++--
>   builtin/shortlog.c                         |   8 +-
>   builtin/stash.c                            | 115 +++++++++++----------
>   builtin/submodule--helper.c                |  14 ++-
>   bundle.c                                   |  12 ++-
>   commit.c                                   |  19 ++--
>   contrib/coccinelle/free.cocci              |  27 +++++
>   diff-lib.c                                 |   8 +-
>   fmt-merge-msg.c                            |   1 +
>   http-push.c                                |   3 +-
>   merge-ort.c                                |   1 +
>   merge-recursive.c                          |   5 +-
>   midx.c                                     |   1 +
>   pack-bitmap-write.c                        |   1 +
>   range-diff.c                               |   2 +-
>   ref-filter.c                               |   1 +
>   reflog-walk.c                              |  24 ++++-
>   reflog-walk.h                              |   1 +
>   remote.c                                   |   1 +
>   revision.c                                 |  72 ++++++++++---
>   revision.h                                 |  73 ++++++++-----
>   sequencer.c                                |  26 +++--
>   shallow.c                                  |   1 +
>   submodule.c                                |  11 +-
>   t/helper/test-fast-rebase.c                |  23 +++--
>   t/helper/test-revision-walking.c           |   1 +
>   t/lib-git-svn.sh                           |   4 +
>   t/t0056-git-C.sh                           |   1 +
>   t/t0062-revision-walking.sh                |   1 +
>   t/t0100-previous.sh                        |   1 +
>   t/t0101-at-syntax.sh                       |   2 +
>   t/t1001-read-tree-m-2way.sh                |   1 +
>   t/t1002-read-tree-m-u-2way.sh              |   1 +
>   t/t1060-object-corruption.sh               |   2 +
>   t/t1401-symbolic-ref.sh                    |   2 +
>   t/t1411-reflog-show.sh                     |   1 +
>   t/t1412-reflog-loop.sh                     |   2 +
>   t/t1415-worktree-refs.sh                   |   1 +
>   t/t2015-checkout-unborn.sh                 |   1 +
>   t/t2200-add-update.sh                      |   1 +
>   t/t3302-notes-index-expensive.sh           |   1 +
>   t/t3303-notes-subtrees.sh                  |   1 +
>   t/t3305-notes-fanout.sh                    |   1 +
>   t/t3408-rebase-multi-line.sh               |   1 +
>   t/t4021-format-patch-numbered.sh           |   1 +
>   t/t4027-diff-submodule.sh                  |   1 +
>   t/t4028-format-patch-mime-headers.sh       |   2 +
>   t/t4036-format-patch-signer-mime.sh        |   1 +
>   t/t4039-diff-assume-unchanged.sh           |   1 +
>   t/t4055-diff-context.sh                    |   1 +
>   t/t4066-diff-emit-delay.sh                 |   1 +
>   t/t4122-apply-symlink-inside.sh            |   1 +
>   t/t4126-apply-empty.sh                     |   1 -
>   t/t4128-apply-root.sh                      |   1 +
>   t/t4206-log-follow-harder-copies.sh        |   2 +
>   t/t4207-log-decoration-colors.sh           |   1 +
>   t/t4212-log-corrupt.sh                     |   1 +
>   t/t5301-sliding-window.sh                  |   2 +
>   t/t5313-pack-bounds-checks.sh              |   2 +
>   t/t5316-pack-delta-depth.sh                |   2 +
>   t/t5320-delta-islands.sh                   |   2 +
>   t/t5322-pack-objects-sparse.sh             |   1 +
>   t/t5506-remote-groups.sh                   |   1 +
>   t/t5513-fetch-track.sh                     |   1 +
>   t/t5515-fetch-merge-logic.sh               |   1 +
>   t/t5518-fetch-exit-status.sh               |   1 +
>   t/t5532-fetch-proxy.sh                     |   2 +
>   t/t5600-clone-fail-cleanup.sh              |   1 +
>   t/t5900-repo-selection.sh                  |   2 +
>   t/t6002-rev-list-bisect.sh                 |   1 +
>   t/t6003-rev-list-topo-order.sh             |   1 +
>   t/t6005-rev-list-count.sh                  |   1 +
>   t/t6018-rev-list-glob.sh                   |   1 +
>   t/t6100-rev-list-in-order.sh               |   1 +
>   t/t6101-rev-parse-parents.sh               |   1 +
>   t/t6110-rev-list-sparse.sh                 |   1 +
>   t/t6114-keep-packs.sh                      |   2 +
>   t/t6131-pathspec-icase.sh                  |   2 +
>   t/t7008-filter-branch-null-sha1.sh         |   1 +
>   t/t7702-repack-cyclic-alternate.sh         |   2 +
>   t/t9001-send-email.sh                      |   1 +
>   t/t9100-git-svn-basic.sh                   |   1 +
>   t/t9101-git-svn-props.sh                   |   2 +
>   t/t9104-git-svn-follow-parent.sh           |   2 +
>   t/t9106-git-svn-commit-diff-clobber.sh     |   2 +
>   t/t9115-git-svn-dcommit-funky-renames.sh   |   1 +
>   t/t9116-git-svn-log.sh                     |   1 +
>   t/t9122-git-svn-author.sh                  |   2 +
>   t/t9127-git-svn-partial-rebuild.sh         |   1 +
>   t/t9129-git-svn-i18n-commitencoding.sh     |   1 +
>   t/t9132-git-svn-broken-symlink.sh          |   1 +
>   t/t9139-git-svn-non-utf8-commitencoding.sh |   1 +
>   t/t9146-git-svn-empty-dirs.sh              |   2 +
>   t/t9148-git-svn-propset.sh                 |   1 +
>   t/t9160-git-svn-preserve-empty-dirs.sh     |   1 +
>   t/t9162-git-svn-dcommit-interactive.sh     |   2 +
>   t/t9164-git-svn-dcommit-concurrent.sh      |   2 +
>   t/t9501-gitweb-standalone-http-status.sh   |   1 +
>   wt-status.c                                |  15 +--
>   117 files changed, 502 insertions(+), 190 deletions(-)
> 
> Range-diff against v4:
>   1:  b99a07f98ee =  1:  e20f951a64c t/helper/test-fast-rebase.c: don't leak "struct strbuf"
>   2:  da5be507c0f =  2:  de2e08a14e6 blame: use "goto cleanup" for cleanup_scoreboard()
>   3:  243f7c8df96 =  3:  364aa7d8990 string_list API users: use string_list_init_{no,}dup
>   4:  69f0aabe38f !  4:  5d4ad92d47b format-patch: don't leak "extra_headers" or "ref_message_ids"
>      @@ Commit message
>           cmd_format_patch(). These two are unusual special-cases in being in
>           the "struct rev_info", but not being "owned" by the code in
>           revision.c. I.e. they're members of the struct so that this code in
>      -    "builtin/log.c" can pass information code in log-tree.c.
>      +    "builtin/log.c" can conveniently pass information code in
>      +    "log-tree.c".
>      +
>      +    See e.g. the make_cover_letter() caller of log_write_email_headers()
>      +    here in "builtin/log.c", and [1] for a demonstration of where the
>      +    "extra_headers" and "ref_message_ids" struct members are used.
>       
>           See 20ff06805c6 (format-patch: resurrect extra headers from config,
>           2006-06-02) and d1566f7883f (git-format-patch: Make the second and
>      @@ Commit message
>           variable to hold it, to avoid the eventual cast from "const char *"
>           when we free() it.
>       
>      +    1. https://lore.kernel.org/git/220401.868rsoogxf.gmgdl@evledraar.gmail.com/
>      +
>           Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
>       
>        ## builtin/log.c ##
>   5:  2d0ed57ec2e <  -:  ----------- revision.[ch]: split freeing of revs->commit into a function
>   -:  ----------- >  5:  c38db28f920 cocci: add and apply free_commit_list() rules
>   6:  b9beaba16bf !  6:  b99f96599bb revision.[ch]: provide and start using a release_revisions()
>      @@ Commit message
>           turn adds a lot of noise when trying to mark up tests with
>           "TEST_PASSES_SANITIZE_LEAK=true".
>       
>      -    The users of that API are largely one-shot, e.g. "git log", so
>      -    arguably freeing the memory is a waste of time, but in many cases
>      -    they've actually been trying to free the memory, and just doing that
>      -    in a buggy manner.
>      +    The users of that API are largely one-shot, e.g. "git rev-list" or
>      +    "git log", or the "git checkout" and "git stash" being modified here
>      +
>      +    For these callers freeing the memory is arguably a waste of time, but
>      +    in many cases they've actually been trying to free the memory, and
>      +    just doing that in a buggy manner.
>       
>           Let's provide a release_revisions() function for these users, and
>           start migrating them over per the plan outlined in [1]. Right now this
>      @@ builtin/checkout.c: static void show_local_changes(struct object *head,
>        static void describe_detached_head(const char *msg, struct commit *commit)
>       
>        ## builtin/stash.c ##
>      +@@ builtin/stash.c: static int check_changes_tracked_files(const struct pathspec *ps)
>      + 		goto done;
>      + 	}
>      +
>      +-	object_array_clear(&rev.pending);
>      + 	result = run_diff_files(&rev, 0);
>      + 	if (diff_result_code(&rev.diffopt, result)) {
>      + 		ret = 1;
>      +@@ builtin/stash.c: static int check_changes_tracked_files(const struct pathspec *ps)
>      +
>      + done:
>      + 	clear_pathspec(&rev.prune_data);
>      ++	release_revisions(&rev);
>      + 	return ret;
>      + }
>      +
>       @@ builtin/stash.c: static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
>        
>        done:
>      @@ range-diff.c: int is_range_diff_range(const char *arg)
>        }
>       
>        ## revision.c ##
>      -@@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)
>      - 	}
>      +@@ revision.c: int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
>      + 	return left;
>        }
>        
>       +void release_revisions(struct rev_info *revs)
>   7:  80ec9a131c0 =  7:  521f6967b64 revisions API users: add straightforward release_revisions()
>   8:  086cec742b4 !  8:  003b507e0b7 revision.[ch]: document and move code declared around "init"
>      @@ revision.h: void mark_trees_uninteresting_sparse(struct repository *r, struct oi
>        void show_object_with_name(FILE *, struct object *, const char *);
>        
>       +/**
>      -+ * Helpers to check if a "struct string_list" item matches wild
>      ++ * Helpers to check if a "struct string_list" item matches with
>       + * wildmatch().
>       + */
>       +int ref_excluded(struct string_list *, const char *path);
>   9:  2f4e65fb534 !  9:  240307bc3c6 revisions API users: use release_revisions() needing REV_INFO_INIT
>      @@ Commit message
>           need to have their "struct rev_info" zero-initialized before we can
>           start using it.
>       
>      -    To do this add a stub "REV_INFO_INIT" macro, ideally macro would be
>      -    able to fully initialize a "struct rev_info", but all it does is the
>      -    equivalent of assigning "{ 0 }" to the struct, the API user will still
>      -    need to use repo_init_revisions(). In some future follow-up work we'll
>      -    hopefully make REV_INFO_INIT be a "stand-alone" init likke STRBUF_INIT
>      -    and other similar macros.
>      -
>           For the bundle.c code see the early exit case added in
>           3bbbe467f29 (bundle verify: error out if called without an object
>           database, 2019-05-27).
>      @@ revision.h: struct rev_info {
>       +/**
>       + * Initialize the "struct rev_info" structure with a macro.
>       + *
>      -+ * This is not sufficient (yet!) to initialize a "struct rev_info",
>      -+ * but it's OK (but redundant) to use it before a call to
>      -+ * repo_init_revisions(), which does the real initialization. By using
>      -+ * this it's safe to call release_revisions() on the "struct rev_info"
>      -+ * without having called repo_init_revisions().
>      ++ * This will not fully initialize a "struct rev_info", the
>      ++ * repo_init_revisions() function needs to be called before
>      ++ * setup_revisions() and any revision walking takes place.
>      ++ *
>      ++ * Use REV_INFO_INIT to make the "struct rev_info" safe for passing to
>      ++ * release_revisions() when it's inconvenient (e.g. due to a "goto
>      ++ * cleanup" pattern) to arrange for repo_init_revisions() to be called
>      ++ * before release_revisions() is called.
>      ++ *
>      ++ * Initializing with this REV_INFO_INIT is redundant to invoking
>      ++ * repo_init_revisions(). If repo_init_revisions() is guaranteed to be
>      ++ * called before release_revisions() the "struct rev_info" can be left
>      ++ * uninitialized.
>       + */
>       +#define REV_INFO_INIT { 0 }
>       +
>      @@ submodule.c: void show_submodule_diff_summary(struct diff_options *o, const char
>        	struct commit_list *merge_bases = NULL;
>        	struct repository *sub;
>       @@ submodule.c: void show_submodule_diff_summary(struct diff_options *o, const char *path,
>      - 	print_submodule_diff_summary(sub, &rev, o);
>        
>        out:
>      + 	free_commit_list(merge_bases);
>       +	release_revisions(&rev);
>      - 	if (merge_bases)
>      - 		free_commit_list(merge_bases);
>        	clear_commit_marks(left, ~0);
>      + 	clear_commit_marks(right, ~0);
>      + 	if (sub) {
> 10:  145a0f74b6a ! 10:  4c5404912e9 stash: always have the owner of "stash_info" free it
>      @@ Commit message
>           pattern we'd otherwise free() uninitialized memory in some cases, as
>           we only strbuf_init() the string in get_stash_info().
>       
>      -    So while it's the smallest possible change, let's convert all users of
>      -    this pattern in the file while we're at it.
>      +    So while it's not the smallest possible change, let's convert all
>      +    users of this pattern in the file while we're at it.
>       
>           A good follow-up to this change would be to change all the "ret = -1;
>           goto done;" in this file to instead use a "goto cleanup", and
>      @@ Commit message
>           would allow us to drop a lot of needless brace verbosity on two-line
>           "if" statements, but let's leave that alone for now.
>       
>      +    To ensure that there's a 1=1 mapping between owners of the "struct
>      +    stash_info" and free_stash_info() change the assert_stash_ref()
>      +    function to be a trivial get_stash_info_assert() wrapper. The caller
>      +    will call free_stash_info(), and by returning -1 we'll eventually (via
>      +    !!ret) exit with status 1 anyway.
>      +
>           Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
>       
>        ## builtin/stash.c ##
>      @@ builtin/stash.c: static int apply_stash(int argc, const char **argv, const char
>        	free_stash_info(&info);
>        	return ret;
>        }
>      -@@ builtin/stash.c: static void assert_stash_ref(struct stash_info *info)
>      +@@ builtin/stash.c: static int do_drop_stash(struct stash_info *info, int quiet)
>      + 	return 0;
>      + }
>      +
>      +-static void assert_stash_ref(struct stash_info *info)
>      ++static int get_stash_info_assert(struct stash_info *info, int argc,
>      ++				 const char **argv)
>      + {
>      +-	if (!info->is_stash_ref) {
>      +-		error(_("'%s' is not a stash reference"), info->revision.buf);
>      +-		free_stash_info(info);
>      +-		exit(1);
>      +-	}
>      ++	int ret = get_stash_info(info, argc, argv);
>      ++
>      ++	if (ret < 0)
>      ++		return ret;
>      ++
>      ++	if (!info->is_stash_ref)
>      ++		return error(_("'%s' is not a stash reference"), info->revision.buf);
>      ++
>      ++	return 0;
>      + }
>        
>        static int drop_stash(int argc, const char **argv, const char *prefix)
>        {
>      @@ builtin/stash.c: static void assert_stash_ref(struct stash_info *info)
>        		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
>        		OPT_END()
>       @@ builtin/stash.c: static int drop_stash(int argc, const char **argv, const char *prefix)
>      + 	argc = parse_options(argc, argv, prefix, options,
>        			     git_stash_drop_usage, 0);
>        
>      - 	if (get_stash_info(&info, argc, argv))
>      +-	if (get_stash_info(&info, argc, argv))
>       -		return -1;
>      +-
>      +-	assert_stash_ref(&info);
>      ++	if (get_stash_info_assert(&info, argc, argv))
>       +		goto cleanup;
>        
>      - 	assert_stash_ref(&info);
>      -
>        	ret = do_drop_stash(&info, quiet);
>       +cleanup:
>        	free_stash_info(&info);
>      @@ builtin/stash.c: static int drop_stash(int argc, const char **argv, const char *
>        		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
>        		OPT_BOOL(0, "index", &index,
>       @@ builtin/stash.c: static int pop_stash(int argc, const char **argv, const char *prefix)
>      + 	argc = parse_options(argc, argv, prefix, options,
>        			     git_stash_pop_usage, 0);
>        
>      - 	if (get_stash_info(&info, argc, argv))
>      +-	if (get_stash_info(&info, argc, argv))
>       -		return -1;
>      ++	if (get_stash_info_assert(&info, argc, argv))
>       +		goto cleanup;
>        
>      - 	assert_stash_ref(&info);
>      +-	assert_stash_ref(&info);
>        	if ((ret = do_apply_stash(prefix, &info, index, quiet)))
>      -@@ builtin/stash.c: static int pop_stash(int argc, const char **argv, const char *prefix)
>      + 		printf_ln(_("The stash entry is kept in case "
>      + 			    "you need it again."));
>        	else
>        		ret = do_drop_stash(&info, quiet);
>        
>      @@ builtin/stash.c: static void diff_include_untracked(const struct stash_info *inf
>        	struct rev_info rev;
>        	struct strvec stash_args = STRVEC_INIT;
>        	struct strvec revision_args = STRVEC_INIT;
>      +@@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *prefix)
>      + 			      UNTRACKED_ONLY, PARSE_OPT_NONEG),
>      + 		OPT_END()
>      + 	};
>      ++	int do_usage = 0;
>      +
>      + 	init_diff_ui_defaults();
>      + 	git_config(git_diff_ui_config, NULL);
>       @@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *prefix)
>        			strvec_push(&revision_args, argv[i]);
>        	}
>      @@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *
>        		}
>        	}
>        
>      + 	argc = setup_revisions(revision_args.nr, revision_args.v, &rev, NULL);
>      +-	if (argc > 1) {
>      +-		free_stash_info(&info);
>      +-		usage_with_options(git_stash_show_usage, options);
>      +-	}
>      ++	if (argc > 1)
>      ++		goto usage;
>      + 	if (!rev.diffopt.output_format) {
>      + 		rev.diffopt.output_format = DIFF_FORMAT_PATCH;
>      + 		diff_setup_done(&rev.diffopt);
>       @@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *prefix)
>        	}
>        	log_tree_diff_flush(&rev);
>        
>      -+	ret = diff_result_code(&rev.diffopt, 0);;
>      ++	ret = diff_result_code(&rev.diffopt, 0);
>       +cleanup:
>       +	strvec_clear(&stash_args);
>        	free_stash_info(&info);
>       -	return diff_result_code(&rev.diffopt, 0);
>      ++	if (do_usage)
>      ++		usage_with_options(git_stash_show_usage, options);
>       +	return ret;
>      ++usage:
>      ++	do_usage = 1;
>      ++	goto cleanup;
>        }
>        
>        static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,
>      @@ builtin/stash.c: static int do_push_stash(const struct pathspec *ps, const char
>        	struct strbuf patch = STRBUF_INIT;
>        	struct strbuf stash_msg_buf = STRBUF_INIT;
>        	struct strbuf untracked_files = STRBUF_INIT;
>      +@@ builtin/stash.c: static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
>      + 	}
>      +
>      + done:
>      ++	free_stash_info(&info);
>      + 	strbuf_release(&stash_msg_buf);
>      + 	return ret;
>      + }
> 11:  c9898a5a96b ! 11:  4210232e3a1 revisions API users: add "goto cleanup" for release_revisions()
>      @@ builtin/stash.c: static int show_stash(int argc, const char **argv, const char *
>        	strvec_clear(&stash_args);
>        	free_stash_info(&info);
>       +	release_revisions(&rev);
>      + 	if (do_usage)
>      + 		usage_with_options(git_stash_show_usage, options);
>        	return ret;
>      - }
>      -
>       
>        ## builtin/submodule--helper.c ##
>       @@ builtin/submodule--helper.c: static int compute_summary_module_list(struct object_id *head_oid,
> 12:  6e41d666223 = 12:  1918ca0a85d revisions API users: use release_revisions() in http-push.c
> 13:  02ca92660af = 13:  6a02b7aae4e revisions API users: use release_revisions() in builtin/log.c
> 14:  1aeb1127f34 = 14:  ddc7402b054 revisions API users: use release_revisions() with UNLEAK()
> 15:  c12b3d5a035 ! 15:  03e9bd73d4b revisions API users: use release_revisions() for "prune_data" users
>      @@ builtin/add.c: int add_files_to_cache(const char *prefix,
>        }
>        
>       
>      - ## builtin/stash.c ##
>      -@@ builtin/stash.c: static int check_changes_tracked_files(const struct pathspec *ps)
>      -
>      - done:
>      - 	clear_pathspec(&rev.prune_data);
>      -+	release_revisions(&rev);
>      - 	return ret;
>      - }
>      -
>      -
>        ## diff-lib.c ##
>       @@ diff-lib.c: int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
>        	if (diff_cache(&revs, tree_oid, NULL, 1))
> 16:  2e351702677 <  -:  ----------- revisions API: have release_revisions() release "commits"
>   -:  ----------- > 16:  285673e40bf revisions API: have release_revisions() release "commits"
> 17:  f67f1f5a575 ! 17:  ca659940ee5 revisions API: have release_revisions() release "mailmap"
>      @@ builtin/commit.c: static const char *find_author_by_nickname(const char *name)
>        	}
>       
>        ## revision.c ##
>      -@@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)
>      - 	}
>      +@@ revision.c: int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
>      + 	return left;
>        }
>        
>       +static void release_revisions_mailmap(struct string_list *mailmap)
>      @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)
>       +
>        void release_revisions(struct rev_info *revs)
>        {
>      - 	release_revisions_commit_list(revs);
>      + 	free_commit_list(revs->commits);
>        	object_array_clear(&revs->pending);
>       +	release_revisions_mailmap(revs->mailmap);
>        }
> 18:  1df61550e5a ! 18:  7987c748ba4 revisions API: have release_revisions() release "cmdline"
>      @@ Commit message
>           Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
>       
>        ## revision.c ##
>      -@@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)
>      - 	}
>      +@@ revision.c: int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
>      + 	return left;
>        }
>        
>       +static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)
>      @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)
>       +		return;
>       +	for (i = 0; i < cmdline->nr; i++)
>       +		free((char *)cmdline->rev[i].name);
>      -+	FREE_AND_NULL(cmdline->rev);
>      ++	free(cmdline->rev);
>       +}
>       +
>        static void release_revisions_mailmap(struct string_list *mailmap)
>      @@ revision.c: static void release_revisions_commit_list(struct rev_info *revs)
>        	if (!mailmap)
>       @@ revision.c: void release_revisions(struct rev_info *revs)
>        {
>      - 	release_revisions_commit_list(revs);
>      + 	free_commit_list(revs->commits);
>        	object_array_clear(&revs->pending);
>       +	release_revisions_cmdline(&revs->cmdline);
>        	release_revisions_mailmap(revs->mailmap);
> 19:  065c3cc91b2 ! 19:  e6e87ce3608 revisions API: have release_revisions() release "filter"
>      @@ Commit message
>       
>        ## revision.c ##
>       @@ revision.c: void release_revisions(struct rev_info *revs)
>      - 	release_revisions_commit_list(revs);
>      + 	free_commit_list(revs->commits);
>        	object_array_clear(&revs->pending);
>        	release_revisions_cmdline(&revs->cmdline);
>       +	list_objects_filter_release(&revs->filter);
> 20:  806fef06b87 = 20:  ebdcdc88d79 revisions API: have release_revisions() release "grep_filter"
> 21:  fabbe452a14 = 21:  84c8714571a revisions API: have release_revisions() release "prune_data"
> 22:  cb201fa1914 ! 22:  d96642d3502 revisions API: clear "boundary_commits" in release_revisions()
>      @@ Commit message
>        ## revision.c ##
>       @@ revision.c: void release_revisions(struct rev_info *revs)
>        {
>      - 	release_revisions_commit_list(revs);
>      + 	free_commit_list(revs->commits);
>        	object_array_clear(&revs->pending);
>       +	object_array_clear(&revs->boundary_commits);
>        	release_revisions_cmdline(&revs->cmdline);
> 23:  0363a69073c = 23:  0e9745d9ee1 revisions API: release "reflog_info" in release revisions()
> 24:  bad359b2983 = 24:  c7f3e92a8d1 revisions API: call diff_free(&revs->pruning) in revisions_release()
> 25:  9a51c1ae1d1 = 25:  d428d752462 revisions API: have release_revisions() release "date_mode"
> 26:  b56db37940d ! 26:  fbf233ec768 revisions API: have release_revisions() release "topo_walk_info"
>      @@ revision.c: static void release_revisions_mailmap(struct string_list *mailmap)
>       +
>        void release_revisions(struct rev_info *revs)
>        {
>      - 	release_revisions_commit_list(revs);
>      + 	free_commit_list(revs->commits);
>       @@ revision.c: void release_revisions(struct rev_info *revs)
>        	free_grep_patterns(&revs->grep_filter);
>        	diff_free(&revs->pruning);
> 27:  b02cb8fcded = 27:  ec2c77ce869 revisions API: add a TODO for diff_free(&revs->diffopt)

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v5 14/27] revisions API users: use release_revisions() with UNLEAK()
  2022-04-03  9:27           ` Phillip Wood
@ 2022-04-03 13:55             ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-03 13:55 UTC (permalink / raw)
  To: Phillip Wood
  Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren,
	Derrick Stolee, brian m . carlson


On Sun, Apr 03 2022, Phillip Wood wrote:

> Hi Ævar
>
> [continuing with v5 from where I left off with v4]
>
> On 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:
>> Use a release_revisions() with those "struct rev_list" users which
>> already "UNLEAK" the struct. It may seem odd to simultaneously attempt
>> to free() memory, but also to explicitly ignore whether we have memory
>> leaks in the same.
>> As explained in preceding commits this is being done to use the
>> built-in commands as a guinea pig for whether the release_revisions()
>> function works as expected, we'd like to test e.g. whether we segfault
>> as we change it. In subsequent commits we'll then remove these
>> UNLEAK() as the function is made to free the memory that caused us to
>> add them in the first place.
>
> I'm a bit confused by this, the previous commit argued in favor of
> removing UNLEAK() so would could see the leaks and fix them, this is 
> saying we should hide the leaks.

..until the UNLEAK() is removed later in the sier.s

All commits in this series pass with the SANITIZE=leak CI check. In this
case it would result in too much churn to remove the UNLEAK() before we
have this solving some of the more meaningful leaks, and if we do this
later we don't have all codepaths that can use release_revisions() using
it as we free things one at a time.

> Best Wishes
>
> Phillip
>
>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
>> ---
>>   builtin/diff-index.c | 4 +++-
>>   builtin/diff.c       | 1 +
>>   2 files changed, 4 insertions(+), 1 deletion(-)
>> diff --git a/builtin/diff-index.c b/builtin/diff-index.c
>> index 5fd23ab5b6c..3a83183c312 100644
>> --- a/builtin/diff-index.c
>> +++ b/builtin/diff-index.c
>> @@ -71,5 +71,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
>>   	}
>>   	result = run_diff_index(&rev, option);
>>   	UNLEAK(rev);
>> -	return diff_result_code(&rev.diffopt, result);
>> +	result = diff_result_code(&rev.diffopt, result);
>> +	release_revisions(&rev);
>> +	return result;
>>   }
>> diff --git a/builtin/diff.c b/builtin/diff.c
>> index bb7fafca618..dd48336da56 100644
>> --- a/builtin/diff.c
>> +++ b/builtin/diff.c
>> @@ -595,6 +595,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
>>   	if (1 < rev.diffopt.skip_stat_unmatch)
>>   		refresh_index_quietly();
>>   	UNLEAK(rev);
>> +	release_revisions(&rev);
>>   	UNLEAK(ent);
>>   	UNLEAK(blob);
>>   	return result;


^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v5 17/27] revisions API: have release_revisions() release "mailmap"
  2022-04-03  9:33           ` Phillip Wood
@ 2022-04-03 13:57             ` Ævar Arnfjörð Bjarmason
  2022-04-04  9:19               ` Phillip Wood
  0 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-03 13:57 UTC (permalink / raw)
  To: Phillip Wood
  Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren,
	Derrick Stolee, brian m . carlson


On Sun, Apr 03 2022, Phillip Wood wrote:

> Hi Ævar
>
> On 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:
>> Extend the the release_revisions() function so that it frees the
>> "mailmap" in the "struct rev_info".
>> The log family of functions now calls the clear_mailmap() function
>> added in fa8afd18e5a (revisions API: provide and use a
>> release_revisions(), 2021-09-19), allowing us to whitelist some tests
>> with "TEST_PASSES_SANITIZE_LEAK=true".
>> Unfortunately having a pointer to a mailmap in "struct rev_info"
>> instead of an embedded member that we "own" get a bit messy, as can be
>> seen in the change to builtin/commit.c.
>> When we free() this data we won't be able to tell apart a pointer to
>> a
>> "mailmap" on the heap from one on the stack. As seen in
>> ea57bc0d41b (log: add --use-mailmap option, 2013-01-05) the "log"
>> family allocates it on the heap, but in the find_author_by_nickname()
>> code added in ea16794e430 (commit: search author pattern against
>> mailmap, 2013-08-23) we allocated it on the stack instead.
>> Ideally we'd simply change that member to a "struct string_list
>> mailmap" and never free() the "mailmap" itself, but that would be a
>> much larger change to the revisions API.
>
> I agree it makes sense to leave that for now
>
>> We have code that needs to hand an existing "mailmap" to a "struct
>> rev_info", while we could change all of that, let's not go there
>> now.
>> The complexity isn't in the ownership of the "mailmap" per-se, but
>> that various things assume a "rev_info.mailmap == NULL" means "doesn't
>> want mailmap", if we changed that to an init'd "struct string_list
>> we'd need to carefully refactor things to change those assumptions.
>> Let's instead always free() it, and simply declare that if you add
>> such a "mailmap" it must be allocated on the heap. Any modern libc
>> will correctly panic if we free() a stack variable, so this should be
>> safe going forward.
>> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
>> ---
>>   builtin/commit.c                   | 5 ++---
>>   revision.c                         | 9 +++++++++
>>   t/t0056-git-C.sh                   | 1 +
>>   t/t3302-notes-index-expensive.sh   | 1 +
>>   t/t4055-diff-context.sh            | 1 +
>>   t/t4066-diff-emit-delay.sh         | 1 +
>>   t/t7008-filter-branch-null-sha1.sh | 1 +
>>   7 files changed, 16 insertions(+), 3 deletions(-)
>> diff --git a/builtin/commit.c b/builtin/commit.c
>> index c7eda9bbb72..cd6cebcf8c8 100644
>> --- a/builtin/commit.c
>> +++ b/builtin/commit.c
>> @@ -1100,7 +1100,6 @@ static const char *find_author_by_nickname(const char *name)
>>   	struct rev_info revs;
>>   	struct commit *commit;
>>   	struct strbuf buf = STRBUF_INIT;
>> -	struct string_list mailmap = STRING_LIST_INIT_NODUP;
>>   	const char *av[20];
>>   	int ac = 0;
>>   @@ -1111,7 +1110,8 @@ static const char
>> *find_author_by_nickname(const char *name)
>>   	av[++ac] = buf.buf;
>>   	av[++ac] = NULL;
>>   	setup_revisions(ac, av, &revs, NULL);
>> -	revs.mailmap = &mailmap;
>> +	revs.mailmap = xmalloc(sizeof(struct string_list));
>> +	string_list_init_nodup(revs.mailmap);
>
> This is a common pattern in one of the previous patches, is it worth
> adding helpers to allocate and initialize a struct string_list? Maybe 
> string_list_new_nodup() and string_list_new_dup().

Maybe, but generally in the git codebase things malloc and then init(),
if we're going to add something like this *_new() that would be a change
for a lot more APIs than just mailmap.

And if it's just for mailmap I don't see how the inconsistency with
other code would be worth it.

>>   	read_mailmap(revs.mailmap);
>>     	if (prepare_revision_walk(&revs))
>> @@ -1122,7 +1122,6 @@ static const char *find_author_by_nickname(const char *name)
>>   		ctx.date_mode.type = DATE_NORMAL;
>>   		strbuf_release(&buf);
>>   		format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
>> -		clear_mailmap(&mailmap);
>>   		release_revisions(&revs);
>>   		return strbuf_detach(&buf, NULL);
>>   	}
>> diff --git a/revision.c b/revision.c
>> index 553f7de8250..622f0faecc4 100644
>> --- a/revision.c
>> +++ b/revision.c
>> @@ -2926,10 +2926,19 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
>>   	return left;
>>   }
>>   +static void release_revisions_mailmap(struct string_list
>> *mailmap)
>> +{
>> +	if (!mailmap)
>> +		return;
>> +	clear_mailmap(mailmap);
>> +	free(mailmap);
>> +}
>
> It's not a big issue but if there are no other users of this then it
> could just go inside release_revisions, my impression is that this 
> series builds a collection of very small functions whose only caller
> is release_revisions()

Yes, these are just trivial static helpers so that each line in
release_revisions() corresponds to a member of the struct, without
loops, indentation for "don't free this" etc.

To the machine code it makes no difference at higher optimization
levels.

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v5 18/27] revisions API: have release_revisions() release "cmdline"
  2022-04-03 12:54           ` Phillip Wood
@ 2022-04-03 14:01             ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-03 14:01 UTC (permalink / raw)
  To: Phillip Wood
  Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren,
	Derrick Stolee, brian m . carlson


On Sun, Apr 03 2022, Phillip Wood wrote:

> Hi Ævar
>
> On 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:
>>   +static void release_revisions_cmdline(struct rev_cmdline_info
>> *cmdline)
>> +{
>> +	unsigned int i;
>> +
>> +	if (!cmdline)
>> +		return;
>
> I don't think we need this guard, the only instances of struct
> rev_cmdline_info exist within struct rev_info, as far as I can see it
> is never created on its own.

Yes, it won't ever be NULL. I'll fix that (missed it with the other NULL
check cargo-culting).

>> +	for (i = 0; i < cmdline->nr; i++)
>> +		free((char *)cmdline->rev[i].name);
>> +	free(cmdline->rev);
>
> This could just be in release_revisions()

Ditto <220403.86ilrqmflb.gmgdl@evledraar.gmail.com>. I.e. I'd prefer to
keep these for readability

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v5 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release()
  2022-04-03 13:00           ` Phillip Wood
@ 2022-04-03 14:04             ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-03 14:04 UTC (permalink / raw)
  To: Phillip Wood
  Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren,
	Derrick Stolee, brian m . carlson


On Sun, Apr 03 2022, Phillip Wood wrote:

> Hi Ævar
>
> On 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:
>> Call diff_free() on the "pruning" member of "struct rev_info".  Doing
>> so makes several tests pass under SANITIZE=leak.
>> This was also the last missing piece that allows us to remove the
>> UNLEAK() in "cmd_diff" and "cmd_diff_index", which allows us to use
>> those commands as a canary for general leaks in the revisions API. See
>> [1] for further rationale, and 886e1084d78 (builtin/: add UNLEAKs,
>> 2017-10-01) for the commit that added the UNLEAK() there.
>
> Oh is the the answer to my confusion about patch 14? I wonder if the
> change could come earlier so we can remove the UNLEAK()s all at once?

As noted in <220403.86mth2mfrh.gmgdl@evledraar.gmail.com> (where I meant
to say "series") It's this specific change that will free enough things
for the tests to pass without these UNLEAK()s.

And no, it can't come earlier beacuse just "pruning" or any other member
isn't sufficient, this is just the last straw.

IOW the API callers weren't calling UNLEAK() on specific members of
"rev", but the whole struct. So we needed to fix a lot of leaks before
we colud remove these.

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v4 13/27] revisions API users: use release_revisions() in builtin/log.c
  2022-04-02  9:22         ` Phillip Wood
@ 2022-04-03 14:07           ` Ævar Arnfjörð Bjarmason
  2022-04-04  9:27             ` Phillip Wood
  2022-04-03 21:49           ` Junio C Hamano
  1 sibling, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-03 14:07 UTC (permalink / raw)
  To: Phillip Wood
  Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren,
	Derrick Stolee, brian m . carlson


On Sat, Apr 02 2022, Phillip Wood wrote:

[A comment on v4, but also applies to v5 I think]

> On 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:
>> In preparation for having the "log" family of functions make wider use
>> of release_revisions() let's have them call it just before
>> exiting. This changes the "log", "whatchanged", "show",
>> "format-patch", etc. commands, all of which live in this file.
>> The release_revisions() API still only frees the "pending" member,
>> but
>> will learn to release more members of "struct rev_info" in subsequent
>> commits.
>> In the case of "format-patch" revert the addition of UNLEAK() in
>> dee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16),
>> which will cause several tests that previously passed under
>> "TEST_PASSES_SANITIZE_LEAK=true" to start failing.
>> In subsequent commits we'll now be able to use those tests to check
>> whether that part of the API is really leaking memory, and will fix
>> all of those memory leaks. Removing the UNLEAK() allows us to make
>> incremental progress in that direction. See [1] for further details
>> about this approach.
>
> This breaks "git bisect" but only when running the test suite to
> detect leaks so I guess that's not too bad. An alternative would be to
> manually remove the UNLEAK() when you're testing rather than
> committing the change.

It doesn't, for this series each individual commit passes with

    make test
    GIT_TEST_PASSING_SANITIZE_LEAK=true make test SANITIZE=leak 

And also in a stricter mode that I have locally (not in git yet):

    make test
    GIT_TEST_PASSING_SANITIZE_LEAK=check make test SANITIZE=leak 

Which ensures not only that the tests we marked as leak free pass, but
that no other tests we *haven't* marked pass unexpectedly (requires prep
changes before this series to mark the still-not-marked-but-should-be
tests).

I think that should address/help explain things re your questions about
some of the UNLEAK() back-and-forth.

I.e. there's a few changes that are in this series just so it can pass
in that "GIT_TEST_PASSING_SANITIZE_LEAK=check" mode, but it would still
pass in "GIT_TEST_PASSING_SANITIZE_LEAK=true", i.e. because we'd make
some new test pass unexpectedly.

But I think maintaining the 1=1 correspondance really helps to follow
along with this, i.e. tests are tweaked as they become leak-free, and we
(or well, mostly I) can be confident that I marked all the relevant
newlry passing ones, and that there are no regressions in-between.


>>   /*
>>    * This gives a rough estimate for how many commits we
>>    * will print out in the list.
>> @@ -558,7 +564,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
>>   	cmd_log_init(argc, argv, prefix, &rev, &opt);
>>   	if (!rev.diffopt.output_format)
>>   		rev.diffopt.output_format = DIFF_FORMAT_RAW;
>> -	return cmd_log_walk(&rev);
>> +	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
>
> This is a rather unusual pattern, at first I wondered if there were
> going to be more added to the body of cmd_log_deinit() in later
> commits but there isn't so why not just call release_revisions() here
> to be consistent with the other release_revisions() call that are
> added in other patches?

It's just a way to save every single call to this callsite a change on
top like this:
	
	diff --git a/builtin/log.c b/builtin/log.c
	index 5dad70aa47e..ece03536bed 100644
	--- a/builtin/log.c
	+++ b/builtin/log.c
	@@ -684,8 +684,11 @@ int cmd_show(int argc, const char **argv, const char *prefix)
	 	opt.tweak = show_setup_revisions_tweak;
	 	cmd_log_init(argc, argv, prefix, &rev, &opt);
	 
	-	if (!rev.no_walk)
	-		return cmd_log_deinit(cmd_log_walk(&rev), &rev);
	+	if (!rev.no_walk) {
	+		ret = cmd_log_walk(&rev);
	+		release_revisions(&rev);
	+		return ret;
	+	}
	 
	 	count = rev.pending.nr;
	 	objects = rev.pending.objects;

Which, given that there's 6 of them nicely cuts down on the resulting
verbosity.

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v5 00/27] revision.[ch]: add and use release_revisions()
  2022-04-03 13:07         ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Phillip Wood
@ 2022-04-03 14:18           ` Ævar Arnfjörð Bjarmason
  0 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-03 14:18 UTC (permalink / raw)
  To: Phillip Wood
  Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren,
	Derrick Stolee, brian m . carlson


On Sun, Apr 03 2022, Phillip Wood wrote:

> Hi Ævar
>
> I've left a few comments on this series for the patches that I had not
> looked at in v4. There's nothing major, the changes mostly make sense
> to me (though I'm not familiar with the revisions api)

Thanks a lot. I'll re-roll a v6 in response, but will wait a bit for any
other comments.

> On 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:
>> This series reduces the memory leaks spotted by our tests by at least
>> half, see v3[1] for a more general overview.
>> Thanks a lot to Phillip Wood and Junio for the v4 review! I think
>> this
>> v5 should address all of the things brought up in v4 in one way or
>> another, changes:
>>   * The "extra_headers" or "ref_message_ids" freeing is the same,
>> but
>>     the commit message is altered to explain that commit.
>
> I'm still not that convinced that these are owned by the code in
> builtin/log.c but lets leave that for now.

Thanks.

> The rest of the changes
> look good. Thanks for working on this, when I use LSAN on my patches
> before submitting them I often see a lot of existing leaks coming from
> struct rev_info.

Yeah it'll become a lot more useful now.

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v5 23/27] revisions API: release "reflog_info" in release revisions()
  2022-04-02 10:49         ` [PATCH v5 23/27] revisions API: release "reflog_info" in release revisions() Ævar Arnfjörð Bjarmason
@ 2022-04-03 15:19           ` Phillip Wood
  0 siblings, 0 replies; 252+ messages in thread
From: Phillip Wood @ 2022-04-03 15:19 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Hi Ævar

[It appears I forgot to press send earlier]

On 02/04/2022 11:49, Ævar Arnfjörð Bjarmason wrote:
> Add a missing reflog_walk_info_release() to "reflog-walk.c" and use it
> in release_revisions().
> 
> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>   reflog-walk.c            | 24 +++++++++++++++++++++++-
>   reflog-walk.h            |  1 +
>   revision.c               |  1 +
>   t/t0100-previous.sh      |  1 +
>   t/t1401-symbolic-ref.sh  |  2 ++
>   t/t1411-reflog-show.sh   |  1 +
>   t/t1412-reflog-loop.sh   |  2 ++
>   t/t1415-worktree-refs.sh |  1 +
>   8 files changed, 32 insertions(+), 1 deletion(-)
> 
> diff --git a/reflog-walk.c b/reflog-walk.c
> index 8ac4b284b6b..7aa6595a51f 100644
> --- a/reflog-walk.c
> +++ b/reflog-walk.c
> @@ -8,7 +8,7 @@
>   
>   struct complete_reflogs {
>   	char *ref;
> -	const char *short_ref;
> +	char *short_ref;

As this struct is only used internally I think changing the type rather 
than casting when we free it makes sense.

Best Wishes

Phillip

>   	struct reflog_info {
>   		struct object_id ooid, noid;
>   		char *email;
> @@ -51,9 +51,16 @@ static void free_complete_reflog(struct complete_reflogs *array)
>   	}
>   	free(array->items);
>   	free(array->ref);
> +	free(array->short_ref);
>   	free(array);
>   }
>   
> +static void complete_reflogs_clear(void *util, const char *str)
> +{
> +	struct complete_reflogs *array = util;
> +	free_complete_reflog(array);
> +}
> +
>   static struct complete_reflogs *read_complete_reflog(const char *ref)
>   {
>   	struct complete_reflogs *reflogs =
> @@ -116,6 +123,21 @@ void init_reflog_walk(struct reflog_walk_info **info)
>   	(*info)->complete_reflogs.strdup_strings = 1;
>   }
>   
> +void reflog_walk_info_release(struct reflog_walk_info *info)
> +{
> +	size_t i;
> +
> +	if (!info)
> +		return;
> +
> +	for (i = 0; i < info->nr; i++)
> +		free(info->logs[i]);
> +	string_list_clear_func(&info->complete_reflogs,
> +			       complete_reflogs_clear);
> +	free(info->logs);
> +	free(info);
> +}
> +
>   int add_reflog_for_walk(struct reflog_walk_info *info,
>   		struct commit *commit, const char *name)
>   {
> diff --git a/reflog-walk.h b/reflog-walk.h
> index e9e00ffd479..8076f10d9fb 100644
> --- a/reflog-walk.h
> +++ b/reflog-walk.h
> @@ -8,6 +8,7 @@ struct reflog_walk_info;
>   struct date_mode;
>   
>   void init_reflog_walk(struct reflog_walk_info **info);
> +void reflog_walk_info_release(struct reflog_walk_info *info);
>   int add_reflog_for_walk(struct reflog_walk_info *info,
>   			struct commit *commit, const char *name);
>   void show_reflog_message(struct reflog_walk_info *info, int,
> diff --git a/revision.c b/revision.c
> index 40f59c441f7..e972addd8fc 100644
> --- a/revision.c
> +++ b/revision.c
> @@ -2955,6 +2955,7 @@ void release_revisions(struct rev_info *revs)
>   	clear_pathspec(&revs->prune_data);
>   	release_revisions_mailmap(revs->mailmap);
>   	free_grep_patterns(&revs->grep_filter);
> +	reflog_walk_info_release(revs->reflog_info);
>   }
>   
>   static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
> diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh
> index 69beb59f627..a16cc3d2983 100755
> --- a/t/t0100-previous.sh
> +++ b/t/t0100-previous.sh
> @@ -5,6 +5,7 @@ test_description='previous branch syntax @{-n}'
>   GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
>   export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   
>   test_expect_success 'branch -d @{-1}' '
> diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh
> index 132a1b885ac..9fb0b90f252 100755
> --- a/t/t1401-symbolic-ref.sh
> +++ b/t/t1401-symbolic-ref.sh
> @@ -1,6 +1,8 @@
>   #!/bin/sh
>   
>   test_description='basic symbolic-ref tests'
> +
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   
>   # If the tests munging HEAD fail, they can break detection of
> diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
> index 0bb319b944a..3770ceffafd 100755
> --- a/t/t1411-reflog-show.sh
> +++ b/t/t1411-reflog-show.sh
> @@ -4,6 +4,7 @@ test_description='Test reflog display routines'
>   GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
>   export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   
>   test_expect_success 'setup' '
> diff --git a/t/t1412-reflog-loop.sh b/t/t1412-reflog-loop.sh
> index 977603f7f1c..ff30874f940 100755
> --- a/t/t1412-reflog-loop.sh
> +++ b/t/t1412-reflog-loop.sh
> @@ -1,6 +1,8 @@
>   #!/bin/sh
>   
>   test_description='reflog walk shows repeated commits again'
> +
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   
>   test_expect_success 'setup commits' '
> diff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh
> index a3e6ea08088..3b531842dd4 100755
> --- a/t/t1415-worktree-refs.sh
> +++ b/t/t1415-worktree-refs.sh
> @@ -2,6 +2,7 @@
>   
>   test_description='per-worktree refs'
>   
> +TEST_PASSES_SANITIZE_LEAK=true
>   . ./test-lib.sh
>   
>   test_expect_success 'setup' '

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v4 13/27] revisions API users: use release_revisions() in builtin/log.c
  2022-04-02  9:22         ` Phillip Wood
  2022-04-03 14:07           ` Ævar Arnfjörð Bjarmason
@ 2022-04-03 21:49           ` Junio C Hamano
  1 sibling, 0 replies; 252+ messages in thread
From: Junio C Hamano @ 2022-04-03 21:49 UTC (permalink / raw)
  To: Phillip Wood
  Cc: Ævar Arnfjörð Bjarmason, git, Martin Ågren,
	Elijah Newren, Derrick Stolee, brian m . carlson

Phillip Wood <phillip.wood123@gmail.com> writes:

>>   +static int cmd_log_deinit(int ret, struct rev_info *rev)
>> +{
>> +	release_revisions(rev);
>> +	return ret;
>> +}
>
>
>>   /*
>>    * This gives a rough estimate for how many commits we
>>    * will print out in the list.
>> @@ -558,7 +564,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
>>   	cmd_log_init(argc, argv, prefix, &rev, &opt);
>>   	if (!rev.diffopt.output_format)
>>   		rev.diffopt.output_format = DIFF_FORMAT_RAW;
>> -	return cmd_log_walk(&rev);
>> +	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
>
> This is a rather unusual pattern, at first I wondered if there were
> going to be more added to the body of cmd_log_deinit() in later
> commits but there isn't so why not just call release_revisions() here
> to be consistent with the other release_revisions() call that are
> added in other patches?

It is being cute and clever by not requiring a temporary variable
ret, where you would normally say

	int ret = 0; /* assume success */

	... a lot of code ...
	ret = cmd_log_walk(&rev);
        release_revisions(&rev);
	return ret;

I agree that this looks confusing; if this pattern can become
majority locally in the file, I guess it would be OK---at that point
we can claim that it is the (new) usual pattern ;-).





^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v5 17/27] revisions API: have release_revisions() release "mailmap"
  2022-04-03 13:57             ` Ævar Arnfjörð Bjarmason
@ 2022-04-04  9:19               ` Phillip Wood
  0 siblings, 0 replies; 252+ messages in thread
From: Phillip Wood @ 2022-04-04  9:19 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren,
	Derrick Stolee, brian m . carlson

Hi Ævar

On 03/04/2022 14:57, Ævar Arnfjörð Bjarmason wrote:
> [...]
>>>    	av[++ac] = buf.buf;
>>>    	av[++ac] = NULL;
>>>    	setup_revisions(ac, av, &revs, NULL);
>>> -	revs.mailmap = &mailmap;
>>> +	revs.mailmap = xmalloc(sizeof(struct string_list));
>>> +	string_list_init_nodup(revs.mailmap);
>>
>> This is a common pattern in one of the previous patches, is it worth
>> adding helpers to allocate and initialize a struct string_list? Maybe
>> string_list_new_nodup() and string_list_new_dup().
> 
> Maybe, but generally in the git codebase things malloc and then init(),
> if we're going to add something like this *_new() that would be a change
> for a lot more APIs than just mailmap.
> 
> And if it's just for mailmap I don't see how the inconsistency with
> other code would be worth it.

It's for struct string_list not for mailmap and could be used for all 
the conversions in patch 3. In general the split between allocation and 
initialization is useful because it allows us to allocate structures on 
the stack where possible or embed them in other structures. However if 
there is a structure that is often allocated on the heap then I don't 
think there is anything wrong with having a combined 
allocate-and-initialize helper function which makes the code shorter and 
eliminates the possibility of passing the wrong size to malloc().

>>> diff --git a/revision.c b/revision.c
>>> index 553f7de8250..622f0faecc4 100644
>>> --- a/revision.c
>>> +++ b/revision.c
>>> @@ -2926,10 +2926,19 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
>>>    	return left;
>>>    }
>>>    +static void release_revisions_mailmap(struct string_list
>>> *mailmap)
>>> +{
>>> +	if (!mailmap)
>>> +		return;
>>> +	clear_mailmap(mailmap);
>>> +	free(mailmap);
>>> +}
>>
>> It's not a big issue but if there are no other users of this then it
>> could just go inside release_revisions, my impression is that this
>> series builds a collection of very small functions whose only caller
>> is release_revisions()
> 
> Yes, these are just trivial static helpers so that each line in
> release_revisions() corresponds to a member of the struct, without
> loops, indentation for "don't free this" etc.

Fair enough

> To the machine code it makes no difference at higher optimization
> levels.

Indeed, not that these functions are particularly performance sensitive 
in the first place.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v4 13/27] revisions API users: use release_revisions() in builtin/log.c
  2022-04-03 14:07           ` Ævar Arnfjörð Bjarmason
@ 2022-04-04  9:27             ` Phillip Wood
  0 siblings, 0 replies; 252+ messages in thread
From: Phillip Wood @ 2022-04-04  9:27 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren,
	Derrick Stolee, brian m . carlson

Hi Ævar

On 03/04/2022 15:07, Ævar Arnfjörð Bjarmason wrote:
> 
> On Sat, Apr 02 2022, Phillip Wood wrote:
> 
> [A comment on v4, but also applies to v5 I think]
> 
>> On 31/03/2022 02:11, Ævar Arnfjörð Bjarmason wrote:
>>> In preparation for having the "log" family of functions make wider use
>>> of release_revisions() let's have them call it just before
>>> exiting. This changes the "log", "whatchanged", "show",
>>> "format-patch", etc. commands, all of which live in this file.
>>> The release_revisions() API still only frees the "pending" member,
>>> but
>>> will learn to release more members of "struct rev_info" in subsequent
>>> commits.
>>> In the case of "format-patch" revert the addition of UNLEAK() in
>>> dee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16),
>>> which will cause several tests that previously passed under
>>> "TEST_PASSES_SANITIZE_LEAK=true" to start failing.
>>> In subsequent commits we'll now be able to use those tests to check
>>> whether that part of the API is really leaking memory, and will fix
>>> all of those memory leaks. Removing the UNLEAK() allows us to make
>>> incremental progress in that direction. See [1] for further details
>>> about this approach.
>>
>> This breaks "git bisect" but only when running the test suite to
>> detect leaks so I guess that's not too bad. An alternative would be to
>> manually remove the UNLEAK() when you're testing rather than
>> committing the change.
> 
> It doesn't, for this series each individual commit passes with

Oh I'd missed that, thanks for explaining

>      make test
>      GIT_TEST_PASSING_SANITIZE_LEAK=true make test SANITIZE=leak
> 
> And also in a stricter mode that I have locally (not in git yet):
> 
>      make test
>      GIT_TEST_PASSING_SANITIZE_LEAK=check make test SANITIZE=leak
> 
> Which ensures not only that the tests we marked as leak free pass, but
> that no other tests we *haven't* marked pass unexpectedly (requires prep
> changes before this series to mark the still-not-marked-but-should-be
> tests).
> 
> I think that should address/help explain things re your questions about
> some of the UNLEAK() back-and-forth.

Yes it does, the next patch makes sense to me now as well thanks

> [...]
>>> @@ -558,7 +564,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
>>>    	cmd_log_init(argc, argv, prefix, &rev, &opt);
>>>    	if (!rev.diffopt.output_format)
>>>    		rev.diffopt.output_format = DIFF_FORMAT_RAW;
>>> -	return cmd_log_walk(&rev);
>>> +	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
>>
>> This is a rather unusual pattern, at first I wondered if there were
>> going to be more added to the body of cmd_log_deinit() in later
>> commits but there isn't so why not just call release_revisions() here
>> to be consistent with the other release_revisions() call that are
>> added in other patches?
> 
> It's just a way to save every single call to this callsite a change on
> top like this:
> 	
> 	diff --git a/builtin/log.c b/builtin/log.c
> 	index 5dad70aa47e..ece03536bed 100644
> 	--- a/builtin/log.c
> 	+++ b/builtin/log.c
> 	@@ -684,8 +684,11 @@ int cmd_show(int argc, const char **argv, const char *prefix)
> 	 	opt.tweak = show_setup_revisions_tweak;
> 	 	cmd_log_init(argc, argv, prefix, &rev, &opt);
> 	
> 	-	if (!rev.no_walk)
> 	-		return cmd_log_deinit(cmd_log_walk(&rev), &rev);
> 	+	if (!rev.no_walk) {
> 	+		ret = cmd_log_walk(&rev);
> 	+		release_revisions(&rev);
> 	+		return ret;
> 	+	}
> 	
> 	 	count = rev.pending.nr;
> 	 	objects = rev.pending.objects;
> 
> Which, given that there's 6 of them nicely cuts down on the resulting
> verbosity.

If you want to adopt this pattern more widely then if there was a helper 
function in revisions.c it could be used in patches 11 and 14 as well as 
this one I think.

Best Wishes

Phillip

^ permalink raw reply	[flat|nested] 252+ messages in thread

* [PATCH v6 00/27] revision.[ch]: add and use release_revisions()
  2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
                           ` (27 preceding siblings ...)
  2022-04-03 13:07         ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Phillip Wood
@ 2022-04-13 20:01         ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
                             ` (26 more replies)
  28 siblings, 27 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

This series reduces the memory leaks spotted by our tests by at least
half, see v3[1] for a more general overview.

The only change in this v6 is a trivial adjustment of the freeing for
the "cmdline" to get rid of a boilerplate was-always-unnecessary NULL
check for the "cmdline", which could never be NULL in that
case. Thanks to Phillip Wood for spotting it.

1. https://lore.kernel.org/git/cover-v3-00.27-00000000000-20220325T171340Z-avarab@gmail.com/

Ævar Arnfjörð Bjarmason (27):
  t/helper/test-fast-rebase.c: don't leak "struct strbuf"
  blame: use "goto cleanup" for cleanup_scoreboard()
  string_list API users: use string_list_init_{no,}dup
  format-patch: don't leak "extra_headers" or "ref_message_ids"
  cocci: add and apply free_commit_list() rules
  revision.[ch]: provide and start using a release_revisions()
  revisions API users: add straightforward release_revisions()
  revision.[ch]: document and move code declared around "init"
  revisions API users: use release_revisions() needing REV_INFO_INIT
  stash: always have the owner of "stash_info" free it
  revisions API users: add "goto cleanup" for release_revisions()
  revisions API users: use release_revisions() in http-push.c
  revisions API users: use release_revisions() in builtin/log.c
  revisions API users: use release_revisions() with UNLEAK()
  revisions API users: use release_revisions() for "prune_data" users
  revisions API: have release_revisions() release "commits"
  revisions API: have release_revisions() release "mailmap"
  revisions API: have release_revisions() release "cmdline"
  revisions API: have release_revisions() release "filter"
  revisions API: have release_revisions() release "grep_filter"
  revisions API: have release_revisions() release "prune_data"
  revisions API: clear "boundary_commits" in release_revisions()
  revisions API: release "reflog_info" in release revisions()
  revisions API: call diff_free(&revs->pruning) in revisions_release()
  revisions API: have release_revisions() release "date_mode"
  revisions API: have release_revisions() release "topo_walk_info"
  revisions API: add a TODO for diff_free(&revs->diffopt)

 add-interactive.c                          |   3 +-
 bisect.c                                   |  20 ++--
 builtin/add.c                              |   3 +-
 builtin/am.c                               |   3 +
 builtin/bisect--helper.c                   |   2 +
 builtin/blame.c                            |   4 +-
 builtin/checkout.c                         |   3 +-
 builtin/commit.c                           |   6 +-
 builtin/describe.c                         |   2 +
 builtin/diff-files.c                       |   8 +-
 builtin/diff-index.c                       |   5 +-
 builtin/diff.c                             |   2 +-
 builtin/fast-export.c                      |   1 +
 builtin/log.c                              |  36 ++++---
 builtin/merge.c                            |   2 +
 builtin/pack-objects.c                     |   2 +
 builtin/prune.c                            |   1 +
 builtin/reflog.c                           |   1 +
 builtin/rev-list.c                         |  25 +++--
 builtin/shortlog.c                         |   8 +-
 builtin/stash.c                            | 115 +++++++++++----------
 builtin/submodule--helper.c                |  14 ++-
 bundle.c                                   |  12 ++-
 commit.c                                   |  19 ++--
 contrib/coccinelle/free.cocci              |  27 +++++
 diff-lib.c                                 |   8 +-
 fmt-merge-msg.c                            |   1 +
 http-push.c                                |   3 +-
 merge-ort.c                                |   1 +
 merge-recursive.c                          |   5 +-
 midx.c                                     |   1 +
 pack-bitmap-write.c                        |   1 +
 range-diff.c                               |   2 +-
 ref-filter.c                               |   1 +
 reflog-walk.c                              |  24 ++++-
 reflog-walk.h                              |   1 +
 remote.c                                   |   1 +
 revision.c                                 |  70 ++++++++++---
 revision.h                                 |  73 ++++++++-----
 sequencer.c                                |  26 +++--
 shallow.c                                  |   1 +
 submodule.c                                |  11 +-
 t/helper/test-fast-rebase.c                |  23 +++--
 t/helper/test-revision-walking.c           |   1 +
 t/lib-git-svn.sh                           |   4 +
 t/t0056-git-C.sh                           |   1 +
 t/t0062-revision-walking.sh                |   1 +
 t/t0100-previous.sh                        |   1 +
 t/t0101-at-syntax.sh                       |   2 +
 t/t1001-read-tree-m-2way.sh                |   1 +
 t/t1002-read-tree-m-u-2way.sh              |   1 +
 t/t1060-object-corruption.sh               |   2 +
 t/t1401-symbolic-ref.sh                    |   2 +
 t/t1411-reflog-show.sh                     |   1 +
 t/t1412-reflog-loop.sh                     |   2 +
 t/t1415-worktree-refs.sh                   |   1 +
 t/t2015-checkout-unborn.sh                 |   1 +
 t/t2200-add-update.sh                      |   1 +
 t/t3302-notes-index-expensive.sh           |   1 +
 t/t3303-notes-subtrees.sh                  |   1 +
 t/t3305-notes-fanout.sh                    |   1 +
 t/t3408-rebase-multi-line.sh               |   1 +
 t/t4021-format-patch-numbered.sh           |   1 +
 t/t4027-diff-submodule.sh                  |   1 +
 t/t4028-format-patch-mime-headers.sh       |   2 +
 t/t4036-format-patch-signer-mime.sh        |   1 +
 t/t4039-diff-assume-unchanged.sh           |   1 +
 t/t4055-diff-context.sh                    |   1 +
 t/t4066-diff-emit-delay.sh                 |   1 +
 t/t4122-apply-symlink-inside.sh            |   1 +
 t/t4126-apply-empty.sh                     |   1 -
 t/t4128-apply-root.sh                      |   1 +
 t/t4206-log-follow-harder-copies.sh        |   2 +
 t/t4207-log-decoration-colors.sh           |   1 +
 t/t4212-log-corrupt.sh                     |   1 +
 t/t5301-sliding-window.sh                  |   2 +
 t/t5313-pack-bounds-checks.sh              |   2 +
 t/t5316-pack-delta-depth.sh                |   2 +
 t/t5320-delta-islands.sh                   |   2 +
 t/t5322-pack-objects-sparse.sh             |   1 +
 t/t5506-remote-groups.sh                   |   1 +
 t/t5513-fetch-track.sh                     |   1 +
 t/t5515-fetch-merge-logic.sh               |   1 +
 t/t5518-fetch-exit-status.sh               |   1 +
 t/t5532-fetch-proxy.sh                     |   2 +
 t/t5600-clone-fail-cleanup.sh              |   1 +
 t/t5900-repo-selection.sh                  |   2 +
 t/t6002-rev-list-bisect.sh                 |   1 +
 t/t6003-rev-list-topo-order.sh             |   1 +
 t/t6005-rev-list-count.sh                  |   1 +
 t/t6018-rev-list-glob.sh                   |   1 +
 t/t6100-rev-list-in-order.sh               |   1 +
 t/t6101-rev-parse-parents.sh               |   1 +
 t/t6110-rev-list-sparse.sh                 |   1 +
 t/t6114-keep-packs.sh                      |   2 +
 t/t6131-pathspec-icase.sh                  |   2 +
 t/t7008-filter-branch-null-sha1.sh         |   1 +
 t/t7702-repack-cyclic-alternate.sh         |   2 +
 t/t9001-send-email.sh                      |   1 +
 t/t9100-git-svn-basic.sh                   |   1 +
 t/t9101-git-svn-props.sh                   |   2 +
 t/t9104-git-svn-follow-parent.sh           |   2 +
 t/t9106-git-svn-commit-diff-clobber.sh     |   2 +
 t/t9115-git-svn-dcommit-funky-renames.sh   |   1 +
 t/t9116-git-svn-log.sh                     |   1 +
 t/t9122-git-svn-author.sh                  |   2 +
 t/t9127-git-svn-partial-rebuild.sh         |   1 +
 t/t9129-git-svn-i18n-commitencoding.sh     |   1 +
 t/t9132-git-svn-broken-symlink.sh          |   1 +
 t/t9139-git-svn-non-utf8-commitencoding.sh |   1 +
 t/t9146-git-svn-empty-dirs.sh              |   2 +
 t/t9148-git-svn-propset.sh                 |   1 +
 t/t9160-git-svn-preserve-empty-dirs.sh     |   1 +
 t/t9162-git-svn-dcommit-interactive.sh     |   2 +
 t/t9164-git-svn-dcommit-concurrent.sh      |   2 +
 t/t9501-gitweb-standalone-http-status.sh   |   1 +
 wt-status.c                                |  15 +--
 117 files changed, 500 insertions(+), 190 deletions(-)

Range-diff against v5:
 1:  f6b7b27109c <  -:  ----------- list-objects-filter: remove CL_ARG__FILTER
 2:  692f9578614 <  -:  ----------- pack-objects: move revs out of get_object_list()
 3:  56ee616b898 <  -:  ----------- pack-objects: parse --filter directly into revs.filter
 4:  00f69fb967f <  -:  ----------- bundle: move capabilities to end of 'verify'
 5:  e33162a7a4f <  -:  ----------- bundle: output hash information in 'verify'
 6:  ba6177f59bb <  -:  ----------- pack-objects: lazily set up "struct rev_info", don't leak
 7:  e20f951a64c =  1:  1e904ad33b4 t/helper/test-fast-rebase.c: don't leak "struct strbuf"
 8:  de2e08a14e6 =  2:  c2dcdfab458 blame: use "goto cleanup" for cleanup_scoreboard()
 9:  364aa7d8990 =  3:  21fe41e4ad8 string_list API users: use string_list_init_{no,}dup
10:  5d4ad92d47b =  4:  f7fb26752b9 format-patch: don't leak "extra_headers" or "ref_message_ids"
11:  c38db28f920 =  5:  9eada36dacf cocci: add and apply free_commit_list() rules
12:  b99f96599bb =  6:  22de5dc3cc1 revision.[ch]: provide and start using a release_revisions()
13:  521f6967b64 =  7:  31c8eccf68e revisions API users: add straightforward release_revisions()
14:  003b507e0b7 =  8:  4a0e57acf17 revision.[ch]: document and move code declared around "init"
15:  240307bc3c6 =  9:  2bfa8640b6b revisions API users: use release_revisions() needing REV_INFO_INIT
16:  4c5404912e9 = 10:  f0c3d274fb5 stash: always have the owner of "stash_info" free it
17:  4210232e3a1 = 11:  e93791b6242 revisions API users: add "goto cleanup" for release_revisions()
18:  1918ca0a85d = 12:  2c744ccdbb1 revisions API users: use release_revisions() in http-push.c
19:  6a02b7aae4e = 13:  fdb3a63e0a9 revisions API users: use release_revisions() in builtin/log.c
20:  ddc7402b054 = 14:  31ce58da43d revisions API users: use release_revisions() with UNLEAK()
21:  03e9bd73d4b = 15:  71d59de2fe1 revisions API users: use release_revisions() for "prune_data" users
22:  285673e40bf = 16:  dcf63f64135 revisions API: have release_revisions() release "commits"
23:  ca659940ee5 = 17:  3e226c5b3d4 revisions API: have release_revisions() release "mailmap"
24:  7987c748ba4 ! 18:  ccae0f32ad5 revisions API: have release_revisions() release "cmdline"
    @@ revision.c: int setup_revisions(int argc, const char **argv, struct rev_info *re
     +{
     +	unsigned int i;
     +
    -+	if (!cmdline)
    -+		return;
     +	for (i = 0; i < cmdline->nr; i++)
     +		free((char *)cmdline->rev[i].name);
     +	free(cmdline->rev);
25:  e6e87ce3608 = 19:  2fc60559f1c revisions API: have release_revisions() release "filter"
26:  ebdcdc88d79 = 20:  e7e0e46bbb2 revisions API: have release_revisions() release "grep_filter"
27:  84c8714571a = 21:  54514a01096 revisions API: have release_revisions() release "prune_data"
28:  d96642d3502 = 22:  69745303734 revisions API: clear "boundary_commits" in release_revisions()
29:  0e9745d9ee1 = 23:  87ba9572b10 revisions API: release "reflog_info" in release revisions()
30:  c7f3e92a8d1 = 24:  79279864c42 revisions API: call diff_free(&revs->pruning) in revisions_release()
31:  d428d752462 = 25:  4aadeabaae7 revisions API: have release_revisions() release "date_mode"
32:  fbf233ec768 = 26:  f0cedadbb94 revisions API: have release_revisions() release "topo_walk_info"
33:  ec2c77ce869 = 27:  3be672a8150 revisions API: add a TODO for diff_free(&revs->diffopt)
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply	[flat|nested] 252+ messages in thread

* [PATCH v6 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf"
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 02/27] blame: use "goto cleanup" for cleanup_scoreboard() Ævar Arnfjörð Bjarmason
                             ` (25 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Fix a memory leak that's been with us since f9500261e0a (fast-rebase:
write conflict state to working tree, index, and HEAD, 2021-05-20)
changed this code to move these strbuf_release() into an if/else
block.

We'll also add to "reflog_msg" in the "else" arm of the "if" block
being modified here, and we'll append to "branch_msg" in both
cases. But after f9500261e0a only the "if" block would free these two
"struct strbuf".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 t/helper/test-fast-rebase.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c
index fc2d4609043..993b90eaedd 100644
--- a/t/helper/test-fast-rebase.c
+++ b/t/helper/test-fast-rebase.c
@@ -201,8 +201,6 @@ int cmd__fast_rebase(int argc, const char **argv)
 		}
 		if (create_symref("HEAD", branch_name.buf, reflog_msg.buf) < 0)
 			die(_("unable to update HEAD"));
-		strbuf_release(&reflog_msg);
-		strbuf_release(&branch_name);
 
 		prime_cache_tree(the_repository, the_repository->index,
 				 result.tree);
@@ -221,5 +219,8 @@ int cmd__fast_rebase(int argc, const char **argv)
 	if (write_locked_index(&the_index, &lock,
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("unable to write %s"), get_index_file());
+
+	strbuf_release(&reflog_msg);
+	strbuf_release(&branch_name);
 	return (result.clean == 0);
 }
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 02/27] blame: use "goto cleanup" for cleanup_scoreboard()
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 03/27] string_list API users: use string_list_init_{no,}dup Ævar Arnfjörð Bjarmason
                             ` (24 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Amend a freeing pattern added in 0906ac2b54b (blame: use changed-path
Bloom filters, 2020-04-16) to use a "goto cleanup", so that we can be
sure that we call cleanup_scoreboard().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/blame.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/builtin/blame.c b/builtin/blame.c
index 8d15b68afc9..885b381ab83 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1167,7 +1167,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 	if (!incremental)
 		setup_pager();
 	else
-		return 0;
+		goto cleanup;
 
 	blame_sort_final(&sb);
 
@@ -1201,6 +1201,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 		printf("num commits: %d\n", sb.num_commits);
 	}
 
+cleanup:
 	cleanup_scoreboard(&sb);
 	return 0;
 }
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 03/27] string_list API users: use string_list_init_{no,}dup
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 02/27] blame: use "goto cleanup" for cleanup_scoreboard() Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids" Ævar Arnfjörð Bjarmason
                             ` (23 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Follow-up on the introduction of string_list_init_nodup() and
string_list_init_dup() in the series merged in bd4232fac33 (Merge
branch 'ab/struct-init', 2021-07-16) and convert code that implicitly
relied on xcalloc() being equivalent to the initializer to use
xmalloc() and string_list_init_{no,}dup() instead.

In the case of get_unmerged() in merge-recursive.c we used the
combination of xcalloc() and assigning "1" to "strdup_strings" to get
what we'd get via string_list_init_dup(), let's use that instead.

Adjacent code in cmd_format_patch() will be changed in a subsequent
commit, since we're changing that let's change the other in-tree
patterns that do the same. Let's also convert a "x == NULL" to "!x"
per our CodingGuidelines, as we need to change the "if" line anyway.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/log.c      | 9 ++++++---
 builtin/shortlog.c | 6 ++++--
 merge-recursive.c  | 4 ++--
 3 files changed, 12 insertions(+), 7 deletions(-)

diff --git a/builtin/log.c b/builtin/log.c
index c211d66d1d0..634dc782cce 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -231,7 +231,8 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
 	}
 
 	if (mailmap) {
-		rev->mailmap = xcalloc(1, sizeof(struct string_list));
+		rev->mailmap = xmalloc(sizeof(struct string_list));
+		string_list_init_nodup(rev->mailmap);
 		read_mailmap(rev->mailmap);
 	}
 
@@ -2173,8 +2174,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		prepare_bases(&bases, base, list, nr);
 	}
 
-	if (in_reply_to || thread || cover_letter)
-		rev.ref_message_ids = xcalloc(1, sizeof(struct string_list));
+	if (in_reply_to || thread || cover_letter) {
+		rev.ref_message_ids = xmalloc(sizeof(*rev.ref_message_ids));
+		string_list_init_nodup(rev.ref_message_ids);
+	}
 	if (in_reply_to) {
 		const char *msgid = clean_message_id(in_reply_to);
 		string_list_append(rev.ref_message_ids, msgid);
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index 26c5c0cf935..fcde07c9367 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -81,8 +81,10 @@ static void insert_one_record(struct shortlog *log,
 		format_subject(&subject, oneline, " ");
 		buffer = strbuf_detach(&subject, NULL);
 
-		if (item->util == NULL)
-			item->util = xcalloc(1, sizeof(struct string_list));
+		if (!item->util) {
+			item->util = xmalloc(sizeof(struct string_list));
+			string_list_init_nodup(item->util);
+		}
 		string_list_append(item->util, buffer);
 	}
 }
diff --git a/merge-recursive.c b/merge-recursive.c
index 1ee6364e8b1..32bbba5fbb1 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -522,10 +522,10 @@ static struct stage_data *insert_stage_data(struct repository *r,
  */
 static struct string_list *get_unmerged(struct index_state *istate)
 {
-	struct string_list *unmerged = xcalloc(1, sizeof(struct string_list));
+	struct string_list *unmerged = xmalloc(sizeof(struct string_list));
 	int i;
 
-	unmerged->strdup_strings = 1;
+	string_list_init_dup(unmerged);
 
 	/* TODO: audit for interaction with sparse-index. */
 	ensure_full_index(istate);
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids"
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (2 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 03/27] string_list API users: use string_list_init_{no,}dup Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 05/27] cocci: add and apply free_commit_list() rules Ævar Arnfjörð Bjarmason
                             ` (22 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Fix two memory leaks in "struct rev_info" by freeing that memory in
cmd_format_patch(). These two are unusual special-cases in being in
the "struct rev_info", but not being "owned" by the code in
revision.c. I.e. they're members of the struct so that this code in
"builtin/log.c" can conveniently pass information code in
"log-tree.c".

See e.g. the make_cover_letter() caller of log_write_email_headers()
here in "builtin/log.c", and [1] for a demonstration of where the
"extra_headers" and "ref_message_ids" struct members are used.

See 20ff06805c6 (format-patch: resurrect extra headers from config,
2006-06-02) and d1566f7883f (git-format-patch: Make the second and
subsequent mails replies to the first, 2006-07-14) for the initial
introduction of "extra_headers" and "ref_message_ids".

We can count on repo_init_revisions() memset()-ing this data to 0
however, so we can count on it being either NULL or something we
allocated. In the case of "extra_headers" let's add a local "char *"
variable to hold it, to avoid the eventual cast from "const char *"
when we free() it.

1. https://lore.kernel.org/git/220401.868rsoogxf.gmgdl@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/log.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/builtin/log.c b/builtin/log.c
index 634dc782cce..6f9928fabfe 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -1747,6 +1747,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	struct commit *commit;
 	struct commit **list = NULL;
 	struct rev_info rev;
+	char *to_free = NULL;
 	struct setup_revision_opt s_r_opt;
 	int nr = 0, total, i;
 	int use_stdout = 0;
@@ -1947,7 +1948,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 		strbuf_addch(&buf, '\n');
 	}
 
-	rev.extra_headers = strbuf_detach(&buf, NULL);
+	rev.extra_headers = to_free = strbuf_detach(&buf, NULL);
 
 	if (from) {
 		if (split_ident_line(&rev.from_ident, from, strlen(from)))
@@ -2284,6 +2285,10 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	strbuf_release(&rdiff1);
 	strbuf_release(&rdiff2);
 	strbuf_release(&rdiff_title);
+	free(to_free);
+	if (rev.ref_message_ids)
+		string_list_clear(rev.ref_message_ids, 0);
+	free(rev.ref_message_ids);
 	UNLEAK(rev);
 	return 0;
 }
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 05/27] cocci: add and apply free_commit_list() rules
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (3 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids" Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 06/27] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
                             ` (21 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Add and apply coccinelle rules to remove "if (E)" before
"free_commit_list(E)", the function can accept NULL, and further
change cases where "E = NULL" followed to also be unconditionally.

The code changes in this commit were entirely made by the coccinelle
rule being added here, and applied with:

    make contrib/coccinelle/free.cocci.patch
    patch -p1 <contrib/coccinelle/free.cocci.patch

The only manual intervention here is that the the relevant code in
commit.c has been manually re-indented.

Suggested-by: Phillip Wood <phillip.wood123@gmail.com>
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/rev-list.c            |  6 ++----
 commit.c                      | 19 ++++++++-----------
 contrib/coccinelle/free.cocci | 27 +++++++++++++++++++++++++++
 revision.c                    | 17 ++++++-----------
 submodule.c                   |  6 ++----
 5 files changed, 45 insertions(+), 30 deletions(-)

diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 572da1472e5..07c0ad11d8d 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -213,10 +213,8 @@ static void show_commit(struct commit *commit, void *data)
 
 static void finish_commit(struct commit *commit)
 {
-	if (commit->parents) {
-		free_commit_list(commit->parents);
-		commit->parents = NULL;
-	}
+	free_commit_list(commit->parents);
+	commit->parents = NULL;
 	free_commit_buffer(the_repository->parsed_objects,
 			   commit);
 }
diff --git a/commit.c b/commit.c
index 59b6c3e4552..9628716bf2f 100644
--- a/commit.c
+++ b/commit.c
@@ -407,17 +407,14 @@ int parse_commit_buffer(struct repository *r, struct commit *item, const void *b
 
 	if (item->object.parsed)
 		return 0;
-
-	if (item->parents) {
-		/*
-		 * Presumably this is leftover from an earlier failed parse;
-		 * clear it out in preparation for us re-parsing (we'll hit the
-		 * same error, but that's good, since it lets our caller know
-		 * the result cannot be trusted.
-		 */
-		free_commit_list(item->parents);
-		item->parents = NULL;
-	}
+	/*
+	 * Presumably this is leftover from an earlier failed parse;
+	 * clear it out in preparation for us re-parsing (we'll hit the
+	 * same error, but that's good, since it lets our caller know
+	 * the result cannot be trusted.
+	 */
+	free_commit_list(item->parents);
+	item->parents = NULL;
 
 	tail += size;
 	if (tail <= bufptr + tree_entry_len + 1 || memcmp(bufptr, "tree ", 5) ||
diff --git a/contrib/coccinelle/free.cocci b/contrib/coccinelle/free.cocci
index 4490069df96..6fb9eb6e883 100644
--- a/contrib/coccinelle/free.cocci
+++ b/contrib/coccinelle/free.cocci
@@ -2,13 +2,21 @@
 expression E;
 @@
 - if (E)
+(
   free(E);
+|
+  free_commit_list(E);
+)
 
 @@
 expression E;
 @@
 - if (!E)
+(
   free(E);
+|
+  free_commit_list(E);
+)
 
 @@
 expression E;
@@ -16,3 +24,22 @@ expression E;
 - free(E);
 + FREE_AND_NULL(E);
 - E = NULL;
+
+@@
+expression E;
+@@
+- if (E)
+- {
+  free_commit_list(E);
+  E = NULL;
+- }
+
+@@
+expression E;
+statement S;
+@@
+- if (E) {
++ if (E)
+  S
+  free_commit_list(E);
+- }
diff --git a/revision.c b/revision.c
index 7d435f80480..4963ba7def8 100644
--- a/revision.c
+++ b/revision.c
@@ -1456,10 +1456,9 @@ static int limit_list(struct rev_info *revs)
 	if (revs->left_only || revs->right_only)
 		limit_left_right(newlist, revs);
 
-	if (bottom) {
+	if (bottom)
 		limit_to_ancestry(bottom, newlist);
-		free_commit_list(bottom);
-	}
+	free_commit_list(bottom);
 
 	/*
 	 * Check if any commits have become TREESAME by some of their parents
@@ -4080,10 +4079,8 @@ static void create_boundary_commit_list(struct rev_info *revs)
 	 * boundary commits anyway.  (This is what the code has always
 	 * done.)
 	 */
-	if (revs->commits) {
-		free_commit_list(revs->commits);
-		revs->commits = NULL;
-	}
+	free_commit_list(revs->commits);
+	revs->commits = NULL;
 
 	/*
 	 * Put all of the actual boundary commits from revs->boundary_commits
@@ -4220,10 +4217,8 @@ struct commit *get_revision(struct rev_info *revs)
 		graph_update(revs->graph, c);
 	if (!c) {
 		free_saved_parents(revs);
-		if (revs->previous_parents) {
-			free_commit_list(revs->previous_parents);
-			revs->previous_parents = NULL;
-		}
+		free_commit_list(revs->previous_parents);
+		revs->previous_parents = NULL;
 	}
 	return c;
 }
diff --git a/submodule.c b/submodule.c
index 86c8f0f89db..9b715b4a5cb 100644
--- a/submodule.c
+++ b/submodule.c
@@ -645,8 +645,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,
 	print_submodule_diff_summary(sub, &rev, o);
 
 out:
-	if (merge_bases)
-		free_commit_list(merge_bases);
+	free_commit_list(merge_bases);
 	clear_commit_marks(left, ~0);
 	clear_commit_marks(right, ~0);
 	if (sub) {
@@ -735,8 +734,7 @@ void show_submodule_inline_diff(struct diff_options *o, const char *path,
 
 done:
 	strbuf_release(&sb);
-	if (merge_bases)
-		free_commit_list(merge_bases);
+	free_commit_list(merge_bases);
 	if (left)
 		clear_commit_marks(left, ~0);
 	if (right)
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 06/27] revision.[ch]: provide and start using a release_revisions()
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (4 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 05/27] cocci: add and apply free_commit_list() rules Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 07/27] revisions API users: add straightforward release_revisions() Ævar Arnfjörð Bjarmason
                             ` (20 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

The users of the revision.[ch] API's "struct rev_info" are a major
source of memory leaks in the test suite under SANITIZE=leak, which in
turn adds a lot of noise when trying to mark up tests with
"TEST_PASSES_SANITIZE_LEAK=true".

The users of that API are largely one-shot, e.g. "git rev-list" or
"git log", or the "git checkout" and "git stash" being modified here

For these callers freeing the memory is arguably a waste of time, but
in many cases they've actually been trying to free the memory, and
just doing that in a buggy manner.

Let's provide a release_revisions() function for these users, and
start migrating them over per the plan outlined in [1]. Right now this
only handles the "pending" member of the struct, but more will be
added in subsequent commits.

Even though we only clear the "pending" member now, let's not leave a
trap in code like the pre-image of index_differs_from(), where we'd
start doing the wrong thing as soon as the release_revisions() learned
to clear its "diffopt". I.e. we need to call release_revisions() after
we've inspected any state in "struct rev_info".

This leaves in place e.g. clear_pathspec(&rev.prune_data) in
stash_working_tree() in builtin/stash.c, subsequent commits will teach
release_revisions() to free "prune_data" and other members that in
some cases are individually cleared by users of "struct rev_info" by
reaching into its members. Those subsequent commits will remove the
relevant calls to e.g. clear_pathspec().

We avoid amending code in index_differs_from() in diff-lib.c as well
as wt_status_collect_changes_index(), has_unstaged_changes() and
has_uncommitted_changes() in wt-status.c in a way that assumes that we
are already clearing the "diffopt" member. That will be handled in a
subsequent commit.

1. https://lore.kernel.org/git/87a6k8daeu.fsf@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/checkout.c | 2 +-
 builtin/stash.c    | 5 ++---
 diff-lib.c         | 2 +-
 range-diff.c       | 2 +-
 revision.c         | 5 +++++
 revision.h         | 6 ++++++
 wt-status.c        | 5 +++--
 7 files changed, 19 insertions(+), 8 deletions(-)

diff --git a/builtin/checkout.c b/builtin/checkout.c
index 797681481d1..4d9e0bd3ac1 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -629,7 +629,7 @@ static void show_local_changes(struct object *head,
 	diff_setup_done(&rev.diffopt);
 	add_pending_object(&rev, head, NULL);
 	run_diff_index(&rev, 0);
-	object_array_clear(&rev.pending);
+	release_revisions(&rev);
 }
 
 static void describe_detached_head(const char *msg, struct commit *commit)
diff --git a/builtin/stash.c b/builtin/stash.c
index 0c7b6a95882..74fa810cf8c 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1047,7 +1047,6 @@ static int check_changes_tracked_files(const struct pathspec *ps)
 		goto done;
 	}
 
-	object_array_clear(&rev.pending);
 	result = run_diff_files(&rev, 0);
 	if (diff_result_code(&rev.diffopt, result)) {
 		ret = 1;
@@ -1056,6 +1055,7 @@ static int check_changes_tracked_files(const struct pathspec *ps)
 
 done:
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 	return ret;
 }
 
@@ -1266,9 +1266,8 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
 
 done:
 	discard_index(&istate);
-	UNLEAK(rev);
-	object_array_clear(&rev.pending);
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 	strbuf_release(&diff_output);
 	remove_path(stash_index_path.buf);
 	return ret;
diff --git a/diff-lib.c b/diff-lib.c
index ca085a03efc..d6800274bd5 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -662,7 +662,7 @@ int index_differs_from(struct repository *r,
 		diff_flags_or(&rev.diffopt.flags, flags);
 	rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
 	run_diff_index(&rev, 1);
-	object_array_clear(&rev.pending);
+	release_revisions(&rev);
 	return (rev.diffopt.flags.has_changes != 0);
 }
 
diff --git a/range-diff.c b/range-diff.c
index b72eb9fdbee..39cc010c628 100644
--- a/range-diff.c
+++ b/range-diff.c
@@ -596,6 +596,6 @@ int is_range_diff_range(const char *arg)
 	}
 
 	free(copy);
-	object_array_clear(&revs.pending);
+	release_revisions(&revs);
 	return negative > 0 && positive > 0;
 }
diff --git a/revision.c b/revision.c
index 4963ba7def8..5dd4b2e910c 100644
--- a/revision.c
+++ b/revision.c
@@ -2922,6 +2922,11 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 	return left;
 }
 
+void release_revisions(struct rev_info *revs)
+{
+	object_array_clear(&revs->pending);
+}
+
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
 {
 	struct commit_list *l = xcalloc(1, sizeof(*l));
diff --git a/revision.h b/revision.h
index 5bc59c7bfe1..61c780fc4cd 100644
--- a/revision.h
+++ b/revision.h
@@ -377,6 +377,12 @@ void repo_init_revisions(struct repository *r,
 int setup_revisions(int argc, const char **argv, struct rev_info *revs,
 		    struct setup_revision_opt *);
 
+/**
+ * Free data allocated in a "struct rev_info" after it's been
+ * initialized with repo_init_revisions().
+ */
+void release_revisions(struct rev_info *revs);
+
 void parse_revision_opt(struct rev_info *revs, struct parse_opt_ctx_t *ctx,
 			const struct option *options,
 			const char * const usagestr[]);
diff --git a/wt-status.c b/wt-status.c
index d33f9272b72..922cf787f95 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -662,7 +662,7 @@ static void wt_status_collect_changes_index(struct wt_status *s)
 
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_index(&rev, 1);
-	object_array_clear(&rev.pending);
+	release_revisions(&rev);
 	clear_pathspec(&rev.prune_data);
 }
 
@@ -2545,6 +2545,7 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)
 	rev_info.diffopt.flags.quick = 1;
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_files(&rev_info, 0);
+	release_revisions(&rev_info);
 	return diff_result_code(&rev_info.diffopt, result);
 }
 
@@ -2577,7 +2578,7 @@ int has_uncommitted_changes(struct repository *r,
 
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_index(&rev_info, 1);
-	object_array_clear(&rev_info.pending);
+	release_revisions(&rev_info);
 	return diff_result_code(&rev_info.diffopt, result);
 }
 
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 07/27] revisions API users: add straightforward release_revisions()
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (5 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 06/27] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 08/27] revision.[ch]: document and move code declared around "init" Ævar Arnfjörð Bjarmason
                             ` (19 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Add a release_revisions() to various users of "struct rev_list" in
those straightforward cases where we only need to add the
release_revisions() call to the end of a block, and don't need to
e.g. refactor anything to use a "goto cleanup" pattern.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 add-interactive.c                | 1 +
 bisect.c                         | 2 ++
 builtin/add.c                    | 1 +
 builtin/am.c                     | 3 +++
 builtin/bisect--helper.c         | 2 ++
 builtin/blame.c                  | 1 +
 builtin/checkout.c               | 1 +
 builtin/commit.c                 | 1 +
 builtin/describe.c               | 2 ++
 builtin/fast-export.c            | 1 +
 builtin/merge.c                  | 2 ++
 builtin/pack-objects.c           | 2 ++
 builtin/prune.c                  | 1 +
 builtin/reflog.c                 | 1 +
 builtin/shortlog.c               | 2 ++
 builtin/submodule--helper.c      | 1 +
 fmt-merge-msg.c                  | 1 +
 merge-ort.c                      | 1 +
 merge-recursive.c                | 1 +
 midx.c                           | 1 +
 pack-bitmap-write.c              | 1 +
 ref-filter.c                     | 1 +
 remote.c                         | 1 +
 sequencer.c                      | 3 +++
 shallow.c                        | 1 +
 submodule.c                      | 2 ++
 t/helper/test-revision-walking.c | 1 +
 wt-status.c                      | 1 +
 28 files changed, 39 insertions(+)

diff --git a/add-interactive.c b/add-interactive.c
index 72472103017..54cdfc82017 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -570,6 +570,7 @@ static int get_modified_files(struct repository *r,
 
 		if (ps)
 			clear_pathspec(&rev.prune_data);
+		release_revisions(&rev);
 	}
 	hashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);
 	if (unmerged_count)
diff --git a/bisect.c b/bisect.c
index 9e6a2b7f201..cc6b8b6230d 100644
--- a/bisect.c
+++ b/bisect.c
@@ -884,6 +884,7 @@ static int check_ancestors(struct repository *r, int rev_nr,
 	/* Clean up objects used, as they will be reused. */
 	clear_commit_marks_many(rev_nr, rev, ALL_REV_FLAGS);
 
+	release_revisions(&revs);
 	return res;
 }
 
@@ -964,6 +965,7 @@ static void show_diff_tree(struct repository *r,
 
 	setup_revisions(ARRAY_SIZE(argv) - 1, argv, &opt, NULL);
 	log_tree_commit(&opt, commit);
+	release_revisions(&opt);
 }
 
 /*
diff --git a/builtin/add.c b/builtin/add.c
index 3ffb86a4338..f507d2191cd 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -340,6 +340,7 @@ static int edit_patch(int argc, const char **argv, const char *prefix)
 
 	unlink(file);
 	free(file);
+	release_revisions(&rev);
 	return 0;
 }
 
diff --git a/builtin/am.c b/builtin/am.c
index 0f4111bafa0..93bec62afa9 100644
--- a/builtin/am.c
+++ b/builtin/am.c
@@ -1397,6 +1397,7 @@ static void write_commit_patch(const struct am_state *state, struct commit *comm
 	add_pending_object(&rev_info, &commit->object, "");
 	diff_setup_done(&rev_info.diffopt);
 	log_tree_commit(&rev_info, commit);
+	release_revisions(&rev_info);
 }
 
 /**
@@ -1429,6 +1430,7 @@ static void write_index_patch(const struct am_state *state)
 	add_pending_object(&rev_info, &tree->object, "");
 	diff_setup_done(&rev_info.diffopt);
 	run_diff_index(&rev_info, 1);
+	release_revisions(&rev_info);
 }
 
 /**
@@ -1582,6 +1584,7 @@ static int fall_back_threeway(const struct am_state *state, const char *index_pa
 		add_pending_oid(&rev_info, "HEAD", &our_tree, 0);
 		diff_setup_done(&rev_info.diffopt);
 		run_diff_index(&rev_info, 1);
+		release_revisions(&rev_info);
 	}
 
 	if (run_apply(state, index_path))
diff --git a/builtin/bisect--helper.c b/builtin/bisect--helper.c
index 8b2b259ff0d..e4d7b6779ae 100644
--- a/builtin/bisect--helper.c
+++ b/builtin/bisect--helper.c
@@ -555,6 +555,7 @@ static int bisect_skipped_commits(struct bisect_terms *terms)
 	reset_revision_walk();
 
 	strbuf_release(&commit_name);
+	release_revisions(&revs);
 	fclose(fp);
 	return 0;
 }
@@ -1041,6 +1042,7 @@ static enum bisect_error bisect_skip(struct bisect_terms *terms, const char **ar
 						oid_to_hex(&commit->object.oid));
 
 			reset_revision_walk();
+			release_revisions(&revs);
 		} else {
 			strvec_push(&argv_state, argv[i]);
 		}
diff --git a/builtin/blame.c b/builtin/blame.c
index 885b381ab83..24bac822c56 100644
--- a/builtin/blame.c
+++ b/builtin/blame.c
@@ -1203,5 +1203,6 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 
 cleanup:
 	cleanup_scoreboard(&sb);
+	release_revisions(&revs);
 	return 0;
 }
diff --git a/builtin/checkout.c b/builtin/checkout.c
index 4d9e0bd3ac1..7ad4a7113c9 100644
--- a/builtin/checkout.c
+++ b/builtin/checkout.c
@@ -1082,6 +1082,7 @@ static void orphaned_commit_warning(struct commit *old_commit, struct commit *ne
 
 	/* Clean up objects used, as they will be reused. */
 	repo_clear_commit_marks(the_repository, ALL_REV_FLAGS);
+	release_revisions(&revs);
 }
 
 static int switch_branches(const struct checkout_opts *opts,
diff --git a/builtin/commit.c b/builtin/commit.c
index 009a1de0a3d..c7eda9bbb72 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1123,6 +1123,7 @@ static const char *find_author_by_nickname(const char *name)
 		strbuf_release(&buf);
 		format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
 		clear_mailmap(&mailmap);
+		release_revisions(&revs);
 		return strbuf_detach(&buf, NULL);
 	}
 	die(_("--author '%s' is not 'Name <email>' and matches no existing author"), name);
diff --git a/builtin/describe.c b/builtin/describe.c
index 42159cd26bd..a76f1a1a7a7 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -517,6 +517,7 @@ static void describe_blob(struct object_id oid, struct strbuf *dst)
 
 	traverse_commit_list(&revs, process_commit, process_object, &pcd);
 	reset_revision_walk();
+	release_revisions(&revs);
 }
 
 static void describe(const char *arg, int last_one)
@@ -667,6 +668,7 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
 				suffix = NULL;
 			else
 				suffix = dirty;
+			release_revisions(&revs);
 		}
 		describe("HEAD", 1);
 	} else if (dirty) {
diff --git a/builtin/fast-export.c b/builtin/fast-export.c
index a7d72697fba..f34ae451ee3 100644
--- a/builtin/fast-export.c
+++ b/builtin/fast-export.c
@@ -1275,6 +1275,7 @@ int cmd_fast_export(int argc, const char **argv, const char *prefix)
 		printf("done\n");
 
 	refspec_clear(&refspecs);
+	release_revisions(&revs);
 
 	return 0;
 }
diff --git a/builtin/merge.c b/builtin/merge.c
index f178f5a3ee1..d9784d4891c 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -443,6 +443,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
 	}
 	write_file_buf(git_path_squash_msg(the_repository), out.buf, out.len);
 	strbuf_release(&out);
+	release_revisions(&rev);
 }
 
 static void finish(struct commit *head_commit,
@@ -998,6 +999,7 @@ static int evaluate_result(void)
 	 */
 	cnt += count_unmerged_entries();
 
+	release_revisions(&rev);
 	return cnt;
 }
 
diff --git a/builtin/pack-objects.c b/builtin/pack-objects.c
index 014dcd4bc98..180fff03569 100644
--- a/builtin/pack-objects.c
+++ b/builtin/pack-objects.c
@@ -4177,11 +4177,13 @@ int cmd_pack_objects(int argc, const char **argv, const char *prefix)
 		read_object_list_from_stdin();
 	} else if (pfd.have_revs) {
 		get_object_list(&pfd.revs, rp.nr, rp.v);
+		release_revisions(&pfd.revs);
 	} else {
 		struct rev_info revs;
 
 		repo_init_revisions(the_repository, &revs, NULL);
 		get_object_list(&revs, rp.nr, rp.v);
+		release_revisions(&revs);
 	}
 	cleanup_preferred_base();
 	if (include_tag && nr_result)
diff --git a/builtin/prune.c b/builtin/prune.c
index c2bcdc07db4..df376b2ed1e 100644
--- a/builtin/prune.c
+++ b/builtin/prune.c
@@ -196,5 +196,6 @@ int cmd_prune(int argc, const char **argv, const char *prefix)
 		prune_shallow(show_only ? PRUNE_SHOW_ONLY : 0);
 	}
 
+	release_revisions(&revs);
 	return 0;
 }
diff --git a/builtin/reflog.c b/builtin/reflog.c
index c943c2aabe6..4dd297dce86 100644
--- a/builtin/reflog.c
+++ b/builtin/reflog.c
@@ -293,6 +293,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
 		if (verbose)
 			printf(_("Marking reachable objects..."));
 		mark_reachable_objects(&revs, 0, 0, NULL);
+		release_revisions(&revs);
 		if (verbose)
 			putchar('\n');
 	}
diff --git a/builtin/shortlog.c b/builtin/shortlog.c
index fcde07c9367..35825f075e3 100644
--- a/builtin/shortlog.c
+++ b/builtin/shortlog.c
@@ -422,6 +422,8 @@ int cmd_shortlog(int argc, const char **argv, const char *prefix)
 	else
 		get_from_rev(&rev, &log);
 
+	release_revisions(&rev);
+
 	shortlog_output(&log);
 	if (log.file != stdout)
 		fclose(log.file);
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 2c87ef9364f..5d6b43fe3c0 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1269,6 +1269,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 		run_diff_files(&rev, 0);
 	prepare_submodule_summary(info, &list);
 	strvec_clear(&diff_args);
+	release_revisions(&rev);
 	return 0;
 }
 
diff --git a/fmt-merge-msg.c b/fmt-merge-msg.c
index baca57d5b64..f48f44f9cd1 100644
--- a/fmt-merge-msg.c
+++ b/fmt-merge-msg.c
@@ -699,6 +699,7 @@ int fmt_merge_msg(struct strbuf *in, struct strbuf *out,
 			shortlog(origins.items[i].string,
 				 origins.items[i].util,
 				 head, &rev, opts, out);
+		release_revisions(&rev);
 	}
 
 	strbuf_complete_line(out);
diff --git a/merge-ort.c b/merge-ort.c
index 8545354dafd..4c0d4eba19b 100644
--- a/merge-ort.c
+++ b/merge-ort.c
@@ -1594,6 +1594,7 @@ static int find_first_merges(struct repository *repo,
 	}
 
 	object_array_clear(&merges);
+	release_revisions(&revs);
 	return result->nr;
 }
 
diff --git a/merge-recursive.c b/merge-recursive.c
index 32bbba5fbb1..acd13b2b069 100644
--- a/merge-recursive.c
+++ b/merge-recursive.c
@@ -1160,6 +1160,7 @@ static int find_first_merges(struct repository *repo,
 	}
 
 	object_array_clear(&merges);
+	release_revisions(&revs);
 	return result->nr;
 }
 
diff --git a/midx.c b/midx.c
index 107365d2114..d5724df7d60 100644
--- a/midx.c
+++ b/midx.c
@@ -1061,6 +1061,7 @@ static struct commit **find_commits_for_midx_bitmap(uint32_t *indexed_commits_nr
 	if (indexed_commits_nr_p)
 		*indexed_commits_nr_p = cb.commits_nr;
 
+	release_revisions(&revs);
 	return cb.commits;
 }
 
diff --git a/pack-bitmap-write.c b/pack-bitmap-write.c
index cf681547f2e..c43375bd344 100644
--- a/pack-bitmap-write.c
+++ b/pack-bitmap-write.c
@@ -326,6 +326,7 @@ static void bitmap_builder_init(struct bitmap_builder *bb,
 	trace2_data_intmax("pack-bitmap-write", the_repository,
 			   "num_maximal_commits", num_maximal);
 
+	release_revisions(&revs);
 	free_commit_list(reusable);
 }
 
diff --git a/ref-filter.c b/ref-filter.c
index 7838bd22b8d..a91688bbf17 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -2392,6 +2392,7 @@ static void reach_filter(struct ref_array *array,
 		clear_commit_marks(merge_commit, ALL_REV_FLAGS);
 	}
 
+	release_revisions(&revs);
 	free(to_clear);
 }
 
diff --git a/remote.c b/remote.c
index 42a4e7106e1..fa3152a5d52 100644
--- a/remote.c
+++ b/remote.c
@@ -2172,6 +2172,7 @@ static int stat_branch_pair(const char *branch_name, const char *base,
 	clear_commit_marks(theirs, ALL_REV_FLAGS);
 
 	strvec_clear(&argv);
+	release_revisions(&revs);
 	return 1;
 }
 
diff --git a/sequencer.c b/sequencer.c
index a1bb39383db..f9d7acd1065 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -1347,6 +1347,7 @@ void print_commit_summary(struct repository *r,
 		log_tree_commit(&rev, commit);
 	}
 
+	release_revisions(&rev);
 	strbuf_release(&format);
 }
 
@@ -3415,6 +3416,7 @@ static int make_patch(struct repository *r,
 		unuse_commit_buffer(commit, commit_buffer);
 	}
 	strbuf_release(&buf);
+	release_revisions(&log_tree_opt);
 
 	return res;
 }
@@ -4525,6 +4527,7 @@ static int pick_commits(struct repository *r,
 					      &log_tree_opt.diffopt);
 				log_tree_diff_flush(&log_tree_opt);
 			}
+			release_revisions(&log_tree_opt);
 		}
 		flush_rewritten_pending();
 		if (!stat(rebase_path_rewritten_list(), &st) &&
diff --git a/shallow.c b/shallow.c
index e158be58b05..797a593633f 100644
--- a/shallow.c
+++ b/shallow.c
@@ -262,6 +262,7 @@ struct commit_list *get_shallow_commits_by_rev_list(int ac, const char **av,
 		if ((o->flags & both_flags) == both_flags)
 			o->flags &= ~not_shallow_flag;
 	}
+	release_revisions(&revs);
 	return result;
 }
 
diff --git a/submodule.c b/submodule.c
index 9b715b4a5cb..7b5d7753930 100644
--- a/submodule.c
+++ b/submodule.c
@@ -923,9 +923,11 @@ static void collect_changed_submodules(struct repository *r,
 		diff_rev.diffopt.format_callback_data = &data;
 		diff_rev.dense_combined_merges = 1;
 		diff_tree_combined_merge(commit, &diff_rev);
+		release_revisions(&diff_rev);
 	}
 
 	reset_revision_walk();
+	release_revisions(&rev);
 }
 
 static void free_submodules_data(struct string_list *submodules)
diff --git a/t/helper/test-revision-walking.c b/t/helper/test-revision-walking.c
index 625b2dbf822..4a45d5bac2a 100644
--- a/t/helper/test-revision-walking.c
+++ b/t/helper/test-revision-walking.c
@@ -43,6 +43,7 @@ static int run_revision_walk(void)
 	}
 
 	reset_revision_walk();
+	release_revisions(&rev);
 	return got_revision;
 }
 
diff --git a/wt-status.c b/wt-status.c
index 922cf787f95..f9100621375 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -1152,6 +1152,7 @@ static void wt_longstatus_print_verbose(struct wt_status *s)
 		rev.diffopt.b_prefix = "w/";
 		run_diff_files(&rev, 0);
 	}
+	release_revisions(&rev);
 }
 
 static void wt_longstatus_print_tracking(struct wt_status *s)
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 08/27] revision.[ch]: document and move code declared around "init"
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (6 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 07/27] revisions API users: add straightforward release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 09/27] revisions API users: use release_revisions() needing REV_INFO_INIT Ævar Arnfjörð Bjarmason
                             ` (18 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

A subsequent commit will add "REV_INFO_INIT" macro adjacent to
repo_init_revisions(), unfortunately between the "struct rev_info"
itself and that function we've added various miscellaneous code
between the two over the years.

Let's move that code either lower in revision.h, giving it API docs
while we're at it, or in cases where it wasn't public API at all move
it into revision.c No lines of code are changed here, only moved
around. The only changes are the addition of new API comments.

The "tree_difference" variable could also be declared like this, which
I think would be a lot clearer, but let's leave that for now to keep
this a move-only change:

	static enum {
		REV_TREE_SAME,
		REV_TREE_NEW, /* Only new files */
		REV_TREE_OLD, /* Only files removed */
		REV_TREE_DIFFERENT, /* Mixed changes */
	} tree_difference = REV_TREE_SAME;

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c |  4 ++++
 revision.h | 50 ++++++++++++++++++++++++--------------------------
 2 files changed, 28 insertions(+), 26 deletions(-)

diff --git a/revision.c b/revision.c
index 5dd4b2e910c..472fff1e0a7 100644
--- a/revision.c
+++ b/revision.c
@@ -606,6 +606,10 @@ static struct commit *one_relevant_parent(const struct rev_info *revs,
  *
  *   2. We saw anything except REV_TREE_NEW.
  */
+#define REV_TREE_SAME		0
+#define REV_TREE_NEW		1	/* Only new files */
+#define REV_TREE_OLD		2	/* Only files removed */
+#define REV_TREE_DIFFERENT	3	/* Mixed changes */
 static int tree_difference = REV_TREE_SAME;
 
 static void file_add_remove(struct diff_options *options,
diff --git a/revision.h b/revision.h
index 61c780fc4cd..b9070e43428 100644
--- a/revision.h
+++ b/revision.h
@@ -329,32 +329,6 @@ struct rev_info {
 	struct tmp_objdir *remerge_objdir;
 };
 
-int ref_excluded(struct string_list *, const char *path);
-void clear_ref_exclusion(struct string_list **);
-void add_ref_exclusion(struct string_list **, const char *exclude);
-
-
-#define REV_TREE_SAME		0
-#define REV_TREE_NEW		1	/* Only new files */
-#define REV_TREE_OLD		2	/* Only files removed */
-#define REV_TREE_DIFFERENT	3	/* Mixed changes */
-
-/* revision.c */
-typedef void (*show_early_output_fn_t)(struct rev_info *, struct commit_list *);
-extern volatile show_early_output_fn_t show_early_output;
-
-struct setup_revision_opt {
-	const char *def;
-	void (*tweak)(struct rev_info *, struct setup_revision_opt *);
-	unsigned int	assume_dashdash:1,
-			allow_exclude_promisor_objects:1;
-	unsigned revarg_opt;
-};
-
-#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
-#define init_revisions(revs, prefix) repo_init_revisions(the_repository, revs, prefix)
-#endif
-
 /**
  * Initialize a rev_info structure with default values. The third parameter may
  * be NULL or can be prefix path, and then the `.prefix` variable will be set
@@ -366,6 +340,9 @@ struct setup_revision_opt {
 void repo_init_revisions(struct repository *r,
 			 struct rev_info *revs,
 			 const char *prefix);
+#ifndef NO_THE_REPOSITORY_COMPATIBILITY_MACROS
+#define init_revisions(revs, prefix) repo_init_revisions(the_repository, revs, prefix)
+#endif
 
 /**
  * Parse revision information, filling in the `rev_info` structure, and
@@ -374,6 +351,13 @@ void repo_init_revisions(struct repository *r,
  * head of the argument list. The last parameter is used in case no
  * parameter given by the first two arguments.
  */
+struct setup_revision_opt {
+	const char *def;
+	void (*tweak)(struct rev_info *, struct setup_revision_opt *);
+	unsigned int	assume_dashdash:1,
+			allow_exclude_promisor_objects:1;
+	unsigned revarg_opt;
+};
 int setup_revisions(int argc, const char **argv, struct rev_info *revs,
 		    struct setup_revision_opt *);
 
@@ -423,6 +407,14 @@ void mark_trees_uninteresting_sparse(struct repository *r, struct oidset *trees)
 
 void show_object_with_name(FILE *, struct object *, const char *);
 
+/**
+ * Helpers to check if a "struct string_list" item matches with
+ * wildmatch().
+ */
+int ref_excluded(struct string_list *, const char *path);
+void clear_ref_exclusion(struct string_list **);
+void add_ref_exclusion(struct string_list **, const char *exclude);
+
 /**
  * This function can be used if you want to add commit objects as revision
  * information. You can use the `UNINTERESTING` object flag to indicate if
@@ -478,4 +470,10 @@ int rewrite_parents(struct rev_info *revs,
  */
 struct commit_list *get_saved_parents(struct rev_info *revs, const struct commit *commit);
 
+/**
+ * Global for the (undocumented) "--early-output" flag for "git log".
+ */
+typedef void (*show_early_output_fn_t)(struct rev_info *, struct commit_list *);
+extern volatile show_early_output_fn_t show_early_output;
+
 #endif
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 09/27] revisions API users: use release_revisions() needing REV_INFO_INIT
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (7 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 08/27] revision.[ch]: document and move code declared around "init" Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 10/27] stash: always have the owner of "stash_info" free it Ævar Arnfjörð Bjarmason
                             ` (17 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Use release_revisions() to various users of "struct rev_list" which
need to have their "struct rev_info" zero-initialized before we can
start using it.

For the bundle.c code see the early exit case added in
3bbbe467f29 (bundle verify: error out if called without an object
database, 2019-05-27).

For the relevant bisect.c code see 45b6370812c (bisect: libify
`check_good_are_ancestors_of_bad` and its dependents, 2020-02-17).

For the submodule.c code see the "goto" on "(!left || !right || !sub)"
added in 8e6df65015f (submodule: refactor show_submodule_summary with
helper function, 2016-08-31).

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 bisect.c                    | 18 ++++++++++++------
 builtin/submodule--helper.c |  3 ++-
 bundle.c                    | 12 ++++++++----
 revision.h                  | 21 ++++++++++++++++++++-
 submodule.c                 |  3 ++-
 5 files changed, 44 insertions(+), 13 deletions(-)

diff --git a/bisect.c b/bisect.c
index cc6b8b6230d..b63669cc9d7 100644
--- a/bisect.c
+++ b/bisect.c
@@ -1010,7 +1010,7 @@ void read_bisect_terms(const char **read_bad, const char **read_good)
  */
 enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 {
-	struct rev_info revs;
+	struct rev_info revs = REV_INFO_INIT;
 	struct commit_list *tried;
 	int reaches = 0, all = 0, nr, steps;
 	enum bisect_error res = BISECT_OK;
@@ -1035,7 +1035,7 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 
 	res = check_good_are_ancestors_of_bad(r, prefix, no_checkout);
 	if (res)
-		return res;
+		goto cleanup;
 
 	bisect_rev_setup(r, &revs, prefix, "%s", "^%s", 1);
 
@@ -1060,14 +1060,16 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 		       term_good,
 		       term_bad);
 
-		return BISECT_FAILED;
+		res = BISECT_FAILED;
+		goto cleanup;
 	}
 
 	if (!all) {
 		fprintf(stderr, _("No testable commit found.\n"
 			"Maybe you started with bad path arguments?\n"));
 
-		return BISECT_NO_TESTABLE_COMMIT;
+		res = BISECT_NO_TESTABLE_COMMIT;
+		goto cleanup;
 	}
 
 	bisect_rev = &revs.commits->item->object.oid;
@@ -1087,7 +1089,8 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 		 * for negative return values for early returns up
 		 * until the cmd_bisect__helper() caller.
 		 */
-		return BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;
+		res = BISECT_INTERNAL_SUCCESS_1ST_BAD_FOUND;
+		goto cleanup;
 	}
 
 	nr = all - reaches - 1;
@@ -1106,7 +1109,10 @@ enum bisect_error bisect_next_all(struct repository *r, const char *prefix)
 	/* Clean up objects used, as they will be reused. */
 	repo_clear_commit_marks(r, ALL_REV_FLAGS);
 
-	return bisect_checkout(bisect_rev, no_checkout);
+	res = bisect_checkout(bisect_rev, no_checkout);
+cleanup:
+	release_revisions(&revs);
+	return res;
 }
 
 static inline int log2i(int n)
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 5d6b43fe3c0..9a65985b418 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -766,7 +766,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
 {
 	char *displaypath;
 	struct strvec diff_files_args = STRVEC_INIT;
-	struct rev_info rev;
+	struct rev_info rev = REV_INFO_INIT;
 	int diff_files_result;
 	struct strbuf buf = STRBUF_INIT;
 	const char *git_dir;
@@ -853,6 +853,7 @@ static void status_submodule(const char *path, const struct object_id *ce_oid,
 cleanup:
 	strvec_clear(&diff_files_args);
 	free(displaypath);
+	release_revisions(&rev);
 }
 
 static void status_submodule_cb(const struct cache_entry *list_item,
diff --git a/bundle.c b/bundle.c
index d50cfb5aa7e..6a870a6edb7 100644
--- a/bundle.c
+++ b/bundle.c
@@ -196,14 +196,16 @@ int verify_bundle(struct repository *r,
 	 * to be verbose about the errors
 	 */
 	struct string_list *p = &header->prerequisites;
-	struct rev_info revs;
+	struct rev_info revs = REV_INFO_INIT;
 	const char *argv[] = {NULL, "--all", NULL};
 	struct commit *commit;
 	int i, ret = 0, req_nr;
 	const char *message = _("Repository lacks these prerequisite commits:");
 
-	if (!r || !r->objects || !r->objects->odb)
-		return error(_("need a repository to verify a bundle"));
+	if (!r || !r->objects || !r->objects->odb) {
+		ret = error(_("need a repository to verify a bundle"));
+		goto cleanup;
+	}
 
 	repo_init_revisions(r, &revs, NULL);
 	for (i = 0; i < p->nr; i++) {
@@ -221,7 +223,7 @@ int verify_bundle(struct repository *r,
 		error("%s %s", oid_to_hex(oid), name);
 	}
 	if (revs.pending.nr != p->nr)
-		return ret;
+		goto cleanup;
 	req_nr = revs.pending.nr;
 	setup_revisions(2, argv, &revs, NULL);
 
@@ -284,6 +286,8 @@ int verify_bundle(struct repository *r,
 			printf_ln("The bundle uses this filter: %s",
 				  list_objects_filter_spec(&header->filter));
 	}
+cleanup:
+	release_revisions(&revs);
 	return ret;
 }
 
diff --git a/revision.h b/revision.h
index b9070e43428..2621eb6d65a 100644
--- a/revision.h
+++ b/revision.h
@@ -329,6 +329,25 @@ struct rev_info {
 	struct tmp_objdir *remerge_objdir;
 };
 
+/**
+ * Initialize the "struct rev_info" structure with a macro.
+ *
+ * This will not fully initialize a "struct rev_info", the
+ * repo_init_revisions() function needs to be called before
+ * setup_revisions() and any revision walking takes place.
+ *
+ * Use REV_INFO_INIT to make the "struct rev_info" safe for passing to
+ * release_revisions() when it's inconvenient (e.g. due to a "goto
+ * cleanup" pattern) to arrange for repo_init_revisions() to be called
+ * before release_revisions() is called.
+ *
+ * Initializing with this REV_INFO_INIT is redundant to invoking
+ * repo_init_revisions(). If repo_init_revisions() is guaranteed to be
+ * called before release_revisions() the "struct rev_info" can be left
+ * uninitialized.
+ */
+#define REV_INFO_INIT { 0 }
+
 /**
  * Initialize a rev_info structure with default values. The third parameter may
  * be NULL or can be prefix path, and then the `.prefix` variable will be set
@@ -363,7 +382,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs,
 
 /**
  * Free data allocated in a "struct rev_info" after it's been
- * initialized with repo_init_revisions().
+ * initialized with repo_init_revisions() or REV_INFO_INIT.
  */
 void release_revisions(struct rev_info *revs);
 
diff --git a/submodule.c b/submodule.c
index 7b5d7753930..7923e951e1c 100644
--- a/submodule.c
+++ b/submodule.c
@@ -619,7 +619,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,
 		struct object_id *one, struct object_id *two,
 		unsigned dirty_submodule)
 {
-	struct rev_info rev;
+	struct rev_info rev = REV_INFO_INIT;
 	struct commit *left = NULL, *right = NULL;
 	struct commit_list *merge_bases = NULL;
 	struct repository *sub;
@@ -646,6 +646,7 @@ void show_submodule_diff_summary(struct diff_options *o, const char *path,
 
 out:
 	free_commit_list(merge_bases);
+	release_revisions(&rev);
 	clear_commit_marks(left, ~0);
 	clear_commit_marks(right, ~0);
 	if (sub) {
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 10/27] stash: always have the owner of "stash_info" free it
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (8 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 09/27] revisions API users: use release_revisions() needing REV_INFO_INIT Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 11/27] revisions API users: add "goto cleanup" for release_revisions() Ævar Arnfjörð Bjarmason
                             ` (16 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Change the initialization of the "revision" member of "struct
stash_info" to be initialized vi a macro, and more importantly that
that initializing function be tasked to free it, usually via a "goto
cleanup" pattern.

Despite the "revision" name (and the topic of the series containing
this commit) the "stash info" has nothing to do with the "struct
rev_info". I'm making this change because in the subsequent commit
when we do want to free the "struct rev_info" via a "goto cleanup"
pattern we'd otherwise free() uninitialized memory in some cases, as
we only strbuf_init() the string in get_stash_info().

So while it's not the smallest possible change, let's convert all
users of this pattern in the file while we're at it.

A good follow-up to this change would be to change all the "ret = -1;
goto done;" in this file to instead use a "goto cleanup", and
initialize "int ret = -1" at the start of the relevant functions. That
would allow us to drop a lot of needless brace verbosity on two-line
"if" statements, but let's leave that alone for now.

To ensure that there's a 1=1 mapping between owners of the "struct
stash_info" and free_stash_info() change the assert_stash_ref()
function to be a trivial get_stash_info_assert() wrapper. The caller
will call free_stash_info(), and by returning -1 we'll eventually (via
!!ret) exit with status 1 anyway.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/stash.c | 107 ++++++++++++++++++++++++++----------------------
 1 file changed, 59 insertions(+), 48 deletions(-)

diff --git a/builtin/stash.c b/builtin/stash.c
index 74fa810cf8c..ba030238939 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -116,6 +116,10 @@ struct stash_info {
 	int has_u;
 };
 
+#define STASH_INFO_INIT { \
+	.revision = STRBUF_INIT, \
+}
+
 static void free_stash_info(struct stash_info *info)
 {
 	strbuf_release(&info->revision);
@@ -157,10 +161,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
 	if (argc == 1)
 		commit = argv[0];
 
-	strbuf_init(&info->revision, 0);
 	if (!commit) {
 		if (!ref_exists(ref_stash)) {
-			free_stash_info(info);
 			fprintf_ln(stderr, _("No stash entries found."));
 			return -1;
 		}
@@ -174,11 +176,8 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
 
 	revision = info->revision.buf;
 
-	if (get_oid(revision, &info->w_commit)) {
-		error(_("%s is not a valid reference"), revision);
-		free_stash_info(info);
-		return -1;
-	}
+	if (get_oid(revision, &info->w_commit))
+		return error(_("%s is not a valid reference"), revision);
 
 	assert_stash_like(info, revision);
 
@@ -197,7 +196,7 @@ static int get_stash_info(struct stash_info *info, int argc, const char **argv)
 		info->is_stash_ref = !strcmp(expanded_ref, ref_stash);
 		break;
 	default: /* Invalid or ambiguous */
-		free_stash_info(info);
+		break;
 	}
 
 	free(expanded_ref);
@@ -598,10 +597,10 @@ static int do_apply_stash(const char *prefix, struct stash_info *info,
 
 static int apply_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret;
+	int ret = -1;
 	int quiet = 0;
 	int index = 0;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_BOOL(0, "index", &index,
@@ -613,9 +612,10 @@ static int apply_stash(int argc, const char **argv, const char *prefix)
 			     git_stash_apply_usage, 0);
 
 	if (get_stash_info(&info, argc, argv))
-		return -1;
+		goto cleanup;
 
 	ret = do_apply_stash(prefix, &info, index, quiet);
+cleanup:
 	free_stash_info(&info);
 	return ret;
 }
@@ -651,20 +651,25 @@ static int do_drop_stash(struct stash_info *info, int quiet)
 	return 0;
 }
 
-static void assert_stash_ref(struct stash_info *info)
+static int get_stash_info_assert(struct stash_info *info, int argc,
+				 const char **argv)
 {
-	if (!info->is_stash_ref) {
-		error(_("'%s' is not a stash reference"), info->revision.buf);
-		free_stash_info(info);
-		exit(1);
-	}
+	int ret = get_stash_info(info, argc, argv);
+
+	if (ret < 0)
+		return ret;
+
+	if (!info->is_stash_ref)
+		return error(_("'%s' is not a stash reference"), info->revision.buf);
+
+	return 0;
 }
 
 static int drop_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret;
+	int ret = -1;
 	int quiet = 0;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_END()
@@ -673,22 +678,21 @@ static int drop_stash(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, options,
 			     git_stash_drop_usage, 0);
 
-	if (get_stash_info(&info, argc, argv))
-		return -1;
-
-	assert_stash_ref(&info);
+	if (get_stash_info_assert(&info, argc, argv))
+		goto cleanup;
 
 	ret = do_drop_stash(&info, quiet);
+cleanup:
 	free_stash_info(&info);
 	return ret;
 }
 
 static int pop_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret;
+	int ret = -1;
 	int index = 0;
 	int quiet = 0;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct option options[] = {
 		OPT__QUIET(&quiet, N_("be quiet, only report errors")),
 		OPT_BOOL(0, "index", &index,
@@ -699,25 +703,25 @@ static int pop_stash(int argc, const char **argv, const char *prefix)
 	argc = parse_options(argc, argv, prefix, options,
 			     git_stash_pop_usage, 0);
 
-	if (get_stash_info(&info, argc, argv))
-		return -1;
+	if (get_stash_info_assert(&info, argc, argv))
+		goto cleanup;
 
-	assert_stash_ref(&info);
 	if ((ret = do_apply_stash(prefix, &info, index, quiet)))
 		printf_ln(_("The stash entry is kept in case "
 			    "you need it again."));
 	else
 		ret = do_drop_stash(&info, quiet);
 
+cleanup:
 	free_stash_info(&info);
 	return ret;
 }
 
 static int branch_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret;
+	int ret = -1;
 	const char *branch = NULL;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct child_process cp = CHILD_PROCESS_INIT;
 	struct option options[] = {
 		OPT_END()
@@ -734,7 +738,7 @@ static int branch_stash(int argc, const char **argv, const char *prefix)
 	branch = argv[0];
 
 	if (get_stash_info(&info, argc - 1, argv + 1))
-		return -1;
+		goto cleanup;
 
 	cp.git_cmd = 1;
 	strvec_pushl(&cp.args, "checkout", "-b", NULL);
@@ -746,8 +750,8 @@ static int branch_stash(int argc, const char **argv, const char *prefix)
 	if (!ret && info.is_stash_ref)
 		ret = do_drop_stash(&info, 0);
 
+cleanup:
 	free_stash_info(&info);
-
 	return ret;
 }
 
@@ -825,8 +829,8 @@ static void diff_include_untracked(const struct stash_info *info, struct diff_op
 static int show_stash(int argc, const char **argv, const char *prefix)
 {
 	int i;
-	int ret = 0;
-	struct stash_info info;
+	int ret = -1;
+	struct stash_info info = STASH_INFO_INIT;
 	struct rev_info rev;
 	struct strvec stash_args = STRVEC_INIT;
 	struct strvec revision_args = STRVEC_INIT;
@@ -844,6 +848,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 			      UNTRACKED_ONLY, PARSE_OPT_NONEG),
 		OPT_END()
 	};
+	int do_usage = 0;
 
 	init_diff_ui_defaults();
 	git_config(git_diff_ui_config, NULL);
@@ -861,10 +866,8 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 			strvec_push(&revision_args, argv[i]);
 	}
 
-	ret = get_stash_info(&info, stash_args.nr, stash_args.v);
-	strvec_clear(&stash_args);
-	if (ret)
-		return -1;
+	if (get_stash_info(&info, stash_args.nr, stash_args.v))
+		goto cleanup;
 
 	/*
 	 * The config settings are applied only if there are not passed
@@ -878,16 +881,14 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 			rev.diffopt.output_format |= DIFF_FORMAT_PATCH;
 
 		if (!show_stat && !show_patch) {
-			free_stash_info(&info);
-			return 0;
+			ret = 0;
+			goto cleanup;
 		}
 	}
 
 	argc = setup_revisions(revision_args.nr, revision_args.v, &rev, NULL);
-	if (argc > 1) {
-		free_stash_info(&info);
-		usage_with_options(git_stash_show_usage, options);
-	}
+	if (argc > 1)
+		goto usage;
 	if (!rev.diffopt.output_format) {
 		rev.diffopt.output_format = DIFF_FORMAT_PATCH;
 		diff_setup_done(&rev.diffopt);
@@ -912,8 +913,16 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 	}
 	log_tree_diff_flush(&rev);
 
+	ret = diff_result_code(&rev.diffopt, 0);
+cleanup:
+	strvec_clear(&stash_args);
 	free_stash_info(&info);
-	return diff_result_code(&rev.diffopt, 0);
+	if (do_usage)
+		usage_with_options(git_stash_show_usage, options);
+	return ret;
+usage:
+	do_usage = 1;
+	goto cleanup;
 }
 
 static int do_store_stash(const struct object_id *w_commit, const char *stash_msg,
@@ -1409,9 +1418,9 @@ static int do_create_stash(const struct pathspec *ps, struct strbuf *stash_msg_b
 
 static int create_stash(int argc, const char **argv, const char *prefix)
 {
-	int ret = 0;
+	int ret;
 	struct strbuf stash_msg_buf = STRBUF_INIT;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct pathspec ps;
 
 	/* Starting with argv[1], since argv[0] is "create" */
@@ -1426,6 +1435,7 @@ static int create_stash(int argc, const char **argv, const char *prefix)
 	if (!ret)
 		printf_ln("%s", oid_to_hex(&info.w_commit));
 
+	free_stash_info(&info);
 	strbuf_release(&stash_msg_buf);
 	return ret;
 }
@@ -1434,7 +1444,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
 			 int keep_index, int patch_mode, int include_untracked, int only_staged)
 {
 	int ret = 0;
-	struct stash_info info;
+	struct stash_info info = STASH_INFO_INIT;
 	struct strbuf patch = STRBUF_INIT;
 	struct strbuf stash_msg_buf = STRBUF_INIT;
 	struct strbuf untracked_files = STRBUF_INIT;
@@ -1633,6 +1643,7 @@ static int do_push_stash(const struct pathspec *ps, const char *stash_msg, int q
 	}
 
 done:
+	free_stash_info(&info);
 	strbuf_release(&stash_msg_buf);
 	return ret;
 }
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 11/27] revisions API users: add "goto cleanup" for release_revisions()
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (9 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 10/27] stash: always have the owner of "stash_info" free it Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-07-11 18:06             ` Jeff King
  2022-04-13 20:01           ` [PATCH v6 12/27] revisions API users: use release_revisions() in http-push.c Ævar Arnfjörð Bjarmason
                             ` (15 subsequent siblings)
  26 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Add a release_revisions() to various users of "struct rev_info" which
requires a minor refactoring to a "goto cleanup" pattern to use that
function.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/diff-files.c        |  8 ++++++--
 builtin/rev-list.c          | 19 ++++++++++++-------
 builtin/stash.c             |  1 +
 builtin/submodule--helper.c | 10 +++++++---
 sequencer.c                 | 23 ++++++++++++++++-------
 t/helper/test-fast-rebase.c | 18 +++++++++++++-----
 6 files changed, 55 insertions(+), 24 deletions(-)

diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index 70103c40952..2bfaf9ba7ae 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -77,8 +77,12 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
 
 	if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
 		perror("read_cache_preload");
-		return -1;
+		result = -1;
+		goto cleanup;
 	}
+cleanup:
 	result = run_diff_files(&rev, options);
-	return diff_result_code(&rev.diffopt, result);
+	result = diff_result_code(&rev.diffopt, result);
+	release_revisions(&rev);
+	return result;
 }
diff --git a/builtin/rev-list.c b/builtin/rev-list.c
index 07c0ad11d8d..30fd8e83eaf 100644
--- a/builtin/rev-list.c
+++ b/builtin/rev-list.c
@@ -500,6 +500,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 	int use_bitmap_index = 0;
 	int filter_provided_objects = 0;
 	const char *show_progress = NULL;
+	int ret = 0;
 
 	if (argc == 2 && !strcmp(argv[1], "-h"))
 		usage(rev_list_usage);
@@ -583,7 +584,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 		}
 		if (!strcmp(arg, "--test-bitmap")) {
 			test_bitmap_walk(&revs);
-			return 0;
+			goto cleanup;
 		}
 		if (skip_prefix(arg, "--progress=", &arg)) {
 			show_progress = arg;
@@ -672,11 +673,11 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 
 	if (use_bitmap_index) {
 		if (!try_bitmap_count(&revs, filter_provided_objects))
-			return 0;
+			goto cleanup;
 		if (!try_bitmap_disk_usage(&revs, filter_provided_objects))
-			return 0;
+			goto cleanup;
 		if (!try_bitmap_traversal(&revs, filter_provided_objects))
-			return 0;
+			goto cleanup;
 	}
 
 	if (prepare_revision_walk(&revs))
@@ -696,8 +697,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 
 		find_bisection(&revs.commits, &reaches, &all, bisect_flags);
 
-		if (bisect_show_vars)
-			return show_bisect_vars(&info, reaches, all);
+		if (bisect_show_vars) {
+			ret = show_bisect_vars(&info, reaches, all);
+			goto cleanup;
+		}
 	}
 
 	if (filter_provided_objects) {
@@ -752,5 +755,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
 	if (show_disk_usage)
 		printf("%"PRIuMAX"\n", (uintmax_t)total_disk_usage);
 
-	return 0;
+cleanup:
+	release_revisions(&revs);
+	return ret;
 }
diff --git a/builtin/stash.c b/builtin/stash.c
index ba030238939..fce4393d123 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -917,6 +917,7 @@ static int show_stash(int argc, const char **argv, const char *prefix)
 cleanup:
 	strvec_clear(&stash_args);
 	free_stash_info(&info);
+	release_revisions(&rev);
 	if (do_usage)
 		usage_with_options(git_stash_show_usage, options);
 	return ret;
diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c
index 9a65985b418..f83c75c7768 100644
--- a/builtin/submodule--helper.c
+++ b/builtin/submodule--helper.c
@@ -1232,6 +1232,7 @@ static int compute_summary_module_list(struct object_id *head_oid,
 	struct strvec diff_args = STRVEC_INIT;
 	struct rev_info rev;
 	struct module_cb_list list = MODULE_CB_LIST_INIT;
+	int ret = 0;
 
 	strvec_push(&diff_args, get_diff_cmd(diff_cmd));
 	if (info->cached)
@@ -1257,11 +1258,13 @@ static int compute_summary_module_list(struct object_id *head_oid,
 			setup_work_tree();
 		if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
 			perror("read_cache_preload");
-			return -1;
+			ret = -1;
+			goto cleanup;
 		}
 	} else if (read_cache() < 0) {
 		perror("read_cache");
-		return -1;
+		ret = -1;
+		goto cleanup;
 	}
 
 	if (diff_cmd == DIFF_INDEX)
@@ -1269,9 +1272,10 @@ static int compute_summary_module_list(struct object_id *head_oid,
 	else
 		run_diff_files(&rev, 0);
 	prepare_submodule_summary(info, &list);
+cleanup:
 	strvec_clear(&diff_args);
 	release_revisions(&rev);
-	return 0;
+	return ret;
 }
 
 static int module_summary(int argc, const char **argv, const char *prefix)
diff --git a/sequencer.c b/sequencer.c
index f9d7acd1065..41ae5e25278 100644
--- a/sequencer.c
+++ b/sequencer.c
@@ -5354,6 +5354,7 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
 	int rebase_merges = flags & TODO_LIST_REBASE_MERGES;
 	int reapply_cherry_picks = flags & TODO_LIST_REAPPLY_CHERRY_PICKS;
 	int skipped_commit = 0;
+	int ret = 0;
 
 	repo_init_revisions(r, &revs, NULL);
 	revs.verbose_header = 1;
@@ -5377,14 +5378,20 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
 	pp.fmt = revs.commit_format;
 	pp.output_encoding = get_log_output_encoding();
 
-	if (setup_revisions(argc, argv, &revs, NULL) > 1)
-		return error(_("make_script: unhandled options"));
+	if (setup_revisions(argc, argv, &revs, NULL) > 1) {
+		ret = error(_("make_script: unhandled options"));
+		goto cleanup;
+	}
 
-	if (prepare_revision_walk(&revs) < 0)
-		return error(_("make_script: error preparing revisions"));
+	if (prepare_revision_walk(&revs) < 0) {
+		ret = error(_("make_script: error preparing revisions"));
+		goto cleanup;
+	}
 
-	if (rebase_merges)
-		return make_script_with_merges(&pp, &revs, out, flags);
+	if (rebase_merges) {
+		ret = make_script_with_merges(&pp, &revs, out, flags);
+		goto cleanup;
+	}
 
 	while ((commit = get_revision(&revs))) {
 		int is_empty = is_original_commit_empty(commit);
@@ -5408,7 +5415,9 @@ int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
 	if (skipped_commit)
 		advise_if_enabled(ADVICE_SKIPPED_CHERRY_PICKS,
 				  _("use --reapply-cherry-picks to include skipped commits"));
-	return 0;
+cleanup:
+	release_revisions(&revs);
+	return ret;
 }
 
 /*
diff --git a/t/helper/test-fast-rebase.c b/t/helper/test-fast-rebase.c
index 993b90eaedd..4e5553e2024 100644
--- a/t/helper/test-fast-rebase.c
+++ b/t/helper/test-fast-rebase.c
@@ -99,6 +99,7 @@ int cmd__fast_rebase(int argc, const char **argv)
 	struct merge_result result;
 	struct strbuf reflog_msg = STRBUF_INIT;
 	struct strbuf branch_name = STRBUF_INIT;
+	int ret = 0;
 
 	/*
 	 * test-tool stuff doesn't set up the git directory by default; need to
@@ -137,13 +138,17 @@ int cmd__fast_rebase(int argc, const char **argv)
 	revs.topo_order = 1;
 	strvec_pushl(&rev_walk_args, "", argv[4], "--not", argv[3], NULL);
 
-	if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1)
-		return error(_("unhandled options"));
+	if (setup_revisions(rev_walk_args.nr, rev_walk_args.v, &revs, NULL) > 1) {
+		ret = error(_("unhandled options"));
+		goto cleanup;
+	}
 
 	strvec_clear(&rev_walk_args);
 
-	if (prepare_revision_walk(&revs) < 0)
-		return error(_("error preparing revisions"));
+	if (prepare_revision_walk(&revs) < 0) {
+		ret = error(_("error preparing revisions"));
+		goto cleanup;
+	}
 
 	init_merge_options(&merge_opt, the_repository);
 	memset(&result, 0, sizeof(result));
@@ -220,7 +225,10 @@ int cmd__fast_rebase(int argc, const char **argv)
 			       COMMIT_LOCK | SKIP_IF_UNCHANGED))
 		die(_("unable to write %s"), get_index_file());
 
+	ret = (result.clean == 0);
+cleanup:
 	strbuf_release(&reflog_msg);
 	strbuf_release(&branch_name);
-	return (result.clean == 0);
+	release_revisions(&revs);
+	return ret;
 }
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 12/27] revisions API users: use release_revisions() in http-push.c
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (10 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 11/27] revisions API users: add "goto cleanup" for release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 13/27] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
                             ` (14 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

In the case of cmd_main() in http-push.c we need to move the
deceleration of the "struct rev-list" into the loop over the
"remote_refs" when adding a release_revisions().

We'd previously set up the "revs" for each remote, but would
potentially leak memory on each one.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 http-push.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/http-push.c b/http-push.c
index f0c044dcf76..01e7c2ac5c8 100644
--- a/http-push.c
+++ b/http-push.c
@@ -1689,7 +1689,6 @@ int cmd_main(int argc, const char **argv)
 	struct refspec rs = REFSPEC_INIT_PUSH;
 	struct remote_lock *ref_lock = NULL;
 	struct remote_lock *info_ref_lock = NULL;
-	struct rev_info revs;
 	int delete_branch = 0;
 	int force_delete = 0;
 	int objects_to_send;
@@ -1825,6 +1824,7 @@ int cmd_main(int argc, const char **argv)
 
 	new_refs = 0;
 	for (ref = remote_refs; ref; ref = ref->next) {
+		struct rev_info revs;
 		struct strvec commit_argv = STRVEC_INIT;
 
 		if (!ref->peer_ref)
@@ -1941,6 +1941,7 @@ int cmd_main(int argc, const char **argv)
 		unlock_remote(ref_lock);
 		check_locks();
 		strvec_clear(&commit_argv);
+		release_revisions(&revs);
 	}
 
 	/* Update remote server info if appropriate */
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 13/27] revisions API users: use release_revisions() in builtin/log.c
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (11 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 12/27] revisions API users: use release_revisions() in http-push.c Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 14/27] revisions API users: use release_revisions() with UNLEAK() Ævar Arnfjörð Bjarmason
                             ` (13 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

In preparation for having the "log" family of functions make wider use
of release_revisions() let's have them call it just before
exiting. This changes the "log", "whatchanged", "show",
"format-patch", etc. commands, all of which live in this file.

The release_revisions() API still only frees the "pending" member, but
will learn to release more members of "struct rev_info" in subsequent
commits.

In the case of "format-patch" revert the addition of UNLEAK() in
dee839a2633 (format-patch: mark rev_info with UNLEAK, 2021-12-16),
which will cause several tests that previously passed under
"TEST_PASSES_SANITIZE_LEAK=true" to start failing.

In subsequent commits we'll now be able to use those tests to check
whether that part of the API is really leaking memory, and will fix
all of those memory leaks. Removing the UNLEAK() allows us to make
incremental progress in that direction. See [1] for further details
about this approach.

Note that the release_revisions() will not be sufficient to deal with
the code in cmd_show() added in 5d7eeee2ac6 (git-show: grok blobs,
trees and tags, too, 2006-12-14) which clobbers the "pending" array in
the case of "OBJ_COMMIT". That will need to be dealt with by some
future follow-up work.

1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/log.c          | 20 ++++++++++++--------
 t/t4126-apply-empty.sh |  2 --
 2 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/builtin/log.c b/builtin/log.c
index 6f9928fabfe..c40ebe1c3f4 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -295,6 +295,12 @@ static void cmd_log_init(int argc, const char **argv, const char *prefix,
 	cmd_log_init_finish(argc, argv, prefix, rev, opt);
 }
 
+static int cmd_log_deinit(int ret, struct rev_info *rev)
+{
+	release_revisions(rev);
+	return ret;
+}
+
 /*
  * This gives a rough estimate for how many commits we
  * will print out in the list.
@@ -558,7 +564,7 @@ int cmd_whatchanged(int argc, const char **argv, const char *prefix)
 	cmd_log_init(argc, argv, prefix, &rev, &opt);
 	if (!rev.diffopt.output_format)
 		rev.diffopt.output_format = DIFF_FORMAT_RAW;
-	return cmd_log_walk(&rev);
+	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 }
 
 static void show_tagger(const char *buf, struct rev_info *rev)
@@ -677,7 +683,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
 	cmd_log_init(argc, argv, prefix, &rev, &opt);
 
 	if (!rev.no_walk)
-		return cmd_log_walk(&rev);
+		return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 
 	count = rev.pending.nr;
 	objects = rev.pending.objects;
@@ -732,8 +738,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
 			ret = error(_("unknown type: %d"), o->type);
 		}
 	}
-	free(objects);
-	return ret;
+	return cmd_log_deinit(ret, &rev);
 }
 
 /*
@@ -761,7 +766,7 @@ int cmd_log_reflog(int argc, const char **argv, const char *prefix)
 	rev.always_show_header = 1;
 	cmd_log_init_finish(argc, argv, prefix, &rev, &opt);
 
-	return cmd_log_walk(&rev);
+	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 }
 
 static void log_setup_revisions_tweak(struct rev_info *rev,
@@ -792,7 +797,7 @@ int cmd_log(int argc, const char **argv, const char *prefix)
 	opt.revarg_opt = REVARG_COMMITTISH;
 	opt.tweak = log_setup_revisions_tweak;
 	cmd_log_init(argc, argv, prefix, &rev, &opt);
-	return cmd_log_walk(&rev);
+	return cmd_log_deinit(cmd_log_walk(&rev), &rev);
 }
 
 /* format-patch */
@@ -2289,8 +2294,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
 	if (rev.ref_message_ids)
 		string_list_clear(rev.ref_message_ids, 0);
 	free(rev.ref_message_ids);
-	UNLEAK(rev);
-	return 0;
+	return cmd_log_deinit(0, &rev);
 }
 
 static int add_pending_commit(const char *arg, struct rev_info *revs, int flags)
diff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh
index 33860d38290..66a7ba8ab8f 100755
--- a/t/t4126-apply-empty.sh
+++ b/t/t4126-apply-empty.sh
@@ -2,8 +2,6 @@
 
 test_description='apply empty'
 
-
-TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 14/27] revisions API users: use release_revisions() with UNLEAK()
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (12 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 13/27] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-15 13:58             ` Phillip Wood
  2022-04-13 20:01           ` [PATCH v6 15/27] revisions API users: use release_revisions() for "prune_data" users Ævar Arnfjörð Bjarmason
                             ` (12 subsequent siblings)
  26 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Use a release_revisions() with those "struct rev_list" users which
already "UNLEAK" the struct. It may seem odd to simultaneously attempt
to free() memory, but also to explicitly ignore whether we have memory
leaks in the same.

As explained in preceding commits this is being done to use the
built-in commands as a guinea pig for whether the release_revisions()
function works as expected, we'd like to test e.g. whether we segfault
as we change it. In subsequent commits we'll then remove these
UNLEAK() as the function is made to free the memory that caused us to
add them in the first place.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/diff-index.c | 4 +++-
 builtin/diff.c       | 1 +
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 5fd23ab5b6c..3a83183c312 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -71,5 +71,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
 	}
 	result = run_diff_index(&rev, option);
 	UNLEAK(rev);
-	return diff_result_code(&rev.diffopt, result);
+	result = diff_result_code(&rev.diffopt, result);
+	release_revisions(&rev);
+	return result;
 }
diff --git a/builtin/diff.c b/builtin/diff.c
index bb7fafca618..dd48336da56 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -595,6 +595,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
 	if (1 < rev.diffopt.skip_stat_unmatch)
 		refresh_index_quietly();
 	UNLEAK(rev);
+	release_revisions(&rev);
 	UNLEAK(ent);
 	UNLEAK(blob);
 	return result;
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 15/27] revisions API users: use release_revisions() for "prune_data" users
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (13 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 14/27] revisions API users: use release_revisions() with UNLEAK() Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 16/27] revisions API: have release_revisions() release "commits" Ævar Arnfjörð Bjarmason
                             ` (11 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Use release_revisions() for users of "struct rev_list" that reach into
the "struct rev_info" and clear the "prune_data" already.

In a subsequent commit we'll teach release_revisions() to clear this
itself, but in the meantime let's invoke release_revisions() here to
clear anything else we may have missed, and for reasons of having
consistent boilerplate.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/add.c | 1 +
 diff-lib.c    | 1 +
 wt-status.c   | 1 +
 3 files changed, 3 insertions(+)

diff --git a/builtin/add.c b/builtin/add.c
index f507d2191cd..115a26ea633 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -143,6 +143,7 @@ int add_files_to_cache(const char *prefix,
 	rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
 	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 	return !!data.add_errors;
 }
 
diff --git a/diff-lib.c b/diff-lib.c
index d6800274bd5..0f16281253f 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -642,6 +642,7 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
 	if (diff_cache(&revs, tree_oid, NULL, 1))
 		exit(128);
 	clear_pathspec(&revs.prune_data);
+	release_revisions(&revs);
 	return 0;
 }
 
diff --git a/wt-status.c b/wt-status.c
index f9100621375..a14fad1e03a 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -617,6 +617,7 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_files(&rev, 0);
 	clear_pathspec(&rev.prune_data);
+	release_revisions(&rev);
 }
 
 static void wt_status_collect_changes_index(struct wt_status *s)
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 16/27] revisions API: have release_revisions() release "commits"
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (14 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 15/27] revisions API users: use release_revisions() for "prune_data" users Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 17/27] revisions API: have release_revisions() release "mailmap" Ævar Arnfjörð Bjarmason
                             ` (10 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"commits" in the "struct rev_info".

We don't expect to use this "struct rev_info" again, so there's no
reason to NULL out revs->commits, as e.g. simplify_merges() and
create_boundary_commit_list() do.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/revision.c b/revision.c
index 472fff1e0a7..553f7de8250 100644
--- a/revision.c
+++ b/revision.c
@@ -2928,6 +2928,7 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 
 void release_revisions(struct rev_info *revs)
 {
+	free_commit_list(revs->commits);
 	object_array_clear(&revs->pending);
 }
 
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 17/27] revisions API: have release_revisions() release "mailmap"
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (15 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 16/27] revisions API: have release_revisions() release "commits" Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 18/27] revisions API: have release_revisions() release "cmdline" Ævar Arnfjörð Bjarmason
                             ` (9 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"mailmap" in the "struct rev_info".

The log family of functions now calls the clear_mailmap() function
added in fa8afd18e5a (revisions API: provide and use a
release_revisions(), 2021-09-19), allowing us to whitelist some tests
with "TEST_PASSES_SANITIZE_LEAK=true".

Unfortunately having a pointer to a mailmap in "struct rev_info"
instead of an embedded member that we "own" get a bit messy, as can be
seen in the change to builtin/commit.c.

When we free() this data we won't be able to tell apart a pointer to a
"mailmap" on the heap from one on the stack. As seen in
ea57bc0d41b (log: add --use-mailmap option, 2013-01-05) the "log"
family allocates it on the heap, but in the find_author_by_nickname()
code added in ea16794e430 (commit: search author pattern against
mailmap, 2013-08-23) we allocated it on the stack instead.

Ideally we'd simply change that member to a "struct string_list
mailmap" and never free() the "mailmap" itself, but that would be a
much larger change to the revisions API.

We have code that needs to hand an existing "mailmap" to a "struct
rev_info", while we could change all of that, let's not go there
now.

The complexity isn't in the ownership of the "mailmap" per-se, but
that various things assume a "rev_info.mailmap == NULL" means "doesn't
want mailmap", if we changed that to an init'd "struct string_list
we'd need to carefully refactor things to change those assumptions.

Let's instead always free() it, and simply declare that if you add
such a "mailmap" it must be allocated on the heap. Any modern libc
will correctly panic if we free() a stack variable, so this should be
safe going forward.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/commit.c                   | 5 ++---
 revision.c                         | 9 +++++++++
 t/t0056-git-C.sh                   | 1 +
 t/t3302-notes-index-expensive.sh   | 1 +
 t/t4055-diff-context.sh            | 1 +
 t/t4066-diff-emit-delay.sh         | 1 +
 t/t7008-filter-branch-null-sha1.sh | 1 +
 7 files changed, 16 insertions(+), 3 deletions(-)

diff --git a/builtin/commit.c b/builtin/commit.c
index c7eda9bbb72..cd6cebcf8c8 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1100,7 +1100,6 @@ static const char *find_author_by_nickname(const char *name)
 	struct rev_info revs;
 	struct commit *commit;
 	struct strbuf buf = STRBUF_INIT;
-	struct string_list mailmap = STRING_LIST_INIT_NODUP;
 	const char *av[20];
 	int ac = 0;
 
@@ -1111,7 +1110,8 @@ static const char *find_author_by_nickname(const char *name)
 	av[++ac] = buf.buf;
 	av[++ac] = NULL;
 	setup_revisions(ac, av, &revs, NULL);
-	revs.mailmap = &mailmap;
+	revs.mailmap = xmalloc(sizeof(struct string_list));
+	string_list_init_nodup(revs.mailmap);
 	read_mailmap(revs.mailmap);
 
 	if (prepare_revision_walk(&revs))
@@ -1122,7 +1122,6 @@ static const char *find_author_by_nickname(const char *name)
 		ctx.date_mode.type = DATE_NORMAL;
 		strbuf_release(&buf);
 		format_commit_message(commit, "%aN <%aE>", &buf, &ctx);
-		clear_mailmap(&mailmap);
 		release_revisions(&revs);
 		return strbuf_detach(&buf, NULL);
 	}
diff --git a/revision.c b/revision.c
index 553f7de8250..622f0faecc4 100644
--- a/revision.c
+++ b/revision.c
@@ -2926,10 +2926,19 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 	return left;
 }
 
+static void release_revisions_mailmap(struct string_list *mailmap)
+{
+	if (!mailmap)
+		return;
+	clear_mailmap(mailmap);
+	free(mailmap);
+}
+
 void release_revisions(struct rev_info *revs)
 {
 	free_commit_list(revs->commits);
 	object_array_clear(&revs->pending);
+	release_revisions_mailmap(revs->mailmap);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
diff --git a/t/t0056-git-C.sh b/t/t0056-git-C.sh
index 2630e756dab..752aa8c9454 100755
--- a/t/t0056-git-C.sh
+++ b/t/t0056-git-C.sh
@@ -2,6 +2,7 @@
 
 test_description='"-C <path>" option and its effects on other path-related options'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success '"git -C <path>" runs git from the directory <path>' '
diff --git a/t/t3302-notes-index-expensive.sh b/t/t3302-notes-index-expensive.sh
index bb5fea02a03..d0c4d38b4d4 100755
--- a/t/t3302-notes-index-expensive.sh
+++ b/t/t3302-notes-index-expensive.sh
@@ -8,6 +8,7 @@ test_description='Test commit notes index (expensive!)'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 create_repo () {
diff --git a/t/t4055-diff-context.sh b/t/t4055-diff-context.sh
index 741e0803c1a..73048d0a526 100755
--- a/t/t4055-diff-context.sh
+++ b/t/t4055-diff-context.sh
@@ -5,6 +5,7 @@
 
 test_description='diff.context configuration'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t4066-diff-emit-delay.sh b/t/t4066-diff-emit-delay.sh
index a1de63b77f8..0ecb3915412 100755
--- a/t/t4066-diff-emit-delay.sh
+++ b/t/t4066-diff-emit-delay.sh
@@ -4,6 +4,7 @@ test_description='test combined/stat/moved interaction'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # This test covers a weird 3-way interaction between "--cc -p", which will run
diff --git a/t/t7008-filter-branch-null-sha1.sh b/t/t7008-filter-branch-null-sha1.sh
index 9ba9f24ad2f..93fbc92b8db 100755
--- a/t/t7008-filter-branch-null-sha1.sh
+++ b/t/t7008-filter-branch-null-sha1.sh
@@ -1,6 +1,7 @@
 #!/bin/sh
 
 test_description='filter-branch removal of trees with null sha1'
+
 . ./test-lib.sh
 
 test_expect_success 'setup: base commits' '
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 18/27] revisions API: have release_revisions() release "cmdline"
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (16 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 17/27] revisions API: have release_revisions() release "mailmap" Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 19/27] revisions API: have release_revisions() release "filter" Ævar Arnfjörð Bjarmason
                             ` (8 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"cmdline" in the "struct rev_info". This in combination with a
preceding change to free "commits" and "mailmap" means that we can
whitelist another test under "TEST_PASSES_SANITIZE_LEAK=true".

There was a proposal in [1] to do away with xstrdup()-ing this
add_rev_cmdline(), perhaps that would be worthwhile, but for now let's
just free() it.

We could also make that a "char *" in "struct rev_cmdline_entry"
itself, but since we own it let's expose it as a constant to outside
callers. I proposed that in [2] but have since changed my mind. See
14d30cdfc04 (ref-filter: fix memory leak in `free_array_item()`,
2019-07-10), c514c62a4fd (checkout: fix leak of non-existent branch
names, 2020-08-14) and other log history hits for "free((char *)" for
prior art.

This includes the tests we had false-positive passes on before my
6798b08e848 (perl Git.pm: don't ignore signalled failure in
_cmd_close(), 2022-02-01), now they pass for real.

Since there are 66 tests matching t/t[0-9]*git-svn*.sh it's easier to
list those that don't pass than to touch most of those 66. So let's
introduce a "TEST_FAILS_SANITIZE_LEAK=true", which if set in the tests
won't cause lib-git-svn.sh to set "TEST_PASSES_SANITIZE_LEAK=true.

This change also marks all the tests that we removed
"TEST_FAILS_SANITIZE_LEAK=true" from in an earlier commit due to
removing the UNLEAK() from cmd_format_patch(), we can now assert that
its API use doesn't leak any "struct rev_info" memory.

This change also made commit "t5503-tagfollow.sh" pass on current
master, but that would regress when combined with
ps/fetch-atomic-fixup's de004e848a9 (t5503: simplify setup of test
which exercises failure of backfill, 2022-03-03) (through no fault of
that topic, that change started using "git clone" in the test, which
has an outstanding leak). Let's leave that test out for now to avoid
in-flight semantic conflicts.

1. https://lore.kernel.org/git/YUj%2FgFRh6pwrZalY@carlos-mbp.lan/
2. https://lore.kernel.org/git/87o88obkb1.fsf@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c                                 | 10 ++++++++++
 t/lib-git-svn.sh                           |  4 ++++
 t/t0062-revision-walking.sh                |  1 +
 t/t0101-at-syntax.sh                       |  2 ++
 t/t1060-object-corruption.sh               |  1 +
 t/t3303-notes-subtrees.sh                  |  1 +
 t/t3305-notes-fanout.sh                    |  1 +
 t/t3408-rebase-multi-line.sh               |  1 +
 t/t4027-diff-submodule.sh                  |  1 +
 t/t4128-apply-root.sh                      |  1 +
 t/t4212-log-corrupt.sh                     |  1 +
 t/t5515-fetch-merge-logic.sh               |  1 +
 t/t5518-fetch-exit-status.sh               |  1 +
 t/t6002-rev-list-bisect.sh                 |  1 +
 t/t6003-rev-list-topo-order.sh             |  1 +
 t/t6005-rev-list-count.sh                  |  1 +
 t/t6018-rev-list-glob.sh                   |  1 +
 t/t6100-rev-list-in-order.sh               |  1 +
 t/t9100-git-svn-basic.sh                   |  1 +
 t/t9101-git-svn-props.sh                   |  2 ++
 t/t9104-git-svn-follow-parent.sh           |  2 ++
 t/t9106-git-svn-commit-diff-clobber.sh     |  2 ++
 t/t9115-git-svn-dcommit-funky-renames.sh   |  1 +
 t/t9116-git-svn-log.sh                     |  2 ++
 t/t9122-git-svn-author.sh                  |  2 ++
 t/t9127-git-svn-partial-rebuild.sh         |  2 ++
 t/t9129-git-svn-i18n-commitencoding.sh     |  1 +
 t/t9132-git-svn-broken-symlink.sh          |  1 +
 t/t9139-git-svn-non-utf8-commitencoding.sh |  1 +
 t/t9146-git-svn-empty-dirs.sh              |  2 ++
 t/t9148-git-svn-propset.sh                 |  1 +
 t/t9151-svn-mergeinfo.sh                   |  1 +
 t/t9160-git-svn-preserve-empty-dirs.sh     |  1 +
 t/t9162-git-svn-dcommit-interactive.sh     |  2 ++
 t/t9164-git-svn-dcommit-concurrent.sh      |  2 ++
 t/t9501-gitweb-standalone-http-status.sh   |  1 +
 36 files changed, 58 insertions(+)

diff --git a/revision.c b/revision.c
index 622f0faecc4..de4076e77de 100644
--- a/revision.c
+++ b/revision.c
@@ -2926,6 +2926,15 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, struct s
 	return left;
 }
 
+static void release_revisions_cmdline(struct rev_cmdline_info *cmdline)
+{
+	unsigned int i;
+
+	for (i = 0; i < cmdline->nr; i++)
+		free((char *)cmdline->rev[i].name);
+	free(cmdline->rev);
+}
+
 static void release_revisions_mailmap(struct string_list *mailmap)
 {
 	if (!mailmap)
@@ -2938,6 +2947,7 @@ void release_revisions(struct rev_info *revs)
 {
 	free_commit_list(revs->commits);
 	object_array_clear(&revs->pending);
+	release_revisions_cmdline(&revs->cmdline);
 	release_revisions_mailmap(revs->mailmap);
 }
 
diff --git a/t/lib-git-svn.sh b/t/lib-git-svn.sh
index 2fde2353fd3..ea28971e8ee 100644
--- a/t/lib-git-svn.sh
+++ b/t/lib-git-svn.sh
@@ -1,3 +1,7 @@
+if test -z "$TEST_FAILS_SANITIZE_LEAK"
+then
+	TEST_PASSES_SANITIZE_LEAK=true
+fi
 . ./test-lib.sh
 
 if test -n "$NO_SVN_TESTS"
diff --git a/t/t0062-revision-walking.sh b/t/t0062-revision-walking.sh
index 8e215867b8c..b9480c81781 100755
--- a/t/t0062-revision-walking.sh
+++ b/t/t0062-revision-walking.sh
@@ -5,6 +5,7 @@
 
 test_description='Test revision walking api'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 cat >run_twice_expected <<-EOF
diff --git a/t/t0101-at-syntax.sh b/t/t0101-at-syntax.sh
index a1998b558f9..878aadd64c9 100755
--- a/t/t0101-at-syntax.sh
+++ b/t/t0101-at-syntax.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='various @{whatever} syntax tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh
index bc89371f534..e8a58b15897 100755
--- a/t/t1060-object-corruption.sh
+++ b/t/t1060-object-corruption.sh
@@ -1,6 +1,7 @@
 #!/bin/sh
 
 test_description='see how we handle various forms of corruption'
+
 . ./test-lib.sh
 
 # convert "1234abcd" to ".git/objects/12/34abcd"
diff --git a/t/t3303-notes-subtrees.sh b/t/t3303-notes-subtrees.sh
index eac193757bf..bc9b791d1b9 100755
--- a/t/t3303-notes-subtrees.sh
+++ b/t/t3303-notes-subtrees.sh
@@ -5,6 +5,7 @@ test_description='Test commit notes organized in subtrees'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 number_of_commits=100
diff --git a/t/t3305-notes-fanout.sh b/t/t3305-notes-fanout.sh
index 9976d787f47..64a9915761a 100755
--- a/t/t3305-notes-fanout.sh
+++ b/t/t3305-notes-fanout.sh
@@ -2,6 +2,7 @@
 
 test_description='Test that adding/removing many notes triggers automatic fanout restructuring'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 path_has_fanout() {
diff --git a/t/t3408-rebase-multi-line.sh b/t/t3408-rebase-multi-line.sh
index cde3562e3a6..7b4607d72f2 100755
--- a/t/t3408-rebase-multi-line.sh
+++ b/t/t3408-rebase-multi-line.sh
@@ -5,6 +5,7 @@ test_description='rebasing a commit with multi-line first paragraph.'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4027-diff-submodule.sh b/t/t4027-diff-submodule.sh
index 295da987cce..40164ae07d2 100755
--- a/t/t4027-diff-submodule.sh
+++ b/t/t4027-diff-submodule.sh
@@ -2,6 +2,7 @@
 
 test_description='difference in submodules'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh
 
diff --git a/t/t4128-apply-root.sh b/t/t4128-apply-root.sh
index f6db5a79dd9..ed94c90204e 100755
--- a/t/t4128-apply-root.sh
+++ b/t/t4128-apply-root.sh
@@ -2,6 +2,7 @@
 
 test_description='apply same filename'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t4212-log-corrupt.sh b/t/t4212-log-corrupt.sh
index 0244888a5a7..30a219894bb 100755
--- a/t/t4212-log-corrupt.sh
+++ b/t/t4212-log-corrupt.sh
@@ -2,6 +2,7 @@
 
 test_description='git log with invalid commit headers'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t5515-fetch-merge-logic.sh b/t/t5515-fetch-merge-logic.sh
index 320d26796d2..c100a809c5e 100755
--- a/t/t5515-fetch-merge-logic.sh
+++ b/t/t5515-fetch-merge-logic.sh
@@ -14,6 +14,7 @@ export GIT_TEST_PROTOCOL_VERSION
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 build_script () {
diff --git a/t/t5518-fetch-exit-status.sh b/t/t5518-fetch-exit-status.sh
index 5c4ac2556e7..c13120088fa 100755
--- a/t/t5518-fetch-exit-status.sh
+++ b/t/t5518-fetch-exit-status.sh
@@ -8,6 +8,7 @@ test_description='fetch exit status test'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t6002-rev-list-bisect.sh b/t/t6002-rev-list-bisect.sh
index b95a0212adf..162cf50778d 100755
--- a/t/t6002-rev-list-bisect.sh
+++ b/t/t6002-rev-list-bisect.sh
@@ -4,6 +4,7 @@
 #
 test_description='Tests git rev-list --bisect functionality'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
 
diff --git a/t/t6003-rev-list-topo-order.sh b/t/t6003-rev-list-topo-order.sh
index 24d1836f417..1f7d7dd20c1 100755
--- a/t/t6003-rev-list-topo-order.sh
+++ b/t/t6003-rev-list-topo-order.sh
@@ -5,6 +5,7 @@
 
 test_description='Tests git rev-list --topo-order functionality'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-t6000.sh # t6xxx specific functions
 
diff --git a/t/t6005-rev-list-count.sh b/t/t6005-rev-list-count.sh
index e960049f647..0729f800c3c 100755
--- a/t/t6005-rev-list-count.sh
+++ b/t/t6005-rev-list-count.sh
@@ -2,6 +2,7 @@
 
 test_description='git rev-list --max-count and --skip test'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t6018-rev-list-glob.sh b/t/t6018-rev-list-glob.sh
index 24b34add833..e1abc5c2b32 100755
--- a/t/t6018-rev-list-glob.sh
+++ b/t/t6018-rev-list-glob.sh
@@ -5,6 +5,7 @@ test_description='rev-list/rev-parse --glob'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 commit () {
diff --git a/t/t6100-rev-list-in-order.sh b/t/t6100-rev-list-in-order.sh
index e934bc239c5..88ed7bd75a7 100755
--- a/t/t6100-rev-list-in-order.sh
+++ b/t/t6100-rev-list-in-order.sh
@@ -2,6 +2,7 @@
 
 test_description='rev-list testing in-commit-order'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup a commit history with trees, blobs' '
diff --git a/t/t9100-git-svn-basic.sh b/t/t9100-git-svn-basic.sh
index fea41b3c360..7c5b847f584 100755
--- a/t/t9100-git-svn-basic.sh
+++ b/t/t9100-git-svn-basic.sh
@@ -8,6 +8,7 @@ test_description='git svn basic tests'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 prepare_utf8_locale
diff --git a/t/t9101-git-svn-props.sh b/t/t9101-git-svn-props.sh
index 8b5681dd68a..d043e80fc34 100755
--- a/t/t9101-git-svn-props.sh
+++ b/t/t9101-git-svn-props.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn property tests'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 mkdir import
diff --git a/t/t9104-git-svn-follow-parent.sh b/t/t9104-git-svn-follow-parent.sh
index c7d8e0bf00f..5cf2ef4b8b0 100755
--- a/t/t9104-git-svn-follow-parent.sh
+++ b/t/t9104-git-svn-follow-parent.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn fetching'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9106-git-svn-commit-diff-clobber.sh b/t/t9106-git-svn-commit-diff-clobber.sh
index aec45bca3b7..3cab0b9720a 100755
--- a/t/t9106-git-svn-commit-diff-clobber.sh
+++ b/t/t9106-git-svn-commit-diff-clobber.sh
@@ -2,6 +2,8 @@
 #
 # Copyright (c) 2006 Eric Wong
 test_description='git svn commit-diff clobber'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9115-git-svn-dcommit-funky-renames.sh b/t/t9115-git-svn-dcommit-funky-renames.sh
index 743fbe1fe46..419f055721d 100755
--- a/t/t9115-git-svn-dcommit-funky-renames.sh
+++ b/t/t9115-git-svn-dcommit-funky-renames.sh
@@ -5,6 +5,7 @@
 
 test_description='git svn dcommit can commit renames of files with ugly names'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'load repository with strange names' '
diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh
index 0a9f1ef366d..34f6c80dea3 100755
--- a/t/t9116-git-svn-log.sh
+++ b/t/t9116-git-svn-log.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn log tests'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup repository and import' '
diff --git a/t/t9122-git-svn-author.sh b/t/t9122-git-svn-author.sh
index 9e8fe38e7ef..527ba3d2932 100755
--- a/t/t9122-git-svn-author.sh
+++ b/t/t9122-git-svn-author.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='git svn authorship'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup svn repository' '
diff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh
index 2e4789d061f..90b1b30dde5 100755
--- a/t/t9127-git-svn-partial-rebuild.sh
+++ b/t/t9127-git-svn-partial-rebuild.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='git svn partial-rebuild tests'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize svnrepo' '
diff --git a/t/t9129-git-svn-i18n-commitencoding.sh b/t/t9129-git-svn-i18n-commitencoding.sh
index 01e1e8a8f76..185248a4cd7 100755
--- a/t/t9129-git-svn-i18n-commitencoding.sh
+++ b/t/t9129-git-svn-i18n-commitencoding.sh
@@ -4,6 +4,7 @@
 
 test_description='git svn honors i18n.commitEncoding in config'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 compare_git_head_with () {
diff --git a/t/t9132-git-svn-broken-symlink.sh b/t/t9132-git-svn-broken-symlink.sh
index aeceffaf7b0..4d8d0584b79 100755
--- a/t/t9132-git-svn-broken-symlink.sh
+++ b/t/t9132-git-svn-broken-symlink.sh
@@ -2,6 +2,7 @@
 
 test_description='test that git handles an svn repository with empty symlinks'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 test_expect_success 'load svn dumpfile' '
 	svnadmin load "$rawsvnrepo" <<EOF
diff --git a/t/t9139-git-svn-non-utf8-commitencoding.sh b/t/t9139-git-svn-non-utf8-commitencoding.sh
index 22d80b0be2b..b7f756b2b7f 100755
--- a/t/t9139-git-svn-non-utf8-commitencoding.sh
+++ b/t/t9139-git-svn-non-utf8-commitencoding.sh
@@ -4,6 +4,7 @@
 
 test_description='git svn refuses to dcommit non-UTF8 messages'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 # ISO-2022-JP can pass for valid UTF-8, so skipping that in this test
diff --git a/t/t9146-git-svn-empty-dirs.sh b/t/t9146-git-svn-empty-dirs.sh
index 80cb55fee70..79c26ed69c1 100755
--- a/t/t9146-git-svn-empty-dirs.sh
+++ b/t/t9146-git-svn-empty-dirs.sh
@@ -3,6 +3,8 @@
 # Copyright (c) 2009 Eric Wong
 
 test_description='git svn creates empty directories'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9148-git-svn-propset.sh b/t/t9148-git-svn-propset.sh
index aebb28995e5..6cc76a07b39 100755
--- a/t/t9148-git-svn-propset.sh
+++ b/t/t9148-git-svn-propset.sh
@@ -5,6 +5,7 @@
 
 test_description='git svn propset tests'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup propset via import' '
diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh
index c93a5beab25..85221d439bd 100755
--- a/t/t9151-svn-mergeinfo.sh
+++ b/t/t9151-svn-mergeinfo.sh
@@ -5,6 +5,7 @@
 
 test_description='git-svn svn mergeinfo properties'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'load svn dump' "
diff --git a/t/t9160-git-svn-preserve-empty-dirs.sh b/t/t9160-git-svn-preserve-empty-dirs.sh
index 36c6b1a12ff..9cf7a1427ab 100755
--- a/t/t9160-git-svn-preserve-empty-dirs.sh
+++ b/t/t9160-git-svn-preserve-empty-dirs.sh
@@ -9,6 +9,7 @@ This test uses git to clone a Subversion repository that contains empty
 directories, and checks that corresponding directories are created in the
 local Git repository with placeholder files.'
 
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 GIT_REPO=git-svn-repo
diff --git a/t/t9162-git-svn-dcommit-interactive.sh b/t/t9162-git-svn-dcommit-interactive.sh
index e38d9fa37b5..e2aa8ed88a9 100755
--- a/t/t9162-git-svn-dcommit-interactive.sh
+++ b/t/t9162-git-svn-dcommit-interactive.sh
@@ -3,6 +3,8 @@
 # Copyright (c) 2011 Frédéric Heitzmann
 
 test_description='git svn dcommit --interactive series'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize repo' '
diff --git a/t/t9164-git-svn-dcommit-concurrent.sh b/t/t9164-git-svn-dcommit-concurrent.sh
index 8466269bf50..1465156072e 100755
--- a/t/t9164-git-svn-dcommit-concurrent.sh
+++ b/t/t9164-git-svn-dcommit-concurrent.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='concurrent git svn dcommit'
+
+TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 
diff --git a/t/t9501-gitweb-standalone-http-status.sh b/t/t9501-gitweb-standalone-http-status.sh
index 32814e75df5..c900231079c 100755
--- a/t/t9501-gitweb-standalone-http-status.sh
+++ b/t/t9501-gitweb-standalone-http-status.sh
@@ -13,6 +13,7 @@ code and message.'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./lib-gitweb.sh
 
 #
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 19/27] revisions API: have release_revisions() release "filter"
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (17 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 18/27] revisions API: have release_revisions() release "cmdline" Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 20/27] revisions API: have release_revisions() release "grep_filter" Ævar Arnfjörð Bjarmason
                             ` (7 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"filter" in the "struct rev_info". This in combination with a
preceding change to free "cmdline" means that we can mark another set
of tests as passing under "TEST_PASSES_SANITIZE_LEAK=true".

The "filter" member was added recently in ffaa137f646 (revision: put
object filter into struct rev_info, 2022-03-09), and this fixes leaks
intruded in the subsequent leak 7940941de1f (pack-objects: use
rev.filter when possible, 2022-03-09) and 105c6f14ad3 (bundle: parse
filter capability, 2022-03-09).

The "builtin/pack-objects.c" leak in 7940941de1f was effectively with
us already, but the variable was referred to by a "static" file-scoped
variable. The "bundle.c " leak in 105c6f14ad3 was newly introduced
with the new "filter" feature for bundles.

The "t5600-clone-fail-cleanup.sh" change here to add
"TEST_PASSES_SANITIZE_LEAK=true" is one of the cases where
run-command.c in not carrying the abort() exit code upwards would have
had that test passing before, but now it *actually* passes[1]. We
should fix the lack of 1=1 mapping of SANITIZE=leak testing to actual
leaks some other time, but it's an existing edge case, let's just mark
the really-passing test as passing for now.

1. https://lore.kernel.org/git/220303.86fsnz5o9w.gmgdl@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c                         | 1 +
 t/t1060-object-corruption.sh       | 1 +
 t/t2015-checkout-unborn.sh         | 1 +
 t/t4207-log-decoration-colors.sh   | 1 +
 t/t5301-sliding-window.sh          | 2 ++
 t/t5313-pack-bounds-checks.sh      | 2 ++
 t/t5316-pack-delta-depth.sh        | 2 ++
 t/t5320-delta-islands.sh           | 2 ++
 t/t5322-pack-objects-sparse.sh     | 1 +
 t/t5506-remote-groups.sh           | 1 +
 t/t5513-fetch-track.sh             | 1 +
 t/t5532-fetch-proxy.sh             | 2 ++
 t/t5600-clone-fail-cleanup.sh      | 1 +
 t/t5900-repo-selection.sh          | 2 ++
 t/t6101-rev-parse-parents.sh       | 1 +
 t/t6114-keep-packs.sh              | 2 ++
 t/t7702-repack-cyclic-alternate.sh | 2 ++
 t/t9127-git-svn-partial-rebuild.sh | 1 -
 18 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/revision.c b/revision.c
index de4076e77de..a9d6d3a8dca 100644
--- a/revision.c
+++ b/revision.c
@@ -2948,6 +2948,7 @@ void release_revisions(struct rev_info *revs)
 	free_commit_list(revs->commits);
 	object_array_clear(&revs->pending);
 	release_revisions_cmdline(&revs->cmdline);
+	list_objects_filter_release(&revs->filter);
 	release_revisions_mailmap(revs->mailmap);
 }
 
diff --git a/t/t1060-object-corruption.sh b/t/t1060-object-corruption.sh
index e8a58b15897..5b8e47e346c 100755
--- a/t/t1060-object-corruption.sh
+++ b/t/t1060-object-corruption.sh
@@ -2,6 +2,7 @@
 
 test_description='see how we handle various forms of corruption'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # convert "1234abcd" to ".git/objects/12/34abcd"
diff --git a/t/t2015-checkout-unborn.sh b/t/t2015-checkout-unborn.sh
index a9721215fab..9425aae6395 100755
--- a/t/t2015-checkout-unborn.sh
+++ b/t/t2015-checkout-unborn.sh
@@ -4,6 +4,7 @@ test_description='checkout from unborn branch'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t4207-log-decoration-colors.sh b/t/t4207-log-decoration-colors.sh
index b8709424981..36ac6aff1e4 100755
--- a/t/t4207-log-decoration-colors.sh
+++ b/t/t4207-log-decoration-colors.sh
@@ -8,6 +8,7 @@ test_description='Test for "git log --decorate" colors'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t5301-sliding-window.sh b/t/t5301-sliding-window.sh
index 76f9798ab95..3ccaaeb3977 100755
--- a/t/t5301-sliding-window.sh
+++ b/t/t5301-sliding-window.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='mmap sliding window tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success \
diff --git a/t/t5313-pack-bounds-checks.sh b/t/t5313-pack-bounds-checks.sh
index 535313e4dc8..cc4cfaa9d37 100755
--- a/t/t5313-pack-bounds-checks.sh
+++ b/t/t5313-pack-bounds-checks.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='bounds-checking of access to mmapped on-disk file formats'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 clear_base () {
diff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh
index e9045009a11..eb4ef3dda4d 100755
--- a/t/t5316-pack-delta-depth.sh
+++ b/t/t5316-pack-delta-depth.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='pack-objects breaks long cross-pack delta chains'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # This mirrors a repeated push setup:
diff --git a/t/t5320-delta-islands.sh b/t/t5320-delta-islands.sh
index fea92a5777f..124d47603df 100755
--- a/t/t5320-delta-islands.sh
+++ b/t/t5320-delta-islands.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='exercise delta islands'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # returns true iff $1 is a delta based on $2
diff --git a/t/t5322-pack-objects-sparse.sh b/t/t5322-pack-objects-sparse.sh
index d39958c066d..770695c9278 100755
--- a/t/t5322-pack-objects-sparse.sh
+++ b/t/t5322-pack-objects-sparse.sh
@@ -4,6 +4,7 @@ test_description='pack-objects object selection using sparse algorithm'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup repo' '
diff --git a/t/t5506-remote-groups.sh b/t/t5506-remote-groups.sh
index 8f150c0793e..5bac03ede81 100755
--- a/t/t5506-remote-groups.sh
+++ b/t/t5506-remote-groups.sh
@@ -4,6 +4,7 @@ test_description='git remote group handling'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 mark() {
diff --git a/t/t5513-fetch-track.sh b/t/t5513-fetch-track.sh
index 65d1e05bd62..c46c4dbaefc 100755
--- a/t/t5513-fetch-track.sh
+++ b/t/t5513-fetch-track.sh
@@ -2,6 +2,7 @@
 
 test_description='fetch follows remote-tracking branches correctly'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t5532-fetch-proxy.sh b/t/t5532-fetch-proxy.sh
index 9c2798603b4..d664912799b 100755
--- a/t/t5532-fetch-proxy.sh
+++ b/t/t5532-fetch-proxy.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='fetching via git:// using core.gitproxy'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup remote repo' '
diff --git a/t/t5600-clone-fail-cleanup.sh b/t/t5600-clone-fail-cleanup.sh
index 34b3df40275..c814afa5656 100755
--- a/t/t5600-clone-fail-cleanup.sh
+++ b/t/t5600-clone-fail-cleanup.sh
@@ -13,6 +13,7 @@ Unless the directory already exists, in which case we clean up only what we
 wrote.
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 corrupt_repo () {
diff --git a/t/t5900-repo-selection.sh b/t/t5900-repo-selection.sh
index 14e59c5b3e4..a84faac242d 100755
--- a/t/t5900-repo-selection.sh
+++ b/t/t5900-repo-selection.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='selecting remote repo in ambiguous cases'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 reset() {
diff --git a/t/t6101-rev-parse-parents.sh b/t/t6101-rev-parse-parents.sh
index c571fa51797..a3a41c7a3e4 100755
--- a/t/t6101-rev-parse-parents.sh
+++ b/t/t6101-rev-parse-parents.sh
@@ -8,6 +8,7 @@ test_description='Test git rev-parse with different parent options'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_cmp_rev_output () {
diff --git a/t/t6114-keep-packs.sh b/t/t6114-keep-packs.sh
index 9239d8aa46d..44246f8a63e 100755
--- a/t/t6114-keep-packs.sh
+++ b/t/t6114-keep-packs.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='rev-list with .keep packs'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t7702-repack-cyclic-alternate.sh b/t/t7702-repack-cyclic-alternate.sh
index 93b74867ac8..f3cdb98eec2 100755
--- a/t/t7702-repack-cyclic-alternate.sh
+++ b/t/t7702-repack-cyclic-alternate.sh
@@ -4,6 +4,8 @@
 #
 
 test_description='repack involving cyclic alternate'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t9127-git-svn-partial-rebuild.sh b/t/t9127-git-svn-partial-rebuild.sh
index 90b1b30dde5..97f495bd49b 100755
--- a/t/t9127-git-svn-partial-rebuild.sh
+++ b/t/t9127-git-svn-partial-rebuild.sh
@@ -5,7 +5,6 @@
 
 test_description='git svn partial-rebuild tests'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'initialize svnrepo' '
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 20/27] revisions API: have release_revisions() release "grep_filter"
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (18 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 19/27] revisions API: have release_revisions() release "filter" Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 21/27] revisions API: have release_revisions() release "prune_data" Ævar Arnfjörð Bjarmason
                             ` (6 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"grep_filter" in the "struct rev_info".This allows us to mark a test
as passing under "TEST_PASSES_SANITIZE_LEAK=true".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c               | 1 +
 t/t9151-svn-mergeinfo.sh | 1 -
 2 files changed, 1 insertion(+), 1 deletion(-)

diff --git a/revision.c b/revision.c
index a9d6d3a8dca..1db58c3e4db 100644
--- a/revision.c
+++ b/revision.c
@@ -2950,6 +2950,7 @@ void release_revisions(struct rev_info *revs)
 	release_revisions_cmdline(&revs->cmdline);
 	list_objects_filter_release(&revs->filter);
 	release_revisions_mailmap(revs->mailmap);
+	free_grep_patterns(&revs->grep_filter);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
diff --git a/t/t9151-svn-mergeinfo.sh b/t/t9151-svn-mergeinfo.sh
index 85221d439bd..c93a5beab25 100755
--- a/t/t9151-svn-mergeinfo.sh
+++ b/t/t9151-svn-mergeinfo.sh
@@ -5,7 +5,6 @@
 
 test_description='git-svn svn mergeinfo properties'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'load svn dump' "
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 21/27] revisions API: have release_revisions() release "prune_data"
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (19 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 20/27] revisions API: have release_revisions() release "grep_filter" Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 22/27] revisions API: clear "boundary_commits" in release_revisions() Ævar Arnfjörð Bjarmason
                             ` (5 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"prune_data" in the "struct rev_info". This means that any code that
calls "release_revisions()" already can get rid of adjacent calls to
clear_pathspec().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 add-interactive.c | 2 --
 builtin/add.c     | 1 -
 builtin/stash.c   | 2 --
 diff-lib.c        | 1 -
 revision.c        | 1 +
 wt-status.c       | 2 --
 6 files changed, 1 insertion(+), 8 deletions(-)

diff --git a/add-interactive.c b/add-interactive.c
index 54cdfc82017..6047e8f6489 100644
--- a/add-interactive.c
+++ b/add-interactive.c
@@ -568,8 +568,6 @@ static int get_modified_files(struct repository *r,
 			run_diff_files(&rev, 0);
 		}
 
-		if (ps)
-			clear_pathspec(&rev.prune_data);
 		release_revisions(&rev);
 	}
 	hashmap_clear_and_free(&s.file_map, struct pathname_entry, ent);
diff --git a/builtin/add.c b/builtin/add.c
index 115a26ea633..fc729e14c17 100644
--- a/builtin/add.c
+++ b/builtin/add.c
@@ -142,7 +142,6 @@ int add_files_to_cache(const char *prefix,
 	rev.diffopt.flags.override_submodule_config = 1;
 	rev.max_count = 0; /* do not compare unmerged paths with stage #2 */
 	run_diff_files(&rev, DIFF_RACY_IS_MODIFIED);
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 	return !!data.add_errors;
 }
diff --git a/builtin/stash.c b/builtin/stash.c
index fce4393d123..16aad4b4d35 100644
--- a/builtin/stash.c
+++ b/builtin/stash.c
@@ -1064,7 +1064,6 @@ static int check_changes_tracked_files(const struct pathspec *ps)
 	}
 
 done:
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 	return ret;
 }
@@ -1276,7 +1275,6 @@ static int stash_working_tree(struct stash_info *info, const struct pathspec *ps
 
 done:
 	discard_index(&istate);
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 	strbuf_release(&diff_output);
 	remove_path(stash_index_path.buf);
diff --git a/diff-lib.c b/diff-lib.c
index 0f16281253f..298265e5b54 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -641,7 +641,6 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
 
 	if (diff_cache(&revs, tree_oid, NULL, 1))
 		exit(128);
-	clear_pathspec(&revs.prune_data);
 	release_revisions(&revs);
 	return 0;
 }
diff --git a/revision.c b/revision.c
index 1db58c3e4db..6f444eaaeba 100644
--- a/revision.c
+++ b/revision.c
@@ -2949,6 +2949,7 @@ void release_revisions(struct rev_info *revs)
 	object_array_clear(&revs->pending);
 	release_revisions_cmdline(&revs->cmdline);
 	list_objects_filter_release(&revs->filter);
+	clear_pathspec(&revs->prune_data);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
 }
diff --git a/wt-status.c b/wt-status.c
index a14fad1e03a..61e0c1022f5 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -616,7 +616,6 @@ static void wt_status_collect_changes_worktree(struct wt_status *s)
 	rev.diffopt.rename_score = s->rename_score >= 0 ? s->rename_score : rev.diffopt.rename_score;
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_files(&rev, 0);
-	clear_pathspec(&rev.prune_data);
 	release_revisions(&rev);
 }
 
@@ -664,7 +663,6 @@ static void wt_status_collect_changes_index(struct wt_status *s)
 	copy_pathspec(&rev.prune_data, &s->pathspec);
 	run_diff_index(&rev, 1);
 	release_revisions(&rev);
-	clear_pathspec(&rev.prune_data);
 }
 
 static int add_file_to_list(const struct object_id *oid,
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 22/27] revisions API: clear "boundary_commits" in release_revisions()
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (20 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 21/27] revisions API: have release_revisions() release "prune_data" Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 23/27] revisions API: release "reflog_info" in release revisions() Ævar Arnfjörð Bjarmason
                             ` (4 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Clear the "boundary_commits" object_array in release_revisions(). This
makes a few more tests pass under SANITIZE=leak, including
"t/t4126-apply-empty.sh" which started failed as an UNLEAK() in
cmd_format_patch() was removed in a preceding commit.

This also re-marks the various tests relying on "git format-patch" as
passing under "SANITIZE=leak", in the preceding "revisions API users:
use release_revisions() in builtin/log.c" commit those were marked as
failing as we removed the UNLEAK(rev) from cmd_format_patch() in
"builtin/log.c".

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c                           | 1 +
 t/t4021-format-patch-numbered.sh     | 1 +
 t/t4028-format-patch-mime-headers.sh | 2 ++
 t/t4036-format-patch-signer-mime.sh  | 1 +
 t/t4122-apply-symlink-inside.sh      | 1 +
 t/t4126-apply-empty.sh               | 1 +
 t/t6110-rev-list-sparse.sh           | 1 +
 t/t9001-send-email.sh                | 1 +
 t/t9116-git-svn-log.sh               | 1 -
 9 files changed, 9 insertions(+), 1 deletion(-)

diff --git a/revision.c b/revision.c
index 6f444eaaeba..5aa6dec4532 100644
--- a/revision.c
+++ b/revision.c
@@ -2947,6 +2947,7 @@ void release_revisions(struct rev_info *revs)
 {
 	free_commit_list(revs->commits);
 	object_array_clear(&revs->pending);
+	object_array_clear(&revs->boundary_commits);
 	release_revisions_cmdline(&revs->cmdline);
 	list_objects_filter_release(&revs->filter);
 	clear_pathspec(&revs->prune_data);
diff --git a/t/t4021-format-patch-numbered.sh b/t/t4021-format-patch-numbered.sh
index 9be65fd4440..1219aa226dc 100755
--- a/t/t4021-format-patch-numbered.sh
+++ b/t/t4021-format-patch-numbered.sh
@@ -5,6 +5,7 @@
 
 test_description='Format-patch numbering options'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4028-format-patch-mime-headers.sh b/t/t4028-format-patch-mime-headers.sh
index 204ba673cb5..60cb819c42e 100755
--- a/t/t4028-format-patch-mime-headers.sh
+++ b/t/t4028-format-patch-mime-headers.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='format-patch mime headers and extra headers do not conflict'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'create commit with utf-8 body' '
diff --git a/t/t4036-format-patch-signer-mime.sh b/t/t4036-format-patch-signer-mime.sh
index 98d9713d8b2..48655bcc789 100755
--- a/t/t4036-format-patch-signer-mime.sh
+++ b/t/t4036-format-patch-signer-mime.sh
@@ -2,6 +2,7 @@
 
 test_description='format-patch -s should force MIME encoding as needed'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4122-apply-symlink-inside.sh b/t/t4122-apply-symlink-inside.sh
index aa52de401b9..96965373036 100755
--- a/t/t4122-apply-symlink-inside.sh
+++ b/t/t4122-apply-symlink-inside.sh
@@ -4,6 +4,7 @@ test_description='apply to deeper directory without getting fooled with symlink'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4126-apply-empty.sh b/t/t4126-apply-empty.sh
index 66a7ba8ab8f..ece9fae207d 100755
--- a/t/t4126-apply-empty.sh
+++ b/t/t4126-apply-empty.sh
@@ -2,6 +2,7 @@
 
 test_description='apply empty'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t6110-rev-list-sparse.sh b/t/t6110-rev-list-sparse.sh
index 13c1da53528..ddefc7f24ee 100755
--- a/t/t6110-rev-list-sparse.sh
+++ b/t/t6110-rev-list-sparse.sh
@@ -4,6 +4,7 @@ test_description='operations that cull histories in unusual ways'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t9001-send-email.sh b/t/t9001-send-email.sh
index 42694fe5841..01c74b8b075 100755
--- a/t/t9001-send-email.sh
+++ b/t/t9001-send-email.sh
@@ -4,6 +4,7 @@ test_description='git send-email'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # May be altered later in the test
diff --git a/t/t9116-git-svn-log.sh b/t/t9116-git-svn-log.sh
index 34f6c80dea3..d74d7b2de68 100755
--- a/t/t9116-git-svn-log.sh
+++ b/t/t9116-git-svn-log.sh
@@ -5,7 +5,6 @@
 
 test_description='git svn log tests'
 
-TEST_FAILS_SANITIZE_LEAK=true
 . ./lib-git-svn.sh
 
 test_expect_success 'setup repository and import' '
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 23/27] revisions API: release "reflog_info" in release revisions()
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (21 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 22/27] revisions API: clear "boundary_commits" in release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-13 20:01           ` [PATCH v6 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release() Ævar Arnfjörð Bjarmason
                             ` (3 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Add a missing reflog_walk_info_release() to "reflog-walk.c" and use it
in release_revisions().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 reflog-walk.c            | 24 +++++++++++++++++++++++-
 reflog-walk.h            |  1 +
 revision.c               |  1 +
 t/t0100-previous.sh      |  1 +
 t/t1401-symbolic-ref.sh  |  2 ++
 t/t1411-reflog-show.sh   |  1 +
 t/t1412-reflog-loop.sh   |  2 ++
 t/t1415-worktree-refs.sh |  1 +
 8 files changed, 32 insertions(+), 1 deletion(-)

diff --git a/reflog-walk.c b/reflog-walk.c
index 8ac4b284b6b..7aa6595a51f 100644
--- a/reflog-walk.c
+++ b/reflog-walk.c
@@ -8,7 +8,7 @@
 
 struct complete_reflogs {
 	char *ref;
-	const char *short_ref;
+	char *short_ref;
 	struct reflog_info {
 		struct object_id ooid, noid;
 		char *email;
@@ -51,9 +51,16 @@ static void free_complete_reflog(struct complete_reflogs *array)
 	}
 	free(array->items);
 	free(array->ref);
+	free(array->short_ref);
 	free(array);
 }
 
+static void complete_reflogs_clear(void *util, const char *str)
+{
+	struct complete_reflogs *array = util;
+	free_complete_reflog(array);
+}
+
 static struct complete_reflogs *read_complete_reflog(const char *ref)
 {
 	struct complete_reflogs *reflogs =
@@ -116,6 +123,21 @@ void init_reflog_walk(struct reflog_walk_info **info)
 	(*info)->complete_reflogs.strdup_strings = 1;
 }
 
+void reflog_walk_info_release(struct reflog_walk_info *info)
+{
+	size_t i;
+
+	if (!info)
+		return;
+
+	for (i = 0; i < info->nr; i++)
+		free(info->logs[i]);
+	string_list_clear_func(&info->complete_reflogs,
+			       complete_reflogs_clear);
+	free(info->logs);
+	free(info);
+}
+
 int add_reflog_for_walk(struct reflog_walk_info *info,
 		struct commit *commit, const char *name)
 {
diff --git a/reflog-walk.h b/reflog-walk.h
index e9e00ffd479..8076f10d9fb 100644
--- a/reflog-walk.h
+++ b/reflog-walk.h
@@ -8,6 +8,7 @@ struct reflog_walk_info;
 struct date_mode;
 
 void init_reflog_walk(struct reflog_walk_info **info);
+void reflog_walk_info_release(struct reflog_walk_info *info);
 int add_reflog_for_walk(struct reflog_walk_info *info,
 			struct commit *commit, const char *name);
 void show_reflog_message(struct reflog_walk_info *info, int,
diff --git a/revision.c b/revision.c
index 5aa6dec4532..8cd849aa2b9 100644
--- a/revision.c
+++ b/revision.c
@@ -2953,6 +2953,7 @@ void release_revisions(struct rev_info *revs)
 	clear_pathspec(&revs->prune_data);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
+	reflog_walk_info_release(revs->reflog_info);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
diff --git a/t/t0100-previous.sh b/t/t0100-previous.sh
index 69beb59f627..a16cc3d2983 100755
--- a/t/t0100-previous.sh
+++ b/t/t0100-previous.sh
@@ -5,6 +5,7 @@ test_description='previous branch syntax @{-n}'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'branch -d @{-1}' '
diff --git a/t/t1401-symbolic-ref.sh b/t/t1401-symbolic-ref.sh
index 132a1b885ac..9fb0b90f252 100755
--- a/t/t1401-symbolic-ref.sh
+++ b/t/t1401-symbolic-ref.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='basic symbolic-ref tests'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # If the tests munging HEAD fail, they can break detection of
diff --git a/t/t1411-reflog-show.sh b/t/t1411-reflog-show.sh
index 975c4ea83a8..da581ec19ac 100755
--- a/t/t1411-reflog-show.sh
+++ b/t/t1411-reflog-show.sh
@@ -4,6 +4,7 @@ test_description='Test reflog display routines'
 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
diff --git a/t/t1412-reflog-loop.sh b/t/t1412-reflog-loop.sh
index 977603f7f1c..ff30874f940 100755
--- a/t/t1412-reflog-loop.sh
+++ b/t/t1412-reflog-loop.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='reflog walk shows repeated commits again'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup commits' '
diff --git a/t/t1415-worktree-refs.sh b/t/t1415-worktree-refs.sh
index a3e6ea08088..3b531842dd4 100755
--- a/t/t1415-worktree-refs.sh
+++ b/t/t1415-worktree-refs.sh
@@ -2,6 +2,7 @@
 
 test_description='per-worktree refs'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success 'setup' '
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release()
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (22 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 23/27] revisions API: release "reflog_info" in release revisions() Ævar Arnfjörð Bjarmason
@ 2022-04-13 20:01           ` Ævar Arnfjörð Bjarmason
  2022-04-14  5:56           ` [PATCH v6 25/27] revisions API: have release_revisions() release "date_mode" Ævar Arnfjörð Bjarmason
                             ` (2 subsequent siblings)
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-13 20:01 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Call diff_free() on the "pruning" member of "struct rev_info".  Doing
so makes several tests pass under SANITIZE=leak.

This was also the last missing piece that allows us to remove the
UNLEAK() in "cmd_diff" and "cmd_diff_index", which allows us to use
those commands as a canary for general leaks in the revisions API. See
[1] for further rationale, and 886e1084d78 (builtin/: add UNLEAKs,
2017-10-01) for the commit that added the UNLEAK() there.

1. https://lore.kernel.org/git/220218.861r00ib86.gmgdl@evledraar.gmail.com/

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 builtin/diff-index.c                | 1 -
 builtin/diff.c                      | 1 -
 revision.c                          | 1 +
 t/t1001-read-tree-m-2way.sh         | 1 +
 t/t1002-read-tree-m-u-2way.sh       | 1 +
 t/t2200-add-update.sh               | 1 +
 t/t4039-diff-assume-unchanged.sh    | 1 +
 t/t4206-log-follow-harder-copies.sh | 2 ++
 t/t6131-pathspec-icase.sh           | 2 ++
 9 files changed, 9 insertions(+), 2 deletions(-)

diff --git a/builtin/diff-index.c b/builtin/diff-index.c
index 3a83183c312..7d158af6b6d 100644
--- a/builtin/diff-index.c
+++ b/builtin/diff-index.c
@@ -70,7 +70,6 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
 		return -1;
 	}
 	result = run_diff_index(&rev, option);
-	UNLEAK(rev);
 	result = diff_result_code(&rev.diffopt, result);
 	release_revisions(&rev);
 	return result;
diff --git a/builtin/diff.c b/builtin/diff.c
index dd48336da56..f539132ac68 100644
--- a/builtin/diff.c
+++ b/builtin/diff.c
@@ -594,7 +594,6 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
 	result = diff_result_code(&rev.diffopt, result);
 	if (1 < rev.diffopt.skip_stat_unmatch)
 		refresh_index_quietly();
-	UNLEAK(rev);
 	release_revisions(&rev);
 	UNLEAK(ent);
 	UNLEAK(blob);
diff --git a/revision.c b/revision.c
index 8cd849aa2b9..63f17c085c9 100644
--- a/revision.c
+++ b/revision.c
@@ -2953,6 +2953,7 @@ void release_revisions(struct rev_info *revs)
 	clear_pathspec(&revs->prune_data);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
+	diff_free(&revs->pruning);
 	reflog_walk_info_release(revs->reflog_info);
 }
 
diff --git a/t/t1001-read-tree-m-2way.sh b/t/t1001-read-tree-m-2way.sh
index 0710b1fb1e9..516a6112fdc 100755
--- a/t/t1001-read-tree-m-2way.sh
+++ b/t/t1001-read-tree-m-2way.sh
@@ -21,6 +21,7 @@ In the test, these paths are used:
 	yomin   - not in H or M
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 
diff --git a/t/t1002-read-tree-m-u-2way.sh b/t/t1002-read-tree-m-u-2way.sh
index 46cbd5514a6..bd5313caec9 100755
--- a/t/t1002-read-tree-m-u-2way.sh
+++ b/t/t1002-read-tree-m-u-2way.sh
@@ -9,6 +9,7 @@ This is identical to t1001, but uses -u to update the work tree as well.
 
 '
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-read-tree.sh
 
diff --git a/t/t2200-add-update.sh b/t/t2200-add-update.sh
index 0c38f8e3569..be394f1131a 100755
--- a/t/t2200-add-update.sh
+++ b/t/t2200-add-update.sh
@@ -14,6 +14,7 @@ only the updates to dir/sub.
 Also tested are "git add -u" without limiting, and "git add -u"
 without contents changes, and other conditions'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 test_expect_success setup '
diff --git a/t/t4039-diff-assume-unchanged.sh b/t/t4039-diff-assume-unchanged.sh
index 0eb0314a8b3..78090e6852d 100755
--- a/t/t4039-diff-assume-unchanged.sh
+++ b/t/t4039-diff-assume-unchanged.sh
@@ -2,6 +2,7 @@
 
 test_description='diff with assume-unchanged entries'
 
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 # external diff has been tested in t4020-diff-external.sh
diff --git a/t/t4206-log-follow-harder-copies.sh b/t/t4206-log-follow-harder-copies.sh
index 4871a5dc92f..33ecf82c7f9 100755
--- a/t/t4206-log-follow-harder-copies.sh
+++ b/t/t4206-log-follow-harder-copies.sh
@@ -6,6 +6,8 @@
 test_description='Test --follow should always find copies hard in git log.
 
 '
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 . "$TEST_DIRECTORY"/lib-diff.sh
 
diff --git a/t/t6131-pathspec-icase.sh b/t/t6131-pathspec-icase.sh
index 39fc3f6769b..770cce026cb 100755
--- a/t/t6131-pathspec-icase.sh
+++ b/t/t6131-pathspec-icase.sh
@@ -1,6 +1,8 @@
 #!/bin/sh
 
 test_description='test case insensitive pathspec limiting'
+
+TEST_PASSES_SANITIZE_LEAK=true
 . ./test-lib.sh
 
 if test_have_prereq CASE_INSENSITIVE_FS
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 25/27] revisions API: have release_revisions() release "date_mode"
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (23 preceding siblings ...)
  2022-04-13 20:01           ` [PATCH v6 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release() Ævar Arnfjörð Bjarmason
@ 2022-04-14  5:56           ` Ævar Arnfjörð Bjarmason
  2022-04-14  5:56           ` [PATCH v6 26/27] revisions API: have release_revisions() release "topo_walk_info" Ævar Arnfjörð Bjarmason
  2022-04-14  5:56           ` [PATCH v6 27/27] revisions API: add a TODO for diff_free(&revs->diffopt) Ævar Arnfjörð Bjarmason
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-14  5:56 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Extend the the release_revisions() function so that it frees the
"date_mode" in the "struct ref_info".

This uses the date_mode_release() function added in 974c919d36d (date
API: add and use a date_mode_release(), 2022-02-16). As that commit
notes "t7004-tag.sh" tests for the leaks that are being fixed
here. That test now fails "only" 44 tests, instead of the 46 it failed
before this change.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/revision.c b/revision.c
index 63f17c085c9..307f41e8891 100644
--- a/revision.c
+++ b/revision.c
@@ -2951,6 +2951,7 @@ void release_revisions(struct rev_info *revs)
 	release_revisions_cmdline(&revs->cmdline);
 	list_objects_filter_release(&revs->filter);
 	clear_pathspec(&revs->prune_data);
+	date_mode_release(&revs->date_mode);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
 	diff_free(&revs->pruning);
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 26/27] revisions API: have release_revisions() release "topo_walk_info"
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (24 preceding siblings ...)
  2022-04-14  5:56           ` [PATCH v6 25/27] revisions API: have release_revisions() release "date_mode" Ævar Arnfjörð Bjarmason
@ 2022-04-14  5:56           ` Ævar Arnfjörð Bjarmason
  2022-04-14  5:56           ` [PATCH v6 27/27] revisions API: add a TODO for diff_free(&revs->diffopt) Ævar Arnfjörð Bjarmason
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-14  5:56 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Refactor the existing reset_topo_walk() into a thin wrapper for a
release_revisions_topo_walk_info() + resetting the member to "NULL",
and call release_revisions_topo_walk_info() from release_revisions().

This fixes memory leaks that have been with us ever since
"topo_walk_info" was added to revision.[ch] in
f0d9cc4196a (revision.c: begin refactoring --topo-order logic,
2018-11-01).

Due to various other leaks this makes no tests pass in their entirety,
but e.g. before this running this on git.git:

    ./git -P log --pretty=tformat:"%P   %H | %s" --parents --full-history --topo-order -3 -- README.md

Would report under SANITIZE=leak:

    SUMMARY: LeakSanitizer: 531064 byte(s) leaked in 6 allocation(s).

Now we'll free all of that memory.

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 revision.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/revision.c b/revision.c
index 307f41e8891..0107ac1077a 100644
--- a/revision.c
+++ b/revision.c
@@ -2943,6 +2943,8 @@ static void release_revisions_mailmap(struct string_list *mailmap)
 	free(mailmap);
 }
 
+static void release_revisions_topo_walk_info(struct topo_walk_info *info);
+
 void release_revisions(struct rev_info *revs)
 {
 	free_commit_list(revs->commits);
@@ -2956,6 +2958,7 @@ void release_revisions(struct rev_info *revs)
 	free_grep_patterns(&revs->grep_filter);
 	diff_free(&revs->pruning);
 	reflog_walk_info_release(revs->reflog_info);
+	release_revisions_topo_walk_info(revs->topo_walk_info);
 }
 
 static void add_child(struct rev_info *revs, struct commit *parent, struct commit *child)
@@ -3468,17 +3471,22 @@ static void compute_indegrees_to_depth(struct rev_info *revs,
 		indegree_walk_step(revs);
 }
 
-static void reset_topo_walk(struct rev_info *revs)
+static void release_revisions_topo_walk_info(struct topo_walk_info *info)
 {
-	struct topo_walk_info *info = revs->topo_walk_info;
-
+	if (!info)
+		return;
 	clear_prio_queue(&info->explore_queue);
 	clear_prio_queue(&info->indegree_queue);
 	clear_prio_queue(&info->topo_queue);
 	clear_indegree_slab(&info->indegree);
 	clear_author_date_slab(&info->author_date);
+	free(info);
+}
 
-	FREE_AND_NULL(revs->topo_walk_info);
+static void reset_topo_walk(struct rev_info *revs)
+{
+	release_revisions_topo_walk_info(revs->topo_walk_info);
+	revs->topo_walk_info = NULL;
 }
 
 static void init_topo_walk(struct rev_info *revs)
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* [PATCH v6 27/27] revisions API: add a TODO for diff_free(&revs->diffopt)
  2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
                             ` (25 preceding siblings ...)
  2022-04-14  5:56           ` [PATCH v6 26/27] revisions API: have release_revisions() release "topo_walk_info" Ævar Arnfjörð Bjarmason
@ 2022-04-14  5:56           ` Ævar Arnfjörð Bjarmason
  26 siblings, 0 replies; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-04-14  5:56 UTC (permalink / raw)
  To: git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson, Phillip Wood,
	Ævar Arnfjörð Bjarmason

Add a TODO comment indicating that we should release "diffopt" in
release_revisions(). In a preceding commit we started releasing the
"pruning" member of the same type, but handling "diffopt" will require
us to untangle the "no_free" conditions I added in e900d494dcf (diff:
add an API for deferred freeing, 2021-02-11).

Let's leave a TODO comment to that effect, and so that we don't forget
refactor code that was changed to use release_revisions() in earlier
commits to stop using the "diffopt" member after a call to
release_revisions(). This works currently, but would become a logic
error as soon as we started freeing "diffopt". Doing that change now
doesn't harm anything, and future-proofs us against a later change to
release_revisions().

Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
---
 diff-lib.c  | 4 +++-
 revision.c  | 1 +
 wt-status.c | 6 ++++--
 3 files changed, 8 insertions(+), 3 deletions(-)

diff --git a/diff-lib.c b/diff-lib.c
index 298265e5b54..7eb66a417aa 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -651,6 +651,7 @@ int index_differs_from(struct repository *r,
 {
 	struct rev_info rev;
 	struct setup_revision_opt opt;
+	unsigned has_changes;
 
 	repo_init_revisions(r, &rev, NULL);
 	memset(&opt, 0, sizeof(opt));
@@ -662,8 +663,9 @@ int index_differs_from(struct repository *r,
 		diff_flags_or(&rev.diffopt.flags, flags);
 	rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
 	run_diff_index(&rev, 1);
+	has_changes = rev.diffopt.flags.has_changes;
 	release_revisions(&rev);
-	return (rev.diffopt.flags.has_changes != 0);
+	return (has_changes != 0);
 }
 
 static struct strbuf *idiff_prefix_cb(struct diff_options *opt, void *data)
diff --git a/revision.c b/revision.c
index 0107ac1077a..58d6212221b 100644
--- a/revision.c
+++ b/revision.c
@@ -2956,6 +2956,7 @@ void release_revisions(struct rev_info *revs)
 	date_mode_release(&revs->date_mode);
 	release_revisions_mailmap(revs->mailmap);
 	free_grep_patterns(&revs->grep_filter);
+	/* TODO (need to handle "no_free"): diff_free(&revs->diffopt) */
 	diff_free(&revs->pruning);
 	reflog_walk_info_release(revs->reflog_info);
 	release_revisions_topo_walk_info(revs->topo_walk_info);
diff --git a/wt-status.c b/wt-status.c
index 61e0c1022f5..102d904adcb 100644
--- a/wt-status.c
+++ b/wt-status.c
@@ -2545,8 +2545,9 @@ int has_unstaged_changes(struct repository *r, int ignore_submodules)
 	rev_info.diffopt.flags.quick = 1;
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_files(&rev_info, 0);
+	result = diff_result_code(&rev_info.diffopt, result);
 	release_revisions(&rev_info);
-	return diff_result_code(&rev_info.diffopt, result);
+	return result;
 }
 
 /**
@@ -2578,8 +2579,9 @@ int has_uncommitted_changes(struct repository *r,
 
 	diff_setup_done(&rev_info.diffopt);
 	result = run_diff_index(&rev_info, 1);
+	result = diff_result_code(&rev_info.diffopt, result);
 	release_revisions(&rev_info);
-	return diff_result_code(&rev_info.diffopt, result);
+	return result;
 }
 
 /**
-- 
2.36.0.rc2.843.g193535c2aa7


^ permalink raw reply related	[flat|nested] 252+ messages in thread

* Re: [PATCH v6 14/27] revisions API users: use release_revisions() with UNLEAK()
  2022-04-13 20:01           ` [PATCH v6 14/27] revisions API users: use release_revisions() with UNLEAK() Ævar Arnfjörð Bjarmason
@ 2022-04-15 13:58             ` Phillip Wood
  0 siblings, 0 replies; 252+ messages in thread
From: Phillip Wood @ 2022-04-15 13:58 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason, git
  Cc: Junio C Hamano, Martin Ågren, Elijah Newren, Derrick Stolee,
	brian m . carlson

Hi Ævar

On 13/04/2022 21:01, Ævar Arnfjörð Bjarmason wrote:
> Use a release_revisions() with those "struct rev_list" users which
> already "UNLEAK" the struct. It may seem odd to simultaneously attempt
> to free() memory, but also to explicitly ignore whether we have memory
> leaks in the same.
> 
> As explained in preceding commits this is being done to use the
> built-in commands as a guinea pig for whether the release_revisions()
> function works as expected, we'd like to test e.g. whether we segfault
> as we change it. In subsequent commits we'll then remove these
> UNLEAK() as the function is made to free the memory that caused us to
> add them in the first place.

It would have been nice to reword this to avoid the confusion that 
prompted my comments on the previous version. Saying "As explained in 
the preceding commits" is confusing when those commits remove UNLEAK() 
and this commit leaves it in. I now understand why you are leaving 
UNLEAK() here thanks to your explanation on the previous series and I 
think it makes sense to do so but I don't think this message does a good 
job of explaining it.

The rest of the series looks good

Best Wishes

Phillip

> Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
> ---
>   builtin/diff-index.c | 4 +++-
>   builtin/diff.c       | 1 +
>   2 files changed, 4 insertions(+), 1 deletion(-)
> 
> diff --git a/builtin/diff-index.c b/builtin/diff-index.c
> index 5fd23ab5b6c..3a83183c312 100644
> --- a/builtin/diff-index.c
> +++ b/builtin/diff-index.c
> @@ -71,5 +71,7 @@ int cmd_diff_index(int argc, const char **argv, const char *prefix)
>   	}
>   	result = run_diff_index(&rev, option);
>   	UNLEAK(rev);
> -	return diff_result_code(&rev.diffopt, result);
> +	result = diff_result_code(&rev.diffopt, result);
> +	release_revisions(&rev);
> +	return result;
>   }
> diff --git a/builtin/diff.c b/builtin/diff.c
> index bb7fafca618..dd48336da56 100644
> --- a/builtin/diff.c
> +++ b/builtin/diff.c
> @@ -595,6 +595,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
>   	if (1 < rev.diffopt.skip_stat_unmatch)
>   		refresh_index_quietly();
>   	UNLEAK(rev);
> +	release_revisions(&rev);
>   	UNLEAK(ent);
>   	UNLEAK(blob);
>   	return result;

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v6 11/27] revisions API users: add "goto cleanup" for release_revisions()
  2022-04-13 20:01           ` [PATCH v6 11/27] revisions API users: add "goto cleanup" for release_revisions() Ævar Arnfjörð Bjarmason
@ 2022-07-11 18:06             ` Jeff King
  2022-07-11 20:12               ` Ævar Arnfjörð Bjarmason
  0 siblings, 1 reply; 252+ messages in thread
From: Jeff King @ 2022-07-11 18:06 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren,
	Derrick Stolee, brian m . carlson, Phillip Wood

On Wed, Apr 13, 2022 at 10:01:40PM +0200, Ævar Arnfjörð Bjarmason wrote:

> diff --git a/builtin/diff-files.c b/builtin/diff-files.c
> index 70103c40952..2bfaf9ba7ae 100644
> --- a/builtin/diff-files.c
> +++ b/builtin/diff-files.c
> @@ -77,8 +77,12 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
>  
>  	if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
>  		perror("read_cache_preload");
> -		return -1;
> +		result = -1;
> +		goto cleanup;
>  	}
> +cleanup:
>  	result = run_diff_files(&rev, options);
> -	return diff_result_code(&rev.diffopt, result);
> +	result = diff_result_code(&rev.diffopt, result);
> +	release_revisions(&rev);
> +	return result;
>  }

A bit late, but I happened to notice Coverity complaining about this
code. And indeed, this patch seems pretty broken. If
read_cache_preload() fails, we assign "-1" to result and jump to
cleanup.

But then the first thing we do in cleanup is overwrite result! That
hides the error (depending on how run_diff_files behaves if the cache
load failed, but one can imagine it thinks there are no files to diff).

Should the cleanup label come after the call to run_diff_files()?

I was also somewhat confused by the double-assignment of "result" in the
cleanup label. But I think that is because diff_result_code() is
massaging the current value of "result" into the right thing. But in
that case, should the "-1" from earlier be passed to diff_result_code()?
I think probably not (and certainly it was not before your patch). Which
would imply that the label should go after that, like:

diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index 2bfaf9ba7a..92cf6e1e92 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -80,9 +80,9 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
 		result = -1;
 		goto cleanup;
 	}
-cleanup:
 	result = run_diff_files(&rev, options);
 	result = diff_result_code(&rev.diffopt, result);
+cleanup:
 	release_revisions(&rev);
 	return result;
 }

-Peff

^ permalink raw reply related	[flat|nested] 252+ messages in thread

* Re: [PATCH v6 11/27] revisions API users: add "goto cleanup" for release_revisions()
  2022-07-11 18:06             ` Jeff King
@ 2022-07-11 20:12               ` Ævar Arnfjörð Bjarmason
  2022-07-12  6:54                 ` Jeff King
  0 siblings, 1 reply; 252+ messages in thread
From: Ævar Arnfjörð Bjarmason @ 2022-07-11 20:12 UTC (permalink / raw)
  To: Jeff King
  Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren,
	Derrick Stolee, brian m . carlson, Phillip Wood


On Mon, Jul 11 2022, Jeff King wrote:

> On Wed, Apr 13, 2022 at 10:01:40PM +0200, Ævar Arnfjörð Bjarmason wrote:
>
>> diff --git a/builtin/diff-files.c b/builtin/diff-files.c
>> index 70103c40952..2bfaf9ba7ae 100644
>> --- a/builtin/diff-files.c
>> +++ b/builtin/diff-files.c
>> @@ -77,8 +77,12 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
>>  
>>  	if (read_cache_preload(&rev.diffopt.pathspec) < 0) {
>>  		perror("read_cache_preload");
>> -		return -1;
>> +		result = -1;
>> +		goto cleanup;
>>  	}
>> +cleanup:
>>  	result = run_diff_files(&rev, options);
>> -	return diff_result_code(&rev.diffopt, result);
>> +	result = diff_result_code(&rev.diffopt, result);
>> +	release_revisions(&rev);
>> +	return result;
>>  }
>
> A bit late, but I happened to notice Coverity complaining about this
> code. And indeed, this patch seems pretty broken. If
> read_cache_preload() fails, we assign "-1" to result and jump to
> cleanup.
>
> But then the first thing we do in cleanup is overwrite result! That
> hides the error (depending on how run_diff_files behaves if the cache
> load failed, but one can imagine it thinks there are no files to diff).
>
> Should the cleanup label come after the call to run_diff_files()?
>
> I was also somewhat confused by the double-assignment of "result" in the
> cleanup label. But I think that is because diff_result_code() is
> massaging the current value of "result" into the right thing. But in
> that case, should the "-1" from earlier be passed to diff_result_code()?
> I think probably not (and certainly it was not before your patch). Which
> would imply that the label should go after that, like:
>
> diff --git a/builtin/diff-files.c b/builtin/diff-files.c
> index 2bfaf9ba7a..92cf6e1e92 100644
> --- a/builtin/diff-files.c
> +++ b/builtin/diff-files.c
> @@ -80,9 +80,9 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
>  		result = -1;
>  		goto cleanup;
>  	}
> -cleanup:
>  	result = run_diff_files(&rev, options);
>  	result = diff_result_code(&rev.diffopt, result);
> +cleanup:
>  	release_revisions(&rev);
>  	return result;
>  }
>
> -Peff

Urgh, yes that's the obviously correct fix to bring it back to the
previous behavior, it's indeed just a misplaced "cleanup" label, sorry
about that.

^ permalink raw reply	[flat|nested] 252+ messages in thread

* Re: [PATCH v6 11/27] revisions API users: add "goto cleanup" for release_revisions()
  2022-07-11 20:12               ` Ævar Arnfjörð Bjarmason
@ 2022-07-12  6:54                 ` Jeff King
  2022-07-12  7:03                   ` [PATCH] diff-files: move misplaced cleanup label Jeff King
  0 siblings, 1 reply; 252+ messages in thread
From: Jeff King @ 2022-07-12  6:54 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren,
	Derrick Stolee, brian m . carlson, Phillip Wood

On Mon, Jul 11, 2022 at 10:12:18PM +0200, Ævar Arnfjörð Bjarmason wrote:

> > I was also somewhat confused by the double-assignment of "result" in the
> > cleanup label. But I think that is because diff_result_code() is
> > massaging the current value of "result" into the right thing. But in
> > that case, should the "-1" from earlier be passed to diff_result_code()?
> > I think probably not (and certainly it was not before your patch). Which
> > would imply that the label should go after that, like:
> [...]
> 
> Urgh, yes that's the obviously correct fix to bring it back to the
> previous behavior, it's indeed just a misplaced "cleanup" label, sorry
> about that.

Thanks, I'll work it up into a real patch. Which I should have just done
in the first place; I just hadn't convinced myself I understood
everything until I finished writing the other email. :)

-Peff

^ permalink raw reply	[flat|nested] 252+ messages in thread

* [PATCH] diff-files: move misplaced cleanup label
  2022-07-12  6:54                 ` Jeff King
@ 2022-07-12  7:03                   ` Jeff King
  0 siblings, 0 replies; 252+ messages in thread
From: Jeff King @ 2022-07-12  7:03 UTC (permalink / raw)
  To: Ævar Arnfjörð Bjarmason
  Cc: git, Junio C Hamano, Martin Ågren, Elijah Newren,
	Derrick Stolee, brian m . carlson, Phillip Wood


Commit 0139c58ab9 (revisions API users: add "goto cleanup" for
release_revisions(), 2022-04-13) converted an early return in
cmd_diff_files() into a goto. But it put the cleanup label too early: if
read_cache_preload() returns an error, we'll set result to "-1", but
then jump to calling run_diff_files(), overwriting our result.

We should jump past the call to run_diff_files(). Likewise, we should go
past diff_result_code(), which is expecting to see a code from an actual
diff, not a negative error code.

In practice, I suspect this bug cannot actually be triggered, because
read_cache_preload() does not seem to ever return an error. Its return
value (eventually) comes from do_read_index(), which gives the number of
cache entries found, and calls die() on error. Still, it makes sense to
fix the inadvertent change from 0139c58ab9 first, and we can look into
the overall error handling of read_cache() separately (which is present
in many other callsites).

Signed-off-by: Jeff King <peff@peff.net>
---
This is on top of ab/plug-leak-in-revisions, though it's long since
graduated to master.

The read_cache() thing is weird. The error checking was introduced back
in 6304c29d51 (diff-files: do not play --no-index games, 2008-05-23).
And even back then, I don't see it ever returning -1. Yet then, as well
as now, running "git grep 'read_cache.*< 0'" turns up many hits. So I'm
not sure if I'm missing a case, or if it's cargo-culting gone wild.

Still, I don't plan to dig further into it anytime soon. Removing the
error handling from those sites is relatively low reward, with high risk
for introducing a bug.

 builtin/diff-files.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/builtin/diff-files.c b/builtin/diff-files.c
index 2bfaf9ba7a..92cf6e1e92 100644
--- a/builtin/diff-files.c
+++ b/builtin/diff-files.c
@@ -80,9 +80,9 @@ int cmd_diff_files(int argc, const char **argv, const char *prefix)
 		result = -1;
 		goto cleanup;
 	}
-cleanup:
 	result = run_diff_files(&rev, options);
 	result = diff_result_code(&rev.diffopt, result);
+cleanup:
 	release_revisions(&rev);
 	return result;
 }
-- 
2.37.0.497.g676cb53800


^ permalink raw reply related	[flat|nested] 252+ messages in thread

end of thread, other threads:[~2022-07-12  7:04 UTC | newest]

Thread overview: 252+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-03-09 13:16 [PATCH 00/24] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
2022-03-09 13:16 ` [PATCH 01/24] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
2022-03-09 13:16 ` [PATCH 02/24] blame: use "goto cleanup" for cleanup_scoreboard() Ævar Arnfjörð Bjarmason
2022-03-09 13:16 ` [PATCH 03/24] format-patch: don't leak "extra_headers" or "ref_message_ids" Ævar Arnfjörð Bjarmason
2022-03-09 20:34   ` Taylor Blau
2022-03-09 13:16 ` [PATCH 04/24] revision.[ch]: split freeing of revs->commit into a function Ævar Arnfjörð Bjarmason
2022-03-09 13:16 ` [PATCH 05/24] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
2022-03-09 21:32   ` Taylor Blau
2022-03-09 22:57   ` Junio C Hamano
2022-03-10  0:28     ` Ævar Arnfjörð Bjarmason
2022-03-10  1:11       ` Junio C Hamano
2022-03-10 14:57         ` Ævar Arnfjörð Bjarmason
2022-03-10 14:56       ` Ævar Arnfjörð Bjarmason
2022-03-09 13:16 ` [PATCH 06/24] revisions API users: add straightforward release_revisions() Ævar Arnfjörð Bjarmason
2022-03-09 13:16 ` [PATCH 07/24] revisions API users: use release_revisions() needing "{ 0 }" init Ævar Arnfjörð Bjarmason
2022-03-09 13:16 ` [PATCH 08/24] revisions API users: add "goto cleanup" for release_revisions() Ævar Arnfjörð Bjarmason
2022-03-09 19:08   ` Derrick Stolee
2022-03-09 13:16 ` [PATCH 09/24] revisions API users: use release_revisions() in http-push.c Ævar Arnfjörð Bjarmason
2022-03-09 13:16 ` [PATCH 10/24] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
2022-03-09 19:12   ` Derrick Stolee
2022-03-09 21:58     ` Taylor Blau
2022-03-09 21:53   ` Taylor Blau
2022-03-09 13:16 ` [PATCH 11/24] revisions API users: use release_revisions() with UNLEAK() Ævar Arnfjörð Bjarmason
2022-03-09 13:16 ` [PATCH 12/24] revisions API users: use release_revisions() in submodule.c edge case Ævar Arnfjörð Bjarmason
2022-03-09 13:16 ` [PATCH 13/24] revisions API users: use release_revisions() in wt-status.c " Ævar Arnfjörð Bjarmason
2022-03-09 13:16 ` [PATCH 14/24] revisions API users: use release_revisions() for "prune_data" users Ævar Arnfjörð Bjarmason
2022-03-09 13:16 ` [PATCH 15/24] revisions API: have release_revisions() release "commits" Ævar Arnfjörð Bjarmason
2022-03-09 13:16 ` [PATCH 16/24] revisions API: have release_revisions() release "mailmap" Ævar Arnfjörð Bjarmason
2022-03-09 19:17   ` Derrick Stolee
2022-03-09 13:16 ` [PATCH 17/24] revisions API: have release_revisions() release "cmdline" Ævar Arnfjörð Bjarmason
2022-03-09 19:19   ` Derrick Stolee
2022-03-10 15:10     ` Ævar Arnfjörð Bjarmason
2022-03-09 13:16 ` [PATCH 18/24] revisions API: have release_revisions() release "grep_filter" Ævar Arnfjörð Bjarmason
2022-03-09 13:16 ` [PATCH 19/24] revisions API: have release_revisions() release "prune_data" Ævar Arnfjörð Bjarmason
2022-03-09 13:16 ` [PATCH 20/24] revisions API: clear "boundary_commits" in release_revisions() Ævar Arnfjörð Bjarmason
2022-03-09 19:21   ` Derrick Stolee
2022-03-10 14:55     ` Ævar Arnfjörð Bjarmason
2022-03-09 13:16 ` [PATCH 21/24] revisions API: release "reflog_info" in release revisions() Ævar Arnfjörð Bjarmason
2022-03-09 19:24   ` Derrick Stolee
2022-03-10  0:13     ` Ævar Arnfjörð Bjarmason
2022-03-09 13:16 ` [PATCH 22/24] revisions API: call diff_free(&revs->pruning) in revisions_release() Ævar Arnfjörð Bjarmason
2022-03-09 13:16 ` [PATCH 23/24] revisions API: have release_revisions() release "date_mode" Ævar Arnfjörð Bjarmason
2022-03-09 13:16 ` [PATCH 24/24] revisions API: have release_revisions() release "topo_walk_info" Ævar Arnfjörð Bjarmason
2022-03-09 19:27   ` Derrick Stolee
2022-03-09 14:34 ` [PATCH 00/24] revision.[ch]: add and use release_revisions() Derrick Stolee
2022-03-09 19:30   ` Derrick Stolee
2022-03-09 19:39     ` Ævar Arnfjörð Bjarmason
2022-03-09 20:05   ` Junio C Hamano
2022-03-23 20:31 ` [PATCH v2 00/27] " Ævar Arnfjörð Bjarmason
2022-03-23 20:31   ` [PATCH v2 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
2022-03-24  4:13     ` Junio C Hamano
2022-03-24 16:57       ` Ævar Arnfjörð Bjarmason
2022-03-24 17:34         ` Junio C Hamano
2022-03-23 20:31   ` [PATCH v2 02/27] blame: use "goto cleanup" for cleanup_scoreboard() Ævar Arnfjörð Bjarmason
2022-03-24  4:23     ` Junio C Hamano
2022-03-23 20:31   ` [PATCH v2 03/27] string_list API users: use string_list_init_{no,}dup Ævar Arnfjörð Bjarmason
2022-03-23 20:31   ` [PATCH v2 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids" Ævar Arnfjörð Bjarmason
2022-03-24  4:33     ` Junio C Hamano
2022-03-24 16:59       ` Ævar Arnfjörð Bjarmason
2022-03-23 20:31   ` [PATCH v2 05/27] revision.[ch]: split freeing of revs->commit into a function Ævar Arnfjörð Bjarmason
2022-03-24  4:33     ` Junio C Hamano
2022-03-24 17:01       ` Ævar Arnfjörð Bjarmason
2022-03-23 20:31   ` [PATCH v2 06/27] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
2022-03-24  4:43     ` Junio C Hamano
2022-03-24 17:02       ` Ævar Arnfjörð Bjarmason
2022-03-23 20:31   ` [PATCH v2 07/27] revisions API users: add straightforward release_revisions() Ævar Arnfjörð Bjarmason
2022-03-23 20:31   ` [PATCH v2 08/27] revisions API users: use release_revisions() needing "{ 0 }" init Ævar Arnfjörð Bjarmason
2022-03-24  4:53     ` Junio C Hamano
2022-03-24 17:04       ` Ævar Arnfjörð Bjarmason
2022-03-24 17:39         ` Junio C Hamano
2022-03-25  0:47           ` Junio C Hamano
2022-03-23 20:31   ` [PATCH v2 09/27] stash: always have the owner of "stash_info" free it Ævar Arnfjörð Bjarmason
2022-03-25  0:51     ` Junio C Hamano
2022-03-23 20:32   ` [PATCH v2 10/27] revisions API users: add "goto cleanup" for release_revisions() Ævar Arnfjörð Bjarmason
2022-03-25  0:52     ` Junio C Hamano
2022-03-23 20:32   ` [PATCH v2 11/27] revisions API users: use release_revisions() in http-push.c Ævar Arnfjörð Bjarmason
2022-03-23 20:32   ` [PATCH v2 12/27] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
2022-03-25  1:03     ` Junio C Hamano
2022-03-23 20:32   ` [PATCH v2 13/27] revisions API users: use release_revisions() with UNLEAK() Ævar Arnfjörð Bjarmason
2022-03-23 20:32   ` [PATCH v2 14/27] revisions API users: use release_revisions() in submodule.c edge case Ævar Arnfjörð Bjarmason
2022-03-25  1:07     ` Junio C Hamano
2022-03-23 20:32   ` [PATCH v2 15/27] revisions API users: use release_revisions() for "prune_data" users Ævar Arnfjörð Bjarmason
2022-03-23 20:32   ` [PATCH v2 16/27] revisions API: have release_revisions() release "commits" Ævar Arnfjörð Bjarmason
2022-03-23 20:32   ` [PATCH v2 17/27] revisions API: have release_revisions() release "mailmap" Ævar Arnfjörð Bjarmason
2022-03-25  1:11     ` Junio C Hamano
2022-03-23 20:32   ` [PATCH v2 18/27] revisions API: have release_revisions() release "cmdline" Ævar Arnfjörð Bjarmason
2022-03-25  1:16     ` Junio C Hamano
2022-03-23 20:32   ` [PATCH v2 19/27] revisions API: have release_revisions() release "filter" Ævar Arnfjörð Bjarmason
2022-03-25  1:17     ` Junio C Hamano
2022-03-23 20:32   ` [PATCH v2 20/27] revisions API: have release_revisions() release "grep_filter" Ævar Arnfjörð Bjarmason
2022-03-23 20:32   ` [PATCH v2 21/27] revisions API: have release_revisions() release "prune_data" Ævar Arnfjörð Bjarmason
2022-03-25  1:20     ` Junio C Hamano
2022-03-23 20:32   ` [PATCH v2 22/27] revisions API: clear "boundary_commits" in release_revisions() Ævar Arnfjörð Bjarmason
2022-03-23 20:32   ` [PATCH v2 23/27] revisions API: release "reflog_info" in release revisions() Ævar Arnfjörð Bjarmason
2022-03-23 20:32   ` [PATCH v2 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release() Ævar Arnfjörð Bjarmason
2022-03-23 20:32   ` [PATCH v2 25/27] revisions API: have release_revisions() release "date_mode" Ævar Arnfjörð Bjarmason
2022-03-23 20:32   ` [PATCH v2 26/27] revisions API: have release_revisions() release "topo_walk_info" Ævar Arnfjörð Bjarmason
2022-03-23 20:32   ` [PATCH v2 27/27] revisions API: add a TODO for diff_free(&revs->diffopt) Ævar Arnfjörð Bjarmason
2022-03-25  1:41   ` [PATCH v2 00/27] revision.[ch]: add and use release_revisions() Junio C Hamano
2022-03-25  9:14     ` Ævar Arnfjörð Bjarmason
2022-03-25 17:18   ` [PATCH v3 " Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 02/27] blame: use "goto cleanup" for cleanup_scoreboard() Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 03/27] string_list API users: use string_list_init_{no,}dup Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids" Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 05/27] revision.[ch]: split freeing of revs->commit into a function Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 06/27] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 07/27] revisions API users: add straightforward release_revisions() Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 08/27] revisions API users: add "goto cleanup" for "rev_info" early exit Ævar Arnfjörð Bjarmason
2022-03-25 20:30       ` Junio C Hamano
2022-03-26  0:37         ` Ævar Arnfjörð Bjarmason
2022-03-26  5:24           ` Junio C Hamano
2022-03-28 17:55             ` Derrick Stolee
2022-03-28 18:55               ` Ævar Arnfjörð Bjarmason
2022-03-28 20:03                 ` Junio C Hamano
2022-03-25 17:18     ` [PATCH v3 09/27] revisions API users: use release_revisions() in submodule.c edge case Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 10/27] stash: always have the owner of "stash_info" free it Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 11/27] revisions API users: add "goto cleanup" for release_revisions() Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 12/27] revisions API users: use release_revisions() in http-push.c Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 13/27] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 14/27] revisions API users: use release_revisions() with UNLEAK() Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 15/27] revisions API users: use release_revisions() for "prune_data" users Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 16/27] revisions API: have release_revisions() release "commits" Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 17/27] revisions API: have release_revisions() release "mailmap" Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 18/27] revisions API: have release_revisions() release "cmdline" Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 19/27] revisions API: have release_revisions() release "filter" Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 20/27] revisions API: have release_revisions() release "grep_filter" Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 21/27] revisions API: have release_revisions() release "prune_data" Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 22/27] revisions API: clear "boundary_commits" in release_revisions() Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 23/27] revisions API: release "reflog_info" in release revisions() Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release() Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 25/27] revisions API: have release_revisions() release "date_mode" Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 26/27] revisions API: have release_revisions() release "topo_walk_info" Ævar Arnfjörð Bjarmason
2022-03-25 17:18     ` [PATCH v3 27/27] revisions API: add a TODO for diff_free(&revs->diffopt) Ævar Arnfjörð Bjarmason
2022-03-31  1:11     ` [PATCH v4 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 02/27] blame: use "goto cleanup" for cleanup_scoreboard() Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 03/27] string_list API users: use string_list_init_{no,}dup Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids" Ævar Arnfjörð Bjarmason
2022-04-01 15:13         ` Phillip Wood
2022-04-01 17:16           ` Ævar Arnfjörð Bjarmason
2022-04-01 19:32             ` Junio C Hamano
2022-03-31  1:11       ` [PATCH v4 05/27] revision.[ch]: split freeing of revs->commit into a function Ævar Arnfjörð Bjarmason
2022-04-01 15:15         ` Phillip Wood
2022-04-01 17:03           ` Junio C Hamano
2022-03-31  1:11       ` [PATCH v4 06/27] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
2022-04-01 15:17         ` Phillip Wood
2022-04-01 17:25           ` Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 07/27] revisions API users: add straightforward release_revisions() Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 08/27] revision.[ch]: document and move code declared around "init" Ævar Arnfjörð Bjarmason
2022-04-01 15:20         ` Phillip Wood
2022-03-31  1:11       ` [PATCH v4 09/27] revisions API users: use release_revisions() needing REV_INFO_INIT Ævar Arnfjörð Bjarmason
2022-03-31 21:14         ` Junio C Hamano
2022-04-01 10:31           ` Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 10/27] stash: always have the owner of "stash_info" free it Ævar Arnfjörð Bjarmason
2022-04-01 15:29         ` Phillip Wood
2022-04-01 17:29           ` Ævar Arnfjörð Bjarmason
2022-04-01 15:33         ` Phillip Wood
2022-03-31  1:11       ` [PATCH v4 11/27] revisions API users: add "goto cleanup" for release_revisions() Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 12/27] revisions API users: use release_revisions() in http-push.c Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 13/27] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
2022-04-02  9:22         ` Phillip Wood
2022-04-03 14:07           ` Ævar Arnfjörð Bjarmason
2022-04-04  9:27             ` Phillip Wood
2022-04-03 21:49           ` Junio C Hamano
2022-03-31  1:11       ` [PATCH v4 14/27] revisions API users: use release_revisions() with UNLEAK() Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 15/27] revisions API users: use release_revisions() for "prune_data" users Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 16/27] revisions API: have release_revisions() release "commits" Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 17/27] revisions API: have release_revisions() release "mailmap" Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 18/27] revisions API: have release_revisions() release "cmdline" Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 19/27] revisions API: have release_revisions() release "filter" Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 20/27] revisions API: have release_revisions() release "grep_filter" Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 21/27] revisions API: have release_revisions() release "prune_data" Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 22/27] revisions API: clear "boundary_commits" in release_revisions() Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 23/27] revisions API: release "reflog_info" in release revisions() Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release() Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 25/27] revisions API: have release_revisions() release "date_mode" Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 26/27] revisions API: have release_revisions() release "topo_walk_info" Ævar Arnfjörð Bjarmason
2022-03-31  1:11       ` [PATCH v4 27/27] revisions API: add a TODO for diff_free(&revs->diffopt) Ævar Arnfjörð Bjarmason
2022-04-02 10:49       ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 02/27] blame: use "goto cleanup" for cleanup_scoreboard() Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 03/27] string_list API users: use string_list_init_{no,}dup Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids" Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 05/27] cocci: add and apply free_commit_list() rules Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 06/27] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 07/27] revisions API users: add straightforward release_revisions() Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 08/27] revision.[ch]: document and move code declared around "init" Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 09/27] revisions API users: use release_revisions() needing REV_INFO_INIT Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 10/27] stash: always have the owner of "stash_info" free it Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 11/27] revisions API users: add "goto cleanup" for release_revisions() Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 12/27] revisions API users: use release_revisions() in http-push.c Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 13/27] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 14/27] revisions API users: use release_revisions() with UNLEAK() Ævar Arnfjörð Bjarmason
2022-04-03  9:27           ` Phillip Wood
2022-04-03 13:55             ` Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 15/27] revisions API users: use release_revisions() for "prune_data" users Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 16/27] revisions API: have release_revisions() release "commits" Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 17/27] revisions API: have release_revisions() release "mailmap" Ævar Arnfjörð Bjarmason
2022-04-03  9:33           ` Phillip Wood
2022-04-03 13:57             ` Ævar Arnfjörð Bjarmason
2022-04-04  9:19               ` Phillip Wood
2022-04-02 10:49         ` [PATCH v5 18/27] revisions API: have release_revisions() release "cmdline" Ævar Arnfjörð Bjarmason
2022-04-03 12:54           ` Phillip Wood
2022-04-03 14:01             ` Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 19/27] revisions API: have release_revisions() release "filter" Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 20/27] revisions API: have release_revisions() release "grep_filter" Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 21/27] revisions API: have release_revisions() release "prune_data" Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 22/27] revisions API: clear "boundary_commits" in release_revisions() Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 23/27] revisions API: release "reflog_info" in release revisions() Ævar Arnfjörð Bjarmason
2022-04-03 15:19           ` Phillip Wood
2022-04-02 10:49         ` [PATCH v5 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release() Ævar Arnfjörð Bjarmason
2022-04-03 13:00           ` Phillip Wood
2022-04-03 14:04             ` Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 25/27] revisions API: have release_revisions() release "date_mode" Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 26/27] revisions API: have release_revisions() release "topo_walk_info" Ævar Arnfjörð Bjarmason
2022-04-02 10:49         ` [PATCH v5 27/27] revisions API: add a TODO for diff_free(&revs->diffopt) Ævar Arnfjörð Bjarmason
2022-04-03 13:07         ` [PATCH v5 00/27] revision.[ch]: add and use release_revisions() Phillip Wood
2022-04-03 14:18           ` Ævar Arnfjörð Bjarmason
2022-04-13 20:01         ` [PATCH v6 " Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 01/27] t/helper/test-fast-rebase.c: don't leak "struct strbuf" Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 02/27] blame: use "goto cleanup" for cleanup_scoreboard() Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 03/27] string_list API users: use string_list_init_{no,}dup Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 04/27] format-patch: don't leak "extra_headers" or "ref_message_ids" Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 05/27] cocci: add and apply free_commit_list() rules Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 06/27] revision.[ch]: provide and start using a release_revisions() Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 07/27] revisions API users: add straightforward release_revisions() Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 08/27] revision.[ch]: document and move code declared around "init" Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 09/27] revisions API users: use release_revisions() needing REV_INFO_INIT Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 10/27] stash: always have the owner of "stash_info" free it Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 11/27] revisions API users: add "goto cleanup" for release_revisions() Ævar Arnfjörð Bjarmason
2022-07-11 18:06             ` Jeff King
2022-07-11 20:12               ` Ævar Arnfjörð Bjarmason
2022-07-12  6:54                 ` Jeff King
2022-07-12  7:03                   ` [PATCH] diff-files: move misplaced cleanup label Jeff King
2022-04-13 20:01           ` [PATCH v6 12/27] revisions API users: use release_revisions() in http-push.c Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 13/27] revisions API users: use release_revisions() in builtin/log.c Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 14/27] revisions API users: use release_revisions() with UNLEAK() Ævar Arnfjörð Bjarmason
2022-04-15 13:58             ` Phillip Wood
2022-04-13 20:01           ` [PATCH v6 15/27] revisions API users: use release_revisions() for "prune_data" users Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 16/27] revisions API: have release_revisions() release "commits" Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 17/27] revisions API: have release_revisions() release "mailmap" Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 18/27] revisions API: have release_revisions() release "cmdline" Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 19/27] revisions API: have release_revisions() release "filter" Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 20/27] revisions API: have release_revisions() release "grep_filter" Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 21/27] revisions API: have release_revisions() release "prune_data" Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 22/27] revisions API: clear "boundary_commits" in release_revisions() Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 23/27] revisions API: release "reflog_info" in release revisions() Ævar Arnfjörð Bjarmason
2022-04-13 20:01           ` [PATCH v6 24/27] revisions API: call diff_free(&revs->pruning) in revisions_release() Ævar Arnfjörð Bjarmason
2022-04-14  5:56           ` [PATCH v6 25/27] revisions API: have release_revisions() release "date_mode" Ævar Arnfjörð Bjarmason
2022-04-14  5:56           ` [PATCH v6 26/27] revisions API: have release_revisions() release "topo_walk_info" Ævar Arnfjörð Bjarmason
2022-04-14  5:56           ` [PATCH v6 27/27] revisions API: add a TODO for diff_free(&revs->diffopt) Ævar Arnfjörð Bjarmason

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).