We currently have seven callers of `reduce_heads(foo)`. Six of them do
not use the original list `foo` again, and actually, all six of those
end up leaking it.

Introduce and use `reduce_heads_replace(&foo)` as a leak-free version of
`foo = reduce_heads(foo)` to fix several of these. Fix the remaining
leaks using `free_commit_list()`.

While we're here, document `reduce_heads()` and mark it as `extern`.

Signed-off-by: Martin Ågren <[email protected]>
Signed-off-by: Junio C Hamano <[email protected]>
---
v3: Like v2 but rebased onto maint.

 commit.h                | 18 +++++++++++++++++-
 builtin/commit.c        |  2 +-
 builtin/fmt-merge-msg.c |  2 +-
 builtin/merge-base.c    |  6 ++++--
 builtin/merge.c         |  1 +
 builtin/pull.c          |  5 ++++-
 commit.c                |  7 +++++++
 7 files changed, 35 insertions(+), 6 deletions(-)

diff --git a/commit.h b/commit.h
index f3805fec6b..cfa2636e33 100644
--- a/commit.h
+++ b/commit.h
@@ -313,7 +313,23 @@ extern int interactive_add(int argc, const char **argv, 
const char *prefix, int
 extern int run_add_interactive(const char *revision, const char *patch_mode,
                               const struct pathspec *pathspec);
 
-struct commit_list *reduce_heads(struct commit_list *heads);
+/*
+ * Takes a list of commits and returns a new list where those
+ * have been removed that can be reached from other commits in
+ * the list. It is useful for, e.g., reducing the commits
+ * randomly thrown at the git-merge command and removing
+ * redundant commits that the user shouldn't have given to it.
+ *
+ * This function destroys the STALE bit of the commit objects'
+ * flags.
+ */
+extern struct commit_list *reduce_heads(struct commit_list *heads);
+
+/*
+ * Like `reduce_heads()`, except it replaces the list. Use this
+ * instead of `foo = reduce_heads(foo);` to avoid memory leaks.
+ */
+extern void reduce_heads_replace(struct commit_list **heads);
 
 struct commit_extra_header {
        struct commit_extra_header *next;
diff --git a/builtin/commit.c b/builtin/commit.c
index 1a0da71a43..73c4ec2b08 100644
--- a/builtin/commit.c
+++ b/builtin/commit.c
@@ -1719,7 +1719,7 @@ int cmd_commit(int argc, const char **argv, const char 
*prefix)
                                allow_fast_forward = 0;
                }
                if (allow_fast_forward)
-                       parents = reduce_heads(parents);
+                       reduce_heads_replace(&parents);
        } else {
                if (!reflog_msg)
                        reflog_msg = (whence == FROM_CHERRY_PICK)
diff --git a/builtin/fmt-merge-msg.c b/builtin/fmt-merge-msg.c
index e99b5ddbf9..27a2361e91 100644
--- a/builtin/fmt-merge-msg.c
+++ b/builtin/fmt-merge-msg.c
@@ -571,7 +571,7 @@ static void find_merge_parents(struct merge_parents *result,
        head_commit = lookup_commit(head);
        if (head_commit)
                commit_list_insert(head_commit, &parents);
-       parents = reduce_heads(parents);
+       reduce_heads_replace(&parents);
 
        while (parents) {
                struct commit *cmit = pop_commit(&parents);
diff --git a/builtin/merge-base.c b/builtin/merge-base.c
index e17835fabb..24f6c71935 100644
--- a/builtin/merge-base.c
+++ b/builtin/merge-base.c
@@ -57,7 +57,7 @@ static int handle_independent(int count, const char **args)
        for (i = count - 1; i >= 0; i--)
                commit_list_insert(get_commit_reference(args[i]), &revs);
 
-       revs = reduce_heads(revs);
+       reduce_heads_replace(&revs);
 
        if (!revs)
                return 1;
@@ -78,7 +78,9 @@ static int handle_octopus(int count, const char **args, int 
show_all)
        for (i = count - 1; i >= 0; i--)
                commit_list_insert(get_commit_reference(args[i]), &revs);
 
-       result = reduce_heads(get_octopus_merge_bases(revs));
+       result = get_octopus_merge_bases(revs);
+       free_commit_list(revs);
+       reduce_heads_replace(&result);
 
        if (!result)
                return 1;
diff --git a/builtin/merge.c b/builtin/merge.c
index 23c53a3082..5cccf21c30 100644
--- a/builtin/merge.c
+++ b/builtin/merge.c
@@ -981,6 +981,7 @@ static struct commit_list *reduce_parents(struct commit 
*head_commit,
 
        /* Find what parents to record by checking independent ones. */
        parents = reduce_heads(remoteheads);
+       free_commit_list(remoteheads);
 
        remoteheads = NULL;
        remotes = &remoteheads;
diff --git a/builtin/pull.c b/builtin/pull.c
index 9b86e519b1..a5dd6426d5 100644
--- a/builtin/pull.c
+++ b/builtin/pull.c
@@ -741,12 +741,15 @@ static int get_octopus_merge_base(struct object_id 
*merge_base,
        if (!is_null_oid(fork_point))
                commit_list_insert(lookup_commit_reference(fork_point), &revs);
 
-       result = reduce_heads(get_octopus_merge_bases(revs));
+       result = get_octopus_merge_bases(revs);
        free_commit_list(revs);
+       reduce_heads_replace(&result);
+
        if (!result)
                return 1;
 
        oidcpy(merge_base, &result->item->object.oid);
+       free_commit_list(result);
        return 0;
 }
 
diff --git a/commit.c b/commit.c
index d3150d6270..7ff102c0df 100644
--- a/commit.c
+++ b/commit.c
@@ -1083,6 +1083,13 @@ struct commit_list *reduce_heads(struct commit_list 
*heads)
        return result;
 }
 
+void reduce_heads_replace(struct commit_list **heads)
+{
+       struct commit_list *result = reduce_heads(*heads);
+       free_commit_list(*heads);
+       *heads = result;
+}
+
 static const char gpg_sig_header[] = "gpgsig";
 static const int gpg_sig_header_len = sizeof(gpg_sig_header) - 1;
 
-- 
2.15.0.415.gac1375d7e

Reply via email to