git/list[1] front-page[2] threads[3] people[4] search[5] about
 

[PATCH v2 6/6] builtin/maintenance: introduce "reflog-expire" task

From
Patrick Steinhardt <ps@pks.im>
Date
Apr 8, 2025, 06:22 UTC
Message-ID
<20250408-pks-maintenance-reflog-expire-v2-6-1ad8634798b7@pks.im>
In-Reply-To
<20250408-pks-maintenance-reflog-expire-v2-0-1ad8634798b7@pks.im>

By default, git-maintenance(1) uses the "gc" task to ensure that the repository is well-maintained. This can be changed, for example by either explicitly configuring which tasks should be enabled or by using the "incremental" maintenance strategy. If so, git-maintenance(1) does not know to expire reflog entries, which is a subtask that git-gc(1) knows to perform for the user. Consequently, the reflog will grow indefinitely unless the user manually trims it.

Introduce a new "reflog-expire" task that plugs this gap:
  - When running the task directly, then we simply execute `git reflog
    expire --all`, which is the same as git-gc(1).
  - When running git-maintenance(1) with the `--auto` flag, then we only
    run the task in case the "HEAD" reflog has at least N reflog entries
    that would be discarded. By default, N is set to 100, but this can
    be configured via "maintenance.reflog-expire.auto". When a negative
    integer has been provided we always expire entries, zero causes us
    to never expire entries, and a positive value specifies how many
    entries need to exist before we consider pruning the entries.

Note that the condition for the `--auto` flags is merely a heuristic and optimized for being fast. This is because `git maintenance run --auto` will be executed quite regularly, so scanning through all reflogs would likely be too expensive in many repositories.

Signed-off-by: Patrick Steinhardt <ps@pks.im>
---
 Documentation/config/maintenance.adoc |  9 +++++++
 Documentation/git-maintenance.adoc    |  4 +++
 builtin/gc.c                          | 50 +++++++++++++++++++++++++++++++++++
 t/t7900-maintenance.sh                | 18 +++++++++++++
 4 files changed, 81 insertions(+)
diff --git a/Documentation/config/maintenance.adoc b/Documentation/config/maintenance.adoc
index 72a9d6cf816..e57f346a067 100644
--- a/Documentation/config/maintenance.adoc
+++ b/Documentation/config/maintenance.adoc
@@ -69,3 +69,12 @@ maintenance.incremental-repack.auto::
 	Otherwise, a positive value implies the command should run when the
 	number of pack-files not in the multi-pack-index is at least the value
 	of `maintenance.incremental-repack.auto`. The default value is 10.
+
+maintenance.reflog-expire.auto::
+	This integer config option controls how often the `reflog-expire` task
+	should be run as part of `git maintenance run --auto`. If zero, then
+	the `reflog-expire` task will not run with the `--auto` option. A
+	negative value will force the task to run every time. Otherwise, a
+	positive value implies the command should run when the number of
+	expired reflog entries in the "HEAD" reflog is at least the value of
+	`maintenance.loose-objects.auto`. The default value is 100.
diff --git a/Documentation/git-maintenance.adoc b/Documentation/git-maintenance.adoc
index 0450d74aff1..8bc94a6d4ff 100644
--- a/Documentation/git-maintenance.adoc
+++ b/Documentation/git-maintenance.adoc
@@ -158,6 +158,10 @@ pack-refs::
 	need to iterate across many references. See linkgit:git-pack-refs[1]
 	for more information.
 
+reflog-expire::
+	The `reflog-expire` task deletes any entries in the reflog older than the
+	expiry threshold. See linkgit:git-reflog[1] for more information.
+
 OPTIONS
 -------
 --auto::
diff --git a/builtin/gc.c b/builtin/gc.c
index e8f5705dc59..ce5bb2630f8 100644
--- a/builtin/gc.c
+++ b/builtin/gc.c
@@ -33,6 +33,7 @@
 #include "pack.h"
 #include "pack-objects.h"
 #include "path.h"
+#include "reflog.h"
 #include "blob.h"
 #include "tree.h"
 #include "promisor-remote.h"
@@ -285,6 +286,49 @@ static int maintenance_task_pack_refs(struct maintenance_run_opts *opts,
 	return run_command(&cmd);
 }
 
+struct count_reflog_entries_data {
+	struct expire_reflog_policy_cb policy;
+	size_t count;
+	size_t limit;
+};
+
+static int count_reflog_entries(struct object_id *old_oid, struct object_id *new_oid,
+				const char *committer, timestamp_t timestamp,
+				int tz, const char *msg, void *cb_data)
+{
+	struct count_reflog_entries_data *data = cb_data;
+	if (should_expire_reflog_ent(old_oid, new_oid, committer, timestamp, tz, msg, &data->policy))
+		data->count++;
+	return data->count >= data->limit;
+}
+
+static int reflog_expire_condition(struct gc_config *cfg UNUSED)
+{
+	timestamp_t now = time(NULL);
+	struct count_reflog_entries_data data = {
+		.policy = {
+			.opts = REFLOG_EXPIRE_OPTIONS_INIT(now),
+		},
+	};
+	int limit = 100;
+
+	git_config_get_int("maintenance.reflog-expire.auto", &limit);
+	if (!limit)
+		return 0;
+	if (limit < 0)
+		return 1;
+	data.limit = limit;
+
+	repo_config(the_repository, reflog_expire_config, &data.policy.opts);
+
+	reflog_expire_options_set_refname(&data.policy.opts, "HEAD");
+	refs_for_each_reflog_ent(get_main_ref_store(the_repository), "HEAD",
+				 count_reflog_entries, &data);
+
+	reflog_expiry_cleanup(&data.policy);
+	return data.count >= data.limit;
+}
+
 static int maintenance_task_reflog_expire(struct maintenance_run_opts *opts UNUSED,
 					  struct gc_config *cfg UNUSED)
 {
@@ -1383,6 +1427,7 @@ enum maintenance_task_label {
 	TASK_GC,
 	TASK_COMMIT_GRAPH,
 	TASK_PACK_REFS,
+	TASK_REFLOG_EXPIRE,
 
 	/* Leave as final value */
 	TASK__COUNT
@@ -1419,6 +1464,11 @@ static struct maintenance_task tasks[] = {
 		maintenance_task_pack_refs,
 		pack_refs_condition,
 	},
+	[TASK_REFLOG_EXPIRE] = {
+		"reflog-expire",
+		maintenance_task_reflog_expire,
+		reflog_expire_condition,
+	},
 };
 
 static int compare_tasks_by_selection(const void *a_, const void *b_)
diff --git a/t/t7900-maintenance.sh b/t/t7900-maintenance.sh
index 1909aed95e0..ff98cde92c0 100755
--- a/t/t7900-maintenance.sh
+++ b/t/t7900-maintenance.sh
@@ -447,6 +447,24 @@ test_expect_success 'pack-refs task' '
 	test_subcommand git pack-refs --all --prune <pack-refs.txt
 '
 
+test_expect_success 'reflog-expire task' '
+	GIT_TRACE2_EVENT="$(pwd)/reflog-expire.txt" \
+		git maintenance run --task=reflog-expire &&
+	test_subcommand git reflog expire --all <reflog-expire.txt
+'
+
+test_expect_success 'reflog-expire task --auto only packs when exceeding limits' '
+	git reflog expire --all --expire=now &&
+	test_commit reflog-one &&
+	test_commit reflog-two &&
+	GIT_TRACE2_EVENT="$(pwd)/reflog-expire-auto.txt" \
+		git -c maintenance.reflog-expire.auto=3 maintenance run --auto --task=reflog-expire &&
+	test_subcommand ! git reflog expire --all <reflog-expire-auto.txt &&
+	GIT_TRACE2_EVENT="$(pwd)/reflog-expire-auto.txt" \
+		git -c maintenance.reflog-expire.auto=2 maintenance run --auto --task=reflog-expire &&
+	test_subcommand git reflog expire --all <reflog-expire-auto.txt
+'
+
 test_expect_success '--auto and --schedule incompatible' '
 	test_must_fail git maintenance run --auto --schedule=daily 2>err &&
 	test_grep "at most one" err
-- 
2.49.0.682.gc9b6a7b2b0.dirty
Previous: Patrick Steinhardt
Message 26 of 26 in “builtin/maintenance: introduce "reflog-expire" task”
  1. 0/6 builtin/maintenance: introduce "reflog-expire" taskPatrick Steinhardt, Feb 26, 2025
  2. 1/6 reflog: rename `cmd_reflog_expire_cb` to `reflog_expire_options`Patrick Steinhardt, Feb 26, 2025
  3. 2/6 builtin/reflog: stop storing default reflog expiry dates globallyPatrick Steinhardt, Feb 26, 2025
  4. Justin ToblerMar 4, 2025
  5. 3/6 builtin/reflog: stop storing per-reflog expiry dates globallyPatrick Steinhardt, Feb 26, 2025
  6. Justin ToblerMar 4, 2025
  7. Patrick SteinhardtMar 6, 2025
  8. Justin ToblerMar 6, 2025
  9. 4/6 builtin/reflog: make functions regarding `reflog_expire_options` publicPatrick Steinhardt, Feb 26, 2025
  10. 5/6 builtin/gc: split out function to expire reflog entriesPatrick Steinhardt, Feb 26, 2025
  11. 6/6 builtin/maintenance: introduce "reflog-expire" taskPatrick Steinhardt, Feb 26, 2025
  12. Ramsay JonesFeb 26, 2025
  13. Junio C HamanoFeb 26, 2025
  14. Ramsay JonesFeb 26, 2025
  15. Patrick SteinhardtFeb 27, 2025
  16. Junio C HamanoFeb 27, 2025
  17. Patrick SteinhardtFeb 27, 2025
  18. Junio C HamanoFeb 27, 2025
  19. Patrick SteinhardtFeb 28, 2025
  20. 0/6 builtin/maintenance: introduce "reflog-expire" taskPatrick Steinhardt, Apr 8, 2025
  21. 2/6 builtin/reflog: stop storing default reflog expiry dates globallyPatrick Steinhardt, Apr 8, 2025
  22. 1/6 reflog: rename `cmd_reflog_expire_cb` to `reflog_expire_options`Patrick Steinhardt, Apr 8, 2025
  23. 3/6 builtin/reflog: stop storing per-reflog expiry dates globallyPatrick Steinhardt, Apr 8, 2025
  24. 4/6 builtin/reflog: make functions regarding `reflog_expire_options` publicPatrick Steinhardt, Apr 8, 2025
  25. 5/6 builtin/gc: split out function to expire reflog entriesPatrick Steinhardt, Apr 8, 2025
  26. 6/6 builtin/maintenance: introduce "reflog-expire" taskPatrick Steinhardt, Apr 8, 2025

Read the whole thread, see it on lore, or plain text.

$ cat FOOTERMessages come from the public archive at lore.kernel.org/git, fetched every hour. The front page is chosen and written each morning by an AI editor and can be wrong; the threads themselves are the record. About and API. For agents: an MCP server at https://gitlist.dev/mcp, and any thread, story or person page as Markdown by adding .md to its URL (or sending Accept: text/markdown). Details in /llms.txt.