Git fork

revision: free diff options

There is a todo comment in `release_revisions()` that mentions that we
need to free the diff options, which was added via 54c8a7c379 (revisions
API: add a TODO for diff_free(&revs->diffopt), 2022-04-14). Releasing
the diff options wasn't quite feasible at that time because some call
sites rely on its contents to remain even after the revisions have been
released.

In fact, there really only are a couple of callsites that misbehave
here:

- `cmd_shortlog()` releases the revisions, but continues to access its
file pointer.

- `do_diff_cache()` creates a shallow copy of `struct diff_options`,
but does not set the `no_free` member. Consequently, we end up
releasing resources of the caller-provided diff options.

- `diff_free()` and friends do not play nice when being called
multiple times as they don't unset data structures that they have
just released.

Fix all of those cases and enable the call to `diff_free()`, which plugs
a bunch of memory leaks.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>

authored by

Patrick Steinhardt and committed by
Junio C Hamano
a90a0896 a282dbeb

+17 -7
+1 -4
builtin/shortlog.c
··· 460 460 else 461 461 get_from_rev(&rev, &log); 462 462 463 + shortlog_output(&log); 463 464 release_revisions(&rev); 464 - 465 - shortlog_output(&log); 466 - if (log.file != stdout) 467 - fclose(log.file); 468 465 return 0; 469 466 } 470 467
+2
diff-lib.c
··· 662 662 repo_init_revisions(opt->repo, &revs, NULL); 663 663 copy_pathspec(&revs.prune_data, &opt->pathspec); 664 664 revs.diffopt = *opt; 665 + revs.diffopt.no_free = 1; 665 666 666 667 if (diff_cache(&revs, tree_oid, NULL, 1)) 667 668 exit(128); 669 + 668 670 release_revisions(&revs); 669 671 return 0; 670 672 }
+6 -2
diff.c
··· 6649 6649 6650 6650 static void diff_free_file(struct diff_options *options) 6651 6651 { 6652 - if (options->close_file) 6652 + if (options->close_file && options->file) { 6653 6653 fclose(options->file); 6654 + options->file = NULL; 6655 + } 6654 6656 } 6655 6657 6656 6658 static void diff_free_ignore_regex(struct diff_options *options) ··· 6661 6663 regfree(options->ignore_regex[i]); 6662 6664 free(options->ignore_regex[i]); 6663 6665 } 6664 - free(options->ignore_regex); 6666 + 6667 + FREE_AND_NULL(options->ignore_regex); 6668 + options->ignore_regex_nr = 0; 6665 6669 } 6666 6670 6667 6671 void diff_free(struct diff_options *options)
+1 -1
revision.c
··· 3191 3191 release_revisions_mailmap(revs->mailmap); 3192 3192 free_grep_patterns(&revs->grep_filter); 3193 3193 graph_clear(revs->graph); 3194 - /* TODO (need to handle "no_free"): diff_free(&revs->diffopt) */ 3194 + diff_free(&revs->diffopt); 3195 3195 diff_free(&revs->pruning); 3196 3196 reflog_walk_info_release(revs->reflog_info); 3197 3197 release_revisions_topo_walk_info(revs->topo_walk_info);
+1
t/t4208-log-magic-pathspec.sh
··· 5 5 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main 6 6 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME 7 7 8 + TEST_PASSES_SANITIZE_LEAK=true 8 9 . ./test-lib.sh 9 10 10 11 test_expect_success 'setup' '
+1
t/t6000-rev-list-misc.sh
··· 5 5 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main 6 6 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME 7 7 8 + TEST_PASSES_SANITIZE_LEAK=true 8 9 . ./test-lib.sh 9 10 10 11 test_expect_success setup '
+1
t/t6001-rev-list-graft.sh
··· 5 5 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main 6 6 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME 7 7 8 + TEST_PASSES_SANITIZE_LEAK=true 8 9 . ./test-lib.sh 9 10 10 11 test_expect_success setup '
+1
t/t6013-rev-list-reverse-parents.sh
··· 5 5 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main 6 6 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME 7 7 8 + TEST_PASSES_SANITIZE_LEAK=true 8 9 . ./test-lib.sh 9 10 10 11
+1
t/t6017-rev-list-stdin.sh
··· 8 8 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main 9 9 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME 10 10 11 + TEST_PASSES_SANITIZE_LEAK=true 11 12 . ./test-lib.sh 12 13 13 14 check () {
+1
t/t9500-gitweb-standalone-no-errors.sh
··· 13 13 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main 14 14 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME 15 15 16 + TEST_PASSES_SANITIZE_LEAK=true 16 17 . ./lib-gitweb.sh 17 18 18 19 # ----------------------------------------------------------------------
+1
t/t9502-gitweb-standalone-parse-output.sh
··· 13 13 GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME=main 14 14 export GIT_TEST_DEFAULT_INITIAL_BRANCH_NAME 15 15 16 + TEST_PASSES_SANITIZE_LEAK=true 16 17 . ./lib-gitweb.sh 17 18 18 19 # ----------------------------------------------------------------------