diff options
author | Ævar Arnfjörð Bjarmason <avarab@gmail.com> | 2022-04-13 23:01:35 +0300 |
---|---|---|
committer | Junio C Hamano <gitster@pobox.com> | 2022-04-14 09:56:08 +0300 |
commit | 1878b5edc03c4bf685c7278fc4ced4704c876984 (patch) | |
tree | c45566b464c5eeff933565d35224ac8ddd113055 | |
parent | bf20fe4ca8c8d2ba2087c9c44b4ee09a1d1467ed (diff) |
revision.[ch]: provide and start using a release_revisions()
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>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
-rw-r--r-- | builtin/checkout.c | 2 | ||||
-rw-r--r-- | builtin/stash.c | 5 | ||||
-rw-r--r-- | diff-lib.c | 2 | ||||
-rw-r--r-- | range-diff.c | 2 | ||||
-rw-r--r-- | revision.c | 5 | ||||
-rw-r--r-- | revision.h | 6 | ||||
-rw-r--r-- | wt-status.c | 5 |
7 files changed, 19 insertions, 8 deletions
diff --git a/builtin/checkout.c b/builtin/checkout.c index 797681481d..4d9e0bd3ac 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 ccdfdab44b..8c90db479c 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 ca085a03ef..d6800274bd 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 b72eb9fdbe..39cc010c62 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 4963ba7def..5dd4b2e910 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 5bc59c7bfe..61c780fc4c 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 d33f9272b7..922cf787f9 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); } |