git/builtin
Patrick Steinhardt b3449b1517 builtin/gc: fix condition for whether to write commit graphs
When performing auto-maintenance we check whether commit graphs need to
be generated by counting the number of commits that are reachable by any
reference, but not covered by a commit graph. This search is performed
by iterating through all references and then doing a depth-first search
until we have found enough commits that are not present in the commit
graph.

This logic has a memory leak though:

  Direct leak of 16 byte(s) in 1 object(s) allocated from:
      #0 0x55555562e433 in malloc (git+0xda433)
      #1 0x555555964322 in do_xmalloc ../wrapper.c:55:8
      #2 0x5555559642e6 in xmalloc ../wrapper.c:76:9
      #3 0x55555579bf29 in commit_list_append ../commit.c:1872:35
      #4 0x55555569f160 in dfs_on_ref ../builtin/gc.c:1165:4
      #5 0x5555558c33fd in do_for_each_ref_iterator ../refs/iterator.c:431:12
      #6 0x5555558af520 in do_for_each_ref ../refs.c:1828:9
      #7 0x5555558ac317 in refs_for_each_ref ../refs.c:1833:9
      #8 0x55555569e207 in should_write_commit_graph ../builtin/gc.c:1188:11
      #9 0x55555569c915 in maintenance_is_needed ../builtin/gc.c:3492:8
      #10 0x55555569b76a in cmd_maintenance ../builtin/gc.c:3542:9
      #11 0x55555575166a in run_builtin ../git.c:506:11
      #12 0x5555557502f0 in handle_builtin ../git.c:779:9
      #13 0x555555751127 in run_argv ../git.c:862:4
      #14 0x55555575007b in cmd_main ../git.c:984:19
      #15 0x5555557523aa in main ../common-main.c:9:11
      #16 0x7ffff7a2a4d7 in __libc_start_call_main (/nix/store/xx7cm72qy2c0643cm1ipngd87aqwkcdp-glibc-2.40-66/lib/libc.so.6+0x2a4d7) (BuildId: cddea92d6cba8333be952b5a02fd47d61054c5ab)
      #17 0x7ffff7a2a59a in __libc_start_main@GLIBC_2.2.5 (/nix/store/xx7cm72qy2c0643cm1ipngd87aqwkcdp-glibc-2.40-66/lib/libc.so.6+0x2a59a) (BuildId: cddea92d6cba8333be952b5a02fd47d61054c5ab)
      #18 0x5555555f0934 in _start (git+0x9c934)

The root cause of this memory leak is our use of `commit_list_append()`.
This function expects as parameters the item to append and the _tail_ of
the list to append. This tail will then be overwritten with the new tail
of the list so that it can be used in subsequent calls. But we call it
with `commit_list_append(parent->item, &stack)`, so we end up losing
everything but the new item.

This issue only surfaces when counting merge commits. Next to being a
memory leak, it also shows that we're in fact miscounting as we only
respect children of the last parent. All previous parents are discarded,
so their children will be disregarded unless they are hit via another
reference.

While crafting a test case for the issue I was puzzled that I couldn't
establish the proper border at which the auto-condition would be
fulfilled. As it turns out, there's another bug: if an object is at the
tip of any reference we don't mark it as seen. Consequently, if it is
the tip of or reachable via another ref, we'd count that object multiple
times.

Fix both of these bugs so that we properly count objects without leaking
any memory.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2026-01-07 09:16:50 +09:00
..
add.c Merge branch 'jt/odb-transaction' 2025-10-02 12:26:11 -07:00
am.c color: use GIT_COLOR_* instead of numeric constants 2025-09-16 13:37:03 -07:00
annotate.c Merge branch 'jc/a-commands-without-the-repo' 2024-10-25 14:02:36 -04:00
apply.c builtin: use default hash when outside a repository 2025-07-01 14:58:24 -07:00
archive.c archive: remove the_repository global variable 2024-10-11 09:37:18 -07:00
backfill.c packfile: split up responsibilities of `reprepare_packed_git()` 2025-09-24 11:53:50 -07:00
bisect.c Merge branch 'ps/ref-peeled-tags' 2025-11-19 10:55:39 -08:00
blame.c blame: make diff algorithm configurable 2025-11-17 09:31:59 -08:00
branch.c color: use git_colorbool enum type to store colorbools 2025-09-16 17:59:53 -07:00
bugreport.c object-file: move `safe_create_leading_directories()` into "path.c" 2025-04-15 08:24:35 -07:00
bundle.c Merge branch 'jt/bundle-fsck' 2024-12-13 07:33:36 -08:00
cat-file.c Merge branch 'ps/remove-packfile-store-get-packs' 2025-10-30 08:00:19 -07:00
check-attr.c config: drop `git_config()` wrapper 2025-07-23 08:15:18 -07:00
check-ignore.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
check-mailmap.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
check-ref-format.c builtin: send usage() help text to standard output 2025-01-17 13:30:03 -08:00
checkout--worker.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
checkout-index.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
checkout.c refs: introduce wrapper struct for `each_ref_fn` 2025-11-04 07:32:24 -08:00
clean.c Merge branch 'jk/color-variable-fixes' 2025-09-29 11:40:35 -07:00
clone.c odb: adopt logic to close object databases 2025-11-19 17:41:03 -08:00
column.c config: drop `git_config()` wrapper 2025-07-23 08:15:18 -07:00
commit-graph.c commit-graph: add new config for changed-paths & recommend it in scalar 2025-10-22 10:40:11 -07:00
commit-tree.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
commit.c Merge branch 'jk/color-variable-fixes' 2025-09-29 11:40:35 -07:00
config.c Merge branch 'ps/config-get-color-fixes' 2025-10-02 12:26:12 -07:00
count-objects.c packfile: introduce macro to iterate through packs 2025-10-16 14:42:39 -07:00
credential-cache--daemon.c config: drop `git_config_get_bool()` wrapper 2025-07-23 08:15:20 -07:00
credential-cache.c Merge branch 'rj/cygwin-exit' 2024-11-01 12:53:19 -04:00
credential-store.c config: drop `git_config_get_int()` wrapper 2025-07-23 08:15:20 -07:00
credential.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
describe.c refs: expose peeled object ID via the iterator 2025-11-04 07:32:25 -08:00
diagnose.c object-file: move `safe_create_leading_directories()` into "path.c" 2025-04-15 08:24:35 -07:00
diff-files.c config: drop `git_config()` wrapper 2025-07-23 08:15:18 -07:00
diff-index.c config: drop `git_config()` wrapper 2025-07-23 08:15:18 -07:00
diff-pairs.c builtin/diff-pairs: allow explicit diff queue flush 2025-03-03 08:17:47 -08:00
diff-tree.c config: drop `git_config()` wrapper 2025-07-23 08:15:18 -07:00
diff.c diff: --no-index should ignore the worktree 2025-08-09 17:22:01 -07:00
difftool.c odb: rename `repo_read_object_file()` 2025-07-01 14:46:38 -07:00
fast-export.c Merge branch 'cc/fast-import-export-i18n-cleanup' 2025-11-06 15:17:01 -08:00
fast-import.c Merge branch 'ps/packed-git-in-object-store' 2025-11-19 10:55:37 -08:00
fetch-pack.c builtin/fetch-pack: cleanup before return error 2025-06-04 08:52:25 -07:00
fetch.c Merge branch 'ja/doc-synopsis-style' 2025-11-30 18:31:39 -08:00
fmt-merge-msg.c builtin/fmt-merge-msg: stop depending on 'the_repository' 2025-08-11 09:19:40 -07:00
for-each-ref.c Merge branch 'ms/refs-list' 2025-08-22 13:13:20 -07:00
for-each-repo.c global: trivial conversions to fix `-Wsign-compare` warnings 2024-12-06 20:20:04 +09:00
fsck.c Merge branch 'ps/ref-peeled-tags' 2025-11-19 10:55:39 -08:00
fsmonitor--daemon.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
gc.c builtin/gc: fix condition for whether to write commit graphs 2026-01-07 09:16:50 +09:00
get-tar-commit-id.c builtin: send usage() help text to standard output 2025-01-17 13:30:03 -08:00
grep.c Merge branch 'tb/incremental-midx-part-3.1' into ps/maintenance-geometric 2025-10-21 11:39:31 -07:00
hash-object.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
help.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
hook.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
index-pack.c builtin/index-pack: fix deferred fsck outside repos 2025-11-25 12:15:59 -08:00
init-db.c Merge branch 'ps/parse-options-integers' 2025-04-24 17:25:34 -07:00
interpret-trailers.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
last-modified.c last-modified: implement faster algorithm 2025-11-03 07:25:41 -08:00
log.c Merge branch 'kh/format-patch-range-diff-notes' 2025-10-14 12:56:09 -07:00
ls-files.c Merge branch 'ds/ls-files-lazy-unsparse' 2025-09-08 14:54:35 -07:00
ls-remote.c ref-filter: propagate peeled object ID 2025-11-04 07:32:25 -08:00
ls-tree.c use repo_get_oid_with_flags() 2025-09-10 14:29:49 -07:00
mailinfo.c mailinfo: stop using `the_repository` 2024-12-18 10:44:31 -08:00
mailsplit.c builtin: send usage() help text to standard output 2025-01-17 13:30:03 -08:00
merge-base.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
merge-file.c Merge branch 'ps/object-file-wo-the-repository' 2025-08-05 11:53:55 -07:00
merge-index.c builtin: send usage() help text to standard output 2025-01-17 13:30:03 -08:00
merge-ours.c builtin: send usage() help text to standard output 2025-01-17 13:30:03 -08:00
merge-recursive.c builtin: also setup gently for --help-all 2025-08-08 11:13:12 -07:00
merge-tree.c Merge branch 'jc/strbuf-split' 2025-08-21 13:47:00 -07:00
merge.c Merge branch 'pw/3.0-commentchar-auto-deprecation' 2025-09-18 10:07:00 -07:00
mktag.c Merge branch 'ps/object-file-wo-the-repository' 2025-08-05 11:53:55 -07:00
mktree.c odb: introduce `odb_write_object()` 2025-07-16 22:16:15 -07:00
multi-pack-index.c Merge branch 'ps/object-store-midx-dedup-info' 2025-09-12 10:41:18 -07:00
mv.c config: drop `git_config()` wrapper 2025-07-23 08:15:18 -07:00
name-rev.c refs: introduce wrapper struct for `each_ref_fn` 2025-11-04 07:32:24 -08:00
notes.c Merge branch 'jc/strbuf-split' 2025-08-21 13:47:00 -07:00
pack-objects.c Merge branch 'ps/object-source-loose' 2025-11-24 15:46:41 -08:00
pack-redundant.c Merge branch 'ps/remove-packfile-store-get-packs' 2025-10-30 08:00:19 -07:00
pack-refs.c builtin/pack-refs: factor out core logic into a shared library 2025-09-19 10:02:55 -07:00
patch-id.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
prune-packed.c builtin: remove USE_THE_REPOSITORY for those without the_repository 2024-09-13 14:33:30 -07:00
prune.c Merge branch 'ps/object-file-wo-the-repository' 2025-08-05 11:53:55 -07:00
pull.c Merge branch 'ps/config-wo-the-repository' 2025-08-04 08:10:33 -07:00
push.c color: use git_colorbool enum type to store colorbools 2025-09-16 17:59:53 -07:00
range-diff.c Merge branch 'kh/format-patch-range-diff-notes' 2025-10-14 12:56:09 -07:00
read-tree.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
rebase.c Merge branch 'jk/setup-revisions-freefix' 2025-09-29 11:40:34 -07:00
receive-pack.c Merge branch 'ps/object-source-management' into ps/odb-misc-fixes 2025-12-05 21:23:32 +09:00
reflog.c Merge branch 'ps/reflog-migrate-fixes' into maint-2.51 2025-10-15 10:29:28 -07:00
refs.c Merge branch 'ms/refs-optimize' 2025-10-02 12:26:12 -07:00
remote-ext.c builtin: send usage() help text to standard output 2025-01-17 13:30:03 -08:00
remote-fd.c builtin: send usage() help text to standard output 2025-01-17 13:30:03 -08:00
remote.c refs: introduce wrapper struct for `each_ref_fn` 2025-11-04 07:32:24 -08:00
repack.c odb: adopt logic to close object databases 2025-11-19 17:41:03 -08:00
replace.c refs: introduce wrapper struct for `each_ref_fn` 2025-11-04 07:32:24 -08:00
replay.c replay: add replay.refAction config option 2025-11-05 13:34:55 -08:00
repo.c Merge branch 'lo/repo-info-all' 2025-11-30 18:31:39 -08:00
rerere.c config: drop `git_config()` wrapper 2025-07-23 08:15:18 -07:00
reset.c Merge branch 'lm/add-p-context' 2025-08-04 08:10:33 -07:00
rev-list.c Merge branch 'ps/config-wo-the-repository' 2025-08-04 08:10:33 -07:00
rev-parse.c Merge branch 'ps/ref-peeled-tags' 2025-11-19 10:55:39 -08:00
revert.c Merge branch 'pw/3.0-commentchar-auto-deprecation' 2025-09-18 10:07:00 -07:00
rm.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
send-pack.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
shortlog.c config: drop `git_config()` wrapper 2025-07-23 08:15:18 -07:00
show-branch.c refs: introduce wrapper struct for `each_ref_fn` 2025-11-04 07:32:24 -08:00
show-index.c builtin: use default hash when outside a repository 2025-07-01 14:58:24 -07:00
show-ref.c builtin/show-ref: convert to use `reference_get_peeled_oid()` 2025-11-04 07:32:25 -08:00
sparse-checkout.c sparse-checkout: add --verbose option to 'clean' 2025-09-15 12:10:56 -07:00
stash.c Merge branch 'dk/stash-apply-index' 2025-09-29 11:40:35 -07:00
stripspace.c config: drop `git_config()` wrapper 2025-07-23 08:15:18 -07:00
submodule--helper.c refs: introduce wrapper struct for `each_ref_fn` 2025-11-04 07:32:24 -08:00
symbolic-ref.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
tag.c ref-filter: propagate peeled object ID 2025-11-04 07:32:25 -08:00
unpack-file.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
unpack-objects.c object-file: refactor writing objects via a stream 2025-11-03 12:18:48 -08:00
update-index.c odb: add transaction interface 2025-09-16 11:37:06 -07:00
update-ref.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
update-server-info.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
upload-archive.c path: move `enter_repo()` into "setup.c" 2025-11-19 17:41:03 -08:00
upload-pack.c path: move `enter_repo()` into "setup.c" 2025-11-19 17:41:03 -08:00
var.c Merge branch 'jc/string-list-split' 2025-08-21 13:46:59 -07:00
verify-commit.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
verify-pack.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00
verify-tag.c ref-filter: propagate peeled object ID 2025-11-04 07:32:25 -08:00
worktree.c Merge branch 'pw/worktree-list-display-width-fix' 2025-11-26 10:32:42 -08:00
write-tree.c config: move Git config parsing into "environment.c" 2025-07-23 08:15:22 -07:00