Merge branch 'ma/leakplugs'
Memory leaks in various codepaths have been plugged. * ma/leakplugs: pack-bitmap[-write]: use `object_array_clear()`, don't leak object_array: add and use `object_array_pop()` object_array: use `object_array_clear()`, not `free()` leak_pending: use `object_array_clear()`, not `free()` commit: fix memory leak in `reduce_heads()` builtin/commit: fix memory leak in `prepare_index()`
This commit is contained in:
3
bisect.c
3
bisect.c
@ -826,7 +826,8 @@ static int check_ancestors(const char *prefix)
|
|||||||
|
|
||||||
/* Clean up objects used, as they will be reused. */
|
/* Clean up objects used, as they will be reused. */
|
||||||
clear_commit_marks_for_object_array(&pending_copy, ALL_REV_FLAGS);
|
clear_commit_marks_for_object_array(&pending_copy, ALL_REV_FLAGS);
|
||||||
free(pending_copy.objects);
|
|
||||||
|
object_array_clear(&pending_copy);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -797,9 +797,14 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new)
|
|||||||
for_each_ref(add_pending_uninteresting_ref, &revs);
|
for_each_ref(add_pending_uninteresting_ref, &revs);
|
||||||
add_pending_oid(&revs, "HEAD", &new->object.oid, UNINTERESTING);
|
add_pending_oid(&revs, "HEAD", &new->object.oid, UNINTERESTING);
|
||||||
|
|
||||||
|
/* Save pending objects, so they can be cleaned up later. */
|
||||||
refs = revs.pending;
|
refs = revs.pending;
|
||||||
revs.leak_pending = 1;
|
revs.leak_pending = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* prepare_revision_walk (together with .leak_pending = 1) makes us
|
||||||
|
* the sole owner of the list of pending objects.
|
||||||
|
*/
|
||||||
if (prepare_revision_walk(&revs))
|
if (prepare_revision_walk(&revs))
|
||||||
die(_("internal error in revision walk"));
|
die(_("internal error in revision walk"));
|
||||||
if (!(old->object.flags & UNINTERESTING))
|
if (!(old->object.flags & UNINTERESTING))
|
||||||
@ -807,8 +812,10 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new)
|
|||||||
else
|
else
|
||||||
describe_detached_head(_("Previous HEAD position was"), old);
|
describe_detached_head(_("Previous HEAD position was"), old);
|
||||||
|
|
||||||
|
/* Clean up objects used, as they will be reused. */
|
||||||
clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
|
clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
|
||||||
free(refs.objects);
|
|
||||||
|
object_array_clear(&refs);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int switch_branches(const struct checkout_opts *opts,
|
static int switch_branches(const struct checkout_opts *opts,
|
||||||
|
@ -335,7 +335,7 @@ static void refresh_cache_or_die(int refresh_flags)
|
|||||||
static const char *prepare_index(int argc, const char **argv, const char *prefix,
|
static const char *prepare_index(int argc, const char **argv, const char *prefix,
|
||||||
const struct commit *current_head, int is_status)
|
const struct commit *current_head, int is_status)
|
||||||
{
|
{
|
||||||
struct string_list partial;
|
struct string_list partial = STRING_LIST_INIT_DUP;
|
||||||
struct pathspec pathspec;
|
struct pathspec pathspec;
|
||||||
int refresh_flags = REFRESH_QUIET;
|
int refresh_flags = REFRESH_QUIET;
|
||||||
const char *ret;
|
const char *ret;
|
||||||
@ -380,7 +380,8 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
|
|||||||
warning(_("Failed to update main cache tree"));
|
warning(_("Failed to update main cache tree"));
|
||||||
|
|
||||||
commit_style = COMMIT_NORMAL;
|
commit_style = COMMIT_NORMAL;
|
||||||
return get_lock_file_path(&index_lock);
|
ret = get_lock_file_path(&index_lock);
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -403,7 +404,8 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
|
|||||||
if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
|
if (write_locked_index(&the_index, &index_lock, CLOSE_LOCK))
|
||||||
die(_("unable to write new_index file"));
|
die(_("unable to write new_index file"));
|
||||||
commit_style = COMMIT_NORMAL;
|
commit_style = COMMIT_NORMAL;
|
||||||
return get_lock_file_path(&index_lock);
|
ret = get_lock_file_path(&index_lock);
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -429,7 +431,8 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
|
|||||||
rollback_lock_file(&index_lock);
|
rollback_lock_file(&index_lock);
|
||||||
}
|
}
|
||||||
commit_style = COMMIT_AS_IS;
|
commit_style = COMMIT_AS_IS;
|
||||||
return get_index_file();
|
ret = get_index_file();
|
||||||
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -460,7 +463,6 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
|
|||||||
die(_("cannot do a partial commit during a cherry-pick."));
|
die(_("cannot do a partial commit during a cherry-pick."));
|
||||||
}
|
}
|
||||||
|
|
||||||
string_list_init(&partial, 1);
|
|
||||||
if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, &pathspec))
|
if (list_paths(&partial, !current_head ? NULL : "HEAD", prefix, &pathspec))
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
||||||
@ -490,6 +492,9 @@ static const char *prepare_index(int argc, const char **argv, const char *prefix
|
|||||||
discard_cache();
|
discard_cache();
|
||||||
ret = get_lock_file_path(&false_lock);
|
ret = get_lock_file_path(&false_lock);
|
||||||
read_cache_from(ret);
|
read_cache_from(ret);
|
||||||
|
out:
|
||||||
|
string_list_clear(&partial, 0);
|
||||||
|
clear_pathspec(&pathspec);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -650,11 +650,10 @@ static void handle_tail(struct object_array *commits, struct rev_info *revs,
|
|||||||
{
|
{
|
||||||
struct commit *commit;
|
struct commit *commit;
|
||||||
while (commits->nr) {
|
while (commits->nr) {
|
||||||
commit = (struct commit *)commits->objects[commits->nr - 1].item;
|
commit = (struct commit *)object_array_pop(commits);
|
||||||
if (has_unshown_parent(commit))
|
if (has_unshown_parent(commit))
|
||||||
return;
|
return;
|
||||||
handle_commit(commit, revs, paths_of_changed_objects);
|
handle_commit(commit, revs, paths_of_changed_objects);
|
||||||
commits->nr--;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -182,12 +182,7 @@ static int traverse_reachable(void)
|
|||||||
if (show_progress)
|
if (show_progress)
|
||||||
progress = start_delayed_progress(_("Checking connectivity"), 0);
|
progress = start_delayed_progress(_("Checking connectivity"), 0);
|
||||||
while (pending.nr) {
|
while (pending.nr) {
|
||||||
struct object_array_entry *entry;
|
result |= traverse_one_object(object_array_pop(&pending));
|
||||||
struct object *obj;
|
|
||||||
|
|
||||||
entry = pending.objects + --pending.nr;
|
|
||||||
obj = entry->item;
|
|
||||||
result |= traverse_one_object(obj);
|
|
||||||
display_progress(progress, ++nr);
|
display_progress(progress, ++nr);
|
||||||
}
|
}
|
||||||
stop_progress(&progress);
|
stop_progress(&progress);
|
||||||
|
@ -126,7 +126,7 @@ static int commit_is_complete(struct commit *commit)
|
|||||||
struct commit *c;
|
struct commit *c;
|
||||||
struct commit_list *parent;
|
struct commit_list *parent;
|
||||||
|
|
||||||
c = (struct commit *)study.objects[--study.nr].item;
|
c = (struct commit *)object_array_pop(&study);
|
||||||
if (!c->object.parsed && !parse_object(&c->object.oid))
|
if (!c->object.parsed && !parse_object(&c->object.oid))
|
||||||
c->object.flags |= INCOMPLETE;
|
c->object.flags |= INCOMPLETE;
|
||||||
|
|
||||||
@ -182,8 +182,8 @@ static int commit_is_complete(struct commit *commit)
|
|||||||
found.objects[i].item->flags |= SEEN;
|
found.objects[i].item->flags |= SEEN;
|
||||||
}
|
}
|
||||||
/* free object arrays */
|
/* free object arrays */
|
||||||
free(study.objects);
|
object_array_clear(&study);
|
||||||
free(found.objects);
|
object_array_clear(&found);
|
||||||
return !is_incomplete;
|
return !is_incomplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
9
bundle.c
9
bundle.c
@ -157,9 +157,14 @@ int verify_bundle(struct bundle_header *header, int verbose)
|
|||||||
req_nr = revs.pending.nr;
|
req_nr = revs.pending.nr;
|
||||||
setup_revisions(2, argv, &revs, NULL);
|
setup_revisions(2, argv, &revs, NULL);
|
||||||
|
|
||||||
|
/* Save pending objects, so they can be cleaned up later. */
|
||||||
refs = revs.pending;
|
refs = revs.pending;
|
||||||
revs.leak_pending = 1;
|
revs.leak_pending = 1;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* prepare_revision_walk (together with .leak_pending = 1) makes us
|
||||||
|
* the sole owner of the list of pending objects.
|
||||||
|
*/
|
||||||
if (prepare_revision_walk(&revs))
|
if (prepare_revision_walk(&revs))
|
||||||
die(_("revision walk setup failed"));
|
die(_("revision walk setup failed"));
|
||||||
|
|
||||||
@ -176,8 +181,10 @@ int verify_bundle(struct bundle_header *header, int verbose)
|
|||||||
refs.objects[i].name);
|
refs.objects[i].name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Clean up objects used, as they will be reused. */
|
||||||
clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
|
clear_commit_marks_for_object_array(&refs, ALL_REV_FLAGS);
|
||||||
free(refs.objects);
|
|
||||||
|
object_array_clear(&refs);
|
||||||
|
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
struct ref_list *r;
|
struct ref_list *r;
|
||||||
|
1
commit.c
1
commit.c
@ -1086,6 +1086,7 @@ struct commit_list *reduce_heads(struct commit_list *heads)
|
|||||||
num_head = remove_redundant(array, num_head);
|
num_head = remove_redundant(array, num_head);
|
||||||
for (i = 0; i < num_head; i++)
|
for (i = 0; i < num_head; i++)
|
||||||
tail = &commit_list_insert(array[i], tail)->next;
|
tail = &commit_list_insert(array[i], tail)->next;
|
||||||
|
free(array);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -549,7 +549,6 @@ int index_differs_from(const char *def, int diff_flags,
|
|||||||
rev.diffopt.flags |= diff_flags;
|
rev.diffopt.flags |= diff_flags;
|
||||||
rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
|
rev.diffopt.ita_invisible_in_index = ita_invisible_in_index;
|
||||||
run_diff_index(&rev, 1);
|
run_diff_index(&rev, 1);
|
||||||
if (rev.pending.alloc)
|
object_array_clear(&rev.pending);
|
||||||
free(rev.pending.objects);
|
|
||||||
return (DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES) != 0);
|
return (DIFF_OPT_TST(&rev.diffopt, HAS_CHANGES) != 0);
|
||||||
}
|
}
|
||||||
|
13
object.c
13
object.c
@ -353,6 +353,19 @@ static void object_array_release_entry(struct object_array_entry *ent)
|
|||||||
free(ent->path);
|
free(ent->path);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct object *object_array_pop(struct object_array *array)
|
||||||
|
{
|
||||||
|
struct object *ret;
|
||||||
|
|
||||||
|
if (!array->nr)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
ret = array->objects[array->nr - 1].item;
|
||||||
|
object_array_release_entry(&array->objects[array->nr - 1]);
|
||||||
|
array->nr--;
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
void object_array_filter(struct object_array *array,
|
void object_array_filter(struct object_array *array,
|
||||||
object_array_each_func_t want, void *cb_data)
|
object_array_each_func_t want, void *cb_data)
|
||||||
{
|
{
|
||||||
|
8
object.h
8
object.h
@ -116,6 +116,14 @@ int object_list_contains(struct object_list *list, struct object *obj);
|
|||||||
void add_object_array(struct object *obj, const char *name, struct object_array *array);
|
void add_object_array(struct object *obj, const char *name, struct object_array *array);
|
||||||
void add_object_array_with_path(struct object *obj, const char *name, struct object_array *array, unsigned mode, const char *path);
|
void add_object_array_with_path(struct object *obj, const char *name, struct object_array *array, unsigned mode, const char *path);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Returns NULL if the array is empty. Otherwise, returns the last object
|
||||||
|
* after removing its entry from the array. Other resources associated
|
||||||
|
* with that object are left in an unspecified state and should not be
|
||||||
|
* examined.
|
||||||
|
*/
|
||||||
|
struct object *object_array_pop(struct object_array *array);
|
||||||
|
|
||||||
typedef int (*object_array_each_func_t)(struct object_array_entry *, void *);
|
typedef int (*object_array_each_func_t)(struct object_array_entry *, void *);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -297,9 +297,7 @@ void bitmap_writer_build(struct packing_data *to_pack)
|
|||||||
|
|
||||||
traverse_commit_list(&revs, show_commit, show_object, base);
|
traverse_commit_list(&revs, show_commit, show_object, base);
|
||||||
|
|
||||||
revs.pending.nr = 0;
|
object_array_clear(&revs.pending);
|
||||||
revs.pending.alloc = 0;
|
|
||||||
revs.pending.objects = NULL;
|
|
||||||
|
|
||||||
stored->bitmap = bitmap_to_ewah(base);
|
stored->bitmap = bitmap_to_ewah(base);
|
||||||
need_reset = 0;
|
need_reset = 0;
|
||||||
|
@ -654,8 +654,6 @@ static int in_bitmapped_pack(struct object_list *roots)
|
|||||||
int prepare_bitmap_walk(struct rev_info *revs)
|
int prepare_bitmap_walk(struct rev_info *revs)
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
unsigned int pending_nr = revs->pending.nr;
|
|
||||||
struct object_array_entry *pending_e = revs->pending.objects;
|
|
||||||
|
|
||||||
struct object_list *wants = NULL;
|
struct object_list *wants = NULL;
|
||||||
struct object_list *haves = NULL;
|
struct object_list *haves = NULL;
|
||||||
@ -670,8 +668,8 @@ int prepare_bitmap_walk(struct rev_info *revs)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < pending_nr; ++i) {
|
for (i = 0; i < revs->pending.nr; ++i) {
|
||||||
struct object *object = pending_e[i].item;
|
struct object *object = revs->pending.objects[i].item;
|
||||||
|
|
||||||
if (object->type == OBJ_NONE)
|
if (object->type == OBJ_NONE)
|
||||||
parse_object_or_die(&object->oid, NULL);
|
parse_object_or_die(&object->oid, NULL);
|
||||||
@ -715,9 +713,7 @@ int prepare_bitmap_walk(struct rev_info *revs)
|
|||||||
if (!bitmap_git.loaded && load_pack_bitmap() < 0)
|
if (!bitmap_git.loaded && load_pack_bitmap() < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
revs->pending.nr = 0;
|
object_array_clear(&revs->pending);
|
||||||
revs->pending.alloc = 0;
|
|
||||||
revs->pending.objects = NULL;
|
|
||||||
|
|
||||||
if (haves) {
|
if (haves) {
|
||||||
revs->ignore_missing_links = 1;
|
revs->ignore_missing_links = 1;
|
||||||
|
11
revision.h
11
revision.h
@ -150,6 +150,17 @@ struct rev_info {
|
|||||||
date_mode_explicit:1,
|
date_mode_explicit:1,
|
||||||
preserve_subject:1;
|
preserve_subject:1;
|
||||||
unsigned int disable_stdin:1;
|
unsigned int disable_stdin:1;
|
||||||
|
/*
|
||||||
|
* Set `leak_pending` to prevent `prepare_revision_walk()` from clearing
|
||||||
|
* the array of pending objects (`pending`). It will still forget about
|
||||||
|
* the array and its entries, so they really are leaked. This can be
|
||||||
|
* useful if the `struct object_array` `pending` is copied before
|
||||||
|
* calling `prepare_revision_walk()`. By setting `leak_pending`, you
|
||||||
|
* effectively claim ownership of the old array, so you should most
|
||||||
|
* likely call `object_array_clear(&pending_copy)` once you are done.
|
||||||
|
* Observe that this is about ownership of the array and its entries,
|
||||||
|
* not the commits referenced by those entries.
|
||||||
|
*/
|
||||||
unsigned int leak_pending:1;
|
unsigned int leak_pending:1;
|
||||||
/* --show-linear-break */
|
/* --show-linear-break */
|
||||||
unsigned int track_linear:1,
|
unsigned int track_linear:1,
|
||||||
|
@ -99,7 +99,7 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
|
|||||||
cur_depth = 0;
|
cur_depth = 0;
|
||||||
} else {
|
} else {
|
||||||
commit = (struct commit *)
|
commit = (struct commit *)
|
||||||
stack.objects[--stack.nr].item;
|
object_array_pop(&stack);
|
||||||
cur_depth = *(int *)commit->util;
|
cur_depth = *(int *)commit->util;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1685,7 +1685,7 @@ static int find_first_merges(struct object_array *result, const char *path,
|
|||||||
add_object_array(merges.objects[i].item, NULL, result);
|
add_object_array(merges.objects[i].item, NULL, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(merges.objects);
|
object_array_clear(&merges);
|
||||||
return result->nr;
|
return result->nr;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1790,7 +1790,7 @@ int merge_submodule(struct object_id *result, const char *path,
|
|||||||
print_commit((struct commit *) merges.objects[i].item);
|
print_commit((struct commit *) merges.objects[i].item);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(merges.objects);
|
object_array_clear(&merges);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -888,7 +888,7 @@ static void receive_needs(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
shallow_nr += shallows.nr;
|
shallow_nr += shallows.nr;
|
||||||
free(shallows.objects);
|
object_array_clear(&shallows);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* return non-zero if the ref is hidden, otherwise 0 */
|
/* return non-zero if the ref is hidden, otherwise 0 */
|
||||||
|
Reference in New Issue
Block a user