Skip to content

Commit 45df6c4

Browse files
committed
Merge branch 'ab/diff-deferred-free'
A small memleak in "diff -I<regexp>" has been corrected. * ab/diff-deferred-free: diff: plug memory leak from regcomp() on {log,diff} -I diff: add an API for deferred freeing
2 parents dcb11fc + c45dc9c commit 45df6c4

File tree

4 files changed

+60
-20
lines changed

4 files changed

+60
-20
lines changed

builtin/log.c

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -307,10 +307,11 @@ static struct itimerval early_output_timer;
307307

308308
static void log_show_early(struct rev_info *revs, struct commit_list *list)
309309
{
310-
int i = revs->early_output, close_file = revs->diffopt.close_file;
310+
int i = revs->early_output;
311311
int show_header = 1;
312+
int no_free = revs->diffopt.no_free;
312313

313-
revs->diffopt.close_file = 0;
314+
revs->diffopt.no_free = 0;
314315
sort_in_topological_order(&list, revs->sort_order);
315316
while (list && i) {
316317
struct commit *commit = list->item;
@@ -327,17 +328,17 @@ static void log_show_early(struct rev_info *revs, struct commit_list *list)
327328
case commit_ignore:
328329
break;
329330
case commit_error:
330-
if (close_file)
331-
fclose(revs->diffopt.file);
331+
revs->diffopt.no_free = no_free;
332+
diff_free(&revs->diffopt);
332333
return;
333334
}
334335
list = list->next;
335336
}
336337

337338
/* Did we already get enough commits for the early output? */
338339
if (!i) {
339-
if (close_file)
340-
fclose(revs->diffopt.file);
340+
revs->diffopt.no_free = 0;
341+
diff_free(&revs->diffopt);
341342
return;
342343
}
343344

@@ -401,7 +402,7 @@ static int cmd_log_walk(struct rev_info *rev)
401402
{
402403
struct commit *commit;
403404
int saved_nrl = 0;
404-
int saved_dcctc = 0, close_file = rev->diffopt.close_file;
405+
int saved_dcctc = 0;
405406

406407
if (rev->early_output)
407408
setup_early_output();
@@ -417,7 +418,7 @@ static int cmd_log_walk(struct rev_info *rev)
417418
* and HAS_CHANGES being accumulated in rev->diffopt, so be careful to
418419
* retain that state information if replacing rev->diffopt in this loop
419420
*/
420-
rev->diffopt.close_file = 0;
421+
rev->diffopt.no_free = 1;
421422
while ((commit = get_revision(rev)) != NULL) {
422423
if (!log_tree_commit(rev, commit) && rev->max_count >= 0)
423424
/*
@@ -442,8 +443,8 @@ static int cmd_log_walk(struct rev_info *rev)
442443
}
443444
rev->diffopt.degraded_cc_to_c = saved_dcctc;
444445
rev->diffopt.needed_rename_limit = saved_nrl;
445-
if (close_file)
446-
fclose(rev->diffopt.file);
446+
rev->diffopt.no_free = 0;
447+
diff_free(&rev->diffopt);
447448

448449
if (rev->diffopt.output_format & DIFF_FORMAT_CHECKDIFF &&
449450
rev->diffopt.flags.check_failed) {
@@ -1961,7 +1962,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
19611962
* file, but but we must instruct it not to close after each
19621963
* diff.
19631964
*/
1964-
rev.diffopt.close_file = 0;
1965+
rev.diffopt.no_free = 1;
19651966
} else {
19661967
int saved;
19671968

diff.c

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6336,6 +6336,32 @@ static void diff_flush_patch_all_file_pairs(struct diff_options *o)
63366336
}
63376337
}
63386338

6339+
static void diff_free_file(struct diff_options *options)
6340+
{
6341+
if (options->close_file)
6342+
fclose(options->file);
6343+
}
6344+
6345+
static void diff_free_ignore_regex(struct diff_options *options)
6346+
{
6347+
int i;
6348+
6349+
for (i = 0; i < options->ignore_regex_nr; i++) {
6350+
regfree(options->ignore_regex[i]);
6351+
free(options->ignore_regex[i]);
6352+
}
6353+
free(options->ignore_regex);
6354+
}
6355+
6356+
void diff_free(struct diff_options *options)
6357+
{
6358+
if (options->no_free)
6359+
return;
6360+
6361+
diff_free_file(options);
6362+
diff_free_ignore_regex(options);
6363+
}
6364+
63396365
void diff_flush(struct diff_options *options)
63406366
{
63416367
struct diff_queue_struct *q = &diff_queued_diff;
@@ -6399,8 +6425,7 @@ void diff_flush(struct diff_options *options)
63996425
* options->file to /dev/null should be safe, because we
64006426
* aren't supposed to produce any output anyway.
64016427
*/
6402-
if (options->close_file)
6403-
fclose(options->file);
6428+
diff_free_file(options);
64046429
options->file = xfopen("/dev/null", "w");
64056430
options->close_file = 1;
64066431
options->color_moved = 0;
@@ -6433,8 +6458,7 @@ void diff_flush(struct diff_options *options)
64336458
free_queue:
64346459
free(q->queue);
64356460
DIFF_QUEUE_CLEAR(q);
6436-
if (options->close_file)
6437-
fclose(options->file);
6461+
diff_free(options);
64386462

64396463
/*
64406464
* Report the content-level differences with HAS_CHANGES;

diff.h

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,17 @@
4949
* - Once you finish feeding the pairs of files, call `diffcore_std()`.
5050
* This will tell the diffcore library to go ahead and do its work.
5151
*
52-
* - Calling `diff_flush()` will produce the output.
52+
* - Calling `diff_flush()` will produce the output, it will call
53+
* `diff_free()` to free any resources, e.g. those allocated in
54+
* `diff_opt_parse()`.
55+
*
56+
* - Set `.no_free = 1` before calling `diff_flush()` to defer the
57+
* freeing of allocated memory in diff_options. This is useful when
58+
* `diff_flush()` is being called in a loop, rather than as a
59+
* one-off. When setting `.no_free = 1` you must ensure that
60+
* `diff_free()` is called at the end, either by flipping the flag
61+
* before the last `diff_flush()` call, or by flipping it before
62+
* calling `diff_free()` yourself.
5363
*/
5464

5565
struct combine_diff_path;
@@ -365,6 +375,8 @@ struct diff_options {
365375

366376
struct repository *repo;
367377
struct option *parseopts;
378+
379+
int no_free;
368380
};
369381

370382
unsigned diff_filter_bit(char status);
@@ -559,6 +571,7 @@ void diffcore_fix_diff_index(void);
559571

560572
int diff_queue_is_empty(void);
561573
void diff_flush(struct diff_options*);
574+
void diff_free(struct diff_options*);
562575
void diff_warn_rename_limit(const char *varname, int needed, int degraded_cc);
563576

564577
/* diff-raw status letters */

log-tree.c

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -963,12 +963,14 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
963963
int log_tree_commit(struct rev_info *opt, struct commit *commit)
964964
{
965965
struct log_info log;
966-
int shown, close_file = opt->diffopt.close_file;
966+
int shown;
967+
/* maybe called by e.g. cmd_log_walk(), maybe stand-alone */
968+
int no_free = opt->diffopt.no_free;
967969

968970
log.commit = commit;
969971
log.parent = NULL;
970972
opt->loginfo = &log;
971-
opt->diffopt.close_file = 0;
973+
opt->diffopt.no_free = 1;
972974

973975
if (opt->line_level_traverse)
974976
return line_log_print(opt, commit);
@@ -985,7 +987,7 @@ int log_tree_commit(struct rev_info *opt, struct commit *commit)
985987
fprintf(opt->diffopt.file, "\n%s\n", opt->break_bar);
986988
opt->loginfo = NULL;
987989
maybe_flush_or_die(opt->diffopt.file, "stdout");
988-
if (close_file)
989-
fclose(opt->diffopt.file);
990+
opt->diffopt.no_free = no_free;
991+
diff_free(&opt->diffopt);
990992
return shown;
991993
}

0 commit comments

Comments
 (0)