mirror of
https://github.com/git/git.git
synced 2024-11-23 01:46:13 +08:00
merge: fix leaking merge bases
When calling either the recursive or the ORT merge machineries we need to provide a list of merge bases. The ownership of that parameter is then implicitly transferred to the callee, which is somewhat fishy. Furthermore, that list may leak in some cases where the merge machinery runs into an error, thus causing a memory leak. Refactor the code such that we stop transferring ownership. Instead, the merge machinery will now create its own local copies of the passed in list as required if they need to modify the list. Free the list at the callsites as required. Signed-off-by: Patrick Steinhardt <ps@pks.im> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
77241a6b5e
commit
44ec7c575f
@ -482,6 +482,7 @@ static int real_merge(struct merge_tree_options *o,
|
||||
die(_("refusing to merge unrelated histories"));
|
||||
merge_bases = reverse_commit_list(merge_bases);
|
||||
merge_incore_recursive(&opt, merge_bases, parent1, parent2, &result);
|
||||
free_commit_list(merge_bases);
|
||||
}
|
||||
|
||||
if (result.clean < 0)
|
||||
|
@ -746,6 +746,8 @@ static int try_merge_strategy(const char *strategy, struct commit_list *common,
|
||||
else
|
||||
clean = merge_recursive(&o, head, remoteheads->item,
|
||||
reversed, &result);
|
||||
free_commit_list(reversed);
|
||||
|
||||
if (clean < 0) {
|
||||
rollback_lock_file(&lock);
|
||||
return 2;
|
||||
|
2
commit.c
2
commit.c
@ -680,7 +680,7 @@ unsigned commit_list_count(const struct commit_list *l)
|
||||
return c;
|
||||
}
|
||||
|
||||
struct commit_list *copy_commit_list(struct commit_list *list)
|
||||
struct commit_list *copy_commit_list(const struct commit_list *list)
|
||||
{
|
||||
struct commit_list *head = NULL;
|
||||
struct commit_list **pp = &head;
|
||||
|
2
commit.h
2
commit.h
@ -181,7 +181,7 @@ struct commit_list *commit_list_insert_by_date(struct commit *item,
|
||||
void commit_list_sort_by_date(struct commit_list **list);
|
||||
|
||||
/* Shallow copy of the input list */
|
||||
struct commit_list *copy_commit_list(struct commit_list *list);
|
||||
struct commit_list *copy_commit_list(const struct commit_list *list);
|
||||
|
||||
/* Modify list in-place to reverse it, returning new head; list will be tail */
|
||||
struct commit_list *reverse_commit_list(struct commit_list *list);
|
||||
|
@ -1047,6 +1047,7 @@ static int do_remerge_diff(struct rev_info *opt,
|
||||
log_tree_diff_flush(opt);
|
||||
|
||||
/* Cleanup */
|
||||
free_commit_list(bases);
|
||||
cleanup_additional_headers(&opt->diffopt);
|
||||
strbuf_release(&parent1_desc);
|
||||
strbuf_release(&parent2_desc);
|
||||
|
@ -48,7 +48,7 @@ int merge_ort_nonrecursive(struct merge_options *opt,
|
||||
int merge_ort_recursive(struct merge_options *opt,
|
||||
struct commit *side1,
|
||||
struct commit *side2,
|
||||
struct commit_list *merge_bases,
|
||||
const struct commit_list *merge_bases,
|
||||
struct commit **result)
|
||||
{
|
||||
struct tree *head = repo_get_commit_tree(opt->repo, side1);
|
||||
|
@ -19,7 +19,7 @@ int merge_ort_nonrecursive(struct merge_options *opt,
|
||||
int merge_ort_recursive(struct merge_options *opt,
|
||||
struct commit *h1,
|
||||
struct commit *h2,
|
||||
struct commit_list *ancestors,
|
||||
const struct commit_list *ancestors,
|
||||
struct commit **result);
|
||||
|
||||
#endif
|
||||
|
12
merge-ort.c
12
merge-ort.c
@ -5071,11 +5071,12 @@ redo:
|
||||
* Originally from merge_recursive_internal(); somewhat adapted, though.
|
||||
*/
|
||||
static void merge_ort_internal(struct merge_options *opt,
|
||||
struct commit_list *merge_bases,
|
||||
const struct commit_list *_merge_bases,
|
||||
struct commit *h1,
|
||||
struct commit *h2,
|
||||
struct merge_result *result)
|
||||
{
|
||||
struct commit_list *merge_bases = copy_commit_list(_merge_bases);
|
||||
struct commit *next;
|
||||
struct commit *merged_merge_bases;
|
||||
const char *ancestor_name;
|
||||
@ -5085,7 +5086,7 @@ static void merge_ort_internal(struct merge_options *opt,
|
||||
if (repo_get_merge_bases(the_repository, h1, h2,
|
||||
&merge_bases) < 0) {
|
||||
result->clean = -1;
|
||||
return;
|
||||
goto out;
|
||||
}
|
||||
/* See merge-ort.h:merge_incore_recursive() declaration NOTE */
|
||||
merge_bases = reverse_commit_list(merge_bases);
|
||||
@ -5129,7 +5130,7 @@ static void merge_ort_internal(struct merge_options *opt,
|
||||
opt->branch2 = "Temporary merge branch 2";
|
||||
merge_ort_internal(opt, NULL, prev, next, result);
|
||||
if (result->clean < 0)
|
||||
return;
|
||||
goto out;
|
||||
opt->branch1 = saved_b1;
|
||||
opt->branch2 = saved_b2;
|
||||
opt->priv->call_depth--;
|
||||
@ -5152,6 +5153,9 @@ static void merge_ort_internal(struct merge_options *opt,
|
||||
result);
|
||||
strbuf_release(&merge_base_abbrev);
|
||||
opt->ancestor = NULL; /* avoid accidental re-use of opt->ancestor */
|
||||
|
||||
out:
|
||||
free_commit_list(merge_bases);
|
||||
}
|
||||
|
||||
void merge_incore_nonrecursive(struct merge_options *opt,
|
||||
@ -5181,7 +5185,7 @@ void merge_incore_nonrecursive(struct merge_options *opt,
|
||||
}
|
||||
|
||||
void merge_incore_recursive(struct merge_options *opt,
|
||||
struct commit_list *merge_bases,
|
||||
const struct commit_list *merge_bases,
|
||||
struct commit *side1,
|
||||
struct commit *side2,
|
||||
struct merge_result *result)
|
||||
|
@ -59,7 +59,7 @@ struct merge_result {
|
||||
* first", 2006-08-09)
|
||||
*/
|
||||
void merge_incore_recursive(struct merge_options *opt,
|
||||
struct commit_list *merge_bases,
|
||||
const struct commit_list *merge_bases,
|
||||
struct commit *side1,
|
||||
struct commit *side2,
|
||||
struct merge_result *result);
|
||||
|
@ -3633,15 +3633,16 @@ static int merge_trees_internal(struct merge_options *opt,
|
||||
static int merge_recursive_internal(struct merge_options *opt,
|
||||
struct commit *h1,
|
||||
struct commit *h2,
|
||||
struct commit_list *merge_bases,
|
||||
const struct commit_list *_merge_bases,
|
||||
struct commit **result)
|
||||
{
|
||||
struct commit_list *merge_bases = copy_commit_list(_merge_bases);
|
||||
struct commit_list *iter;
|
||||
struct commit *merged_merge_bases;
|
||||
struct tree *result_tree;
|
||||
int clean;
|
||||
const char *ancestor_name;
|
||||
struct strbuf merge_base_abbrev = STRBUF_INIT;
|
||||
int ret;
|
||||
|
||||
if (show(opt, 4)) {
|
||||
output(opt, 4, _("Merging:"));
|
||||
@ -3651,8 +3652,10 @@ static int merge_recursive_internal(struct merge_options *opt,
|
||||
|
||||
if (!merge_bases) {
|
||||
if (repo_get_merge_bases(the_repository, h1, h2,
|
||||
&merge_bases) < 0)
|
||||
return -1;
|
||||
&merge_bases) < 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
merge_bases = reverse_commit_list(merge_bases);
|
||||
}
|
||||
|
||||
@ -3702,14 +3705,18 @@ static int merge_recursive_internal(struct merge_options *opt,
|
||||
opt->branch1 = "Temporary merge branch 1";
|
||||
opt->branch2 = "Temporary merge branch 2";
|
||||
if (merge_recursive_internal(opt, merged_merge_bases, iter->item,
|
||||
NULL, &merged_merge_bases) < 0)
|
||||
return -1;
|
||||
NULL, &merged_merge_bases) < 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
opt->branch1 = saved_b1;
|
||||
opt->branch2 = saved_b2;
|
||||
opt->priv->call_depth--;
|
||||
|
||||
if (!merged_merge_bases)
|
||||
return err(opt, _("merge returned no commit"));
|
||||
if (!merged_merge_bases) {
|
||||
ret = err(opt, _("merge returned no commit"));
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
@ -3726,17 +3733,16 @@ static int merge_recursive_internal(struct merge_options *opt,
|
||||
repo_read_index(opt->repo);
|
||||
|
||||
opt->ancestor = ancestor_name;
|
||||
clean = merge_trees_internal(opt,
|
||||
repo_get_commit_tree(opt->repo, h1),
|
||||
repo_get_commit_tree(opt->repo, h2),
|
||||
repo_get_commit_tree(opt->repo,
|
||||
merged_merge_bases),
|
||||
&result_tree);
|
||||
strbuf_release(&merge_base_abbrev);
|
||||
ret = merge_trees_internal(opt,
|
||||
repo_get_commit_tree(opt->repo, h1),
|
||||
repo_get_commit_tree(opt->repo, h2),
|
||||
repo_get_commit_tree(opt->repo,
|
||||
merged_merge_bases),
|
||||
&result_tree);
|
||||
opt->ancestor = NULL; /* avoid accidental re-use of opt->ancestor */
|
||||
if (clean < 0) {
|
||||
if (ret < 0) {
|
||||
flush_output(opt);
|
||||
return clean;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (opt->priv->call_depth) {
|
||||
@ -3745,7 +3751,11 @@ static int merge_recursive_internal(struct merge_options *opt,
|
||||
commit_list_insert(h1, &(*result)->parents);
|
||||
commit_list_insert(h2, &(*result)->parents->next);
|
||||
}
|
||||
return clean;
|
||||
|
||||
out:
|
||||
strbuf_release(&merge_base_abbrev);
|
||||
free_commit_list(merge_bases);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int merge_start(struct merge_options *opt, struct tree *head)
|
||||
@ -3827,7 +3837,7 @@ int merge_trees(struct merge_options *opt,
|
||||
int merge_recursive(struct merge_options *opt,
|
||||
struct commit *h1,
|
||||
struct commit *h2,
|
||||
struct commit_list *merge_bases,
|
||||
const struct commit_list *merge_bases,
|
||||
struct commit **result)
|
||||
{
|
||||
int clean;
|
||||
@ -3895,6 +3905,7 @@ int merge_recursive_generic(struct merge_options *opt,
|
||||
repo_hold_locked_index(opt->repo, &lock, LOCK_DIE_ON_ERROR);
|
||||
clean = merge_recursive(opt, head_commit, next_commit, ca,
|
||||
result);
|
||||
free_commit_list(ca);
|
||||
if (clean < 0) {
|
||||
rollback_lock_file(&lock);
|
||||
return clean;
|
||||
|
@ -104,7 +104,7 @@ int merge_trees(struct merge_options *opt,
|
||||
int merge_recursive(struct merge_options *opt,
|
||||
struct commit *h1,
|
||||
struct commit *h2,
|
||||
struct commit_list *merge_bases,
|
||||
const struct commit_list *merge_bases,
|
||||
struct commit **result);
|
||||
|
||||
/*
|
||||
|
@ -4315,6 +4315,7 @@ leave_merge:
|
||||
strbuf_release(&ref_name);
|
||||
rollback_lock_file(&lock);
|
||||
free_commit_list(to_merge);
|
||||
free_commit_list(bases);
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
@ -21,6 +21,7 @@ Initial setup:
|
||||
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
|
||||
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
|
||||
|
||||
TEST_PASSES_SANITIZE_LEAK=true
|
||||
. ./test-lib.sh
|
||||
. "$TEST_DIRECTORY"/lib-rebase.sh
|
||||
. "$TEST_DIRECTORY"/lib-log-graph.sh
|
||||
|
@ -4,6 +4,7 @@ test_description='Merge-recursive merging renames'
|
||||
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
|
||||
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
|
||||
|
||||
TEST_PASSES_SANITIZE_LEAK=true
|
||||
. ./test-lib.sh
|
||||
|
||||
modify () {
|
||||
|
@ -5,6 +5,7 @@ test_description='merge-recursive backend test'
|
||||
GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main
|
||||
export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME
|
||||
|
||||
TEST_PASSES_SANITIZE_LEAK=true
|
||||
. ./test-lib.sh
|
||||
. "$TEST_DIRECTORY"/lib-merge.sh
|
||||
|
||||
|
@ -7,6 +7,7 @@ Do not overwrite changes.'
|
||||
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' '
|
||||
|
@ -25,6 +25,7 @@ Next, test git merge --abort with the following variables:
|
||||
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' '
|
||||
|
Loading…
Reference in New Issue
Block a user