Skip to content

Commit 01a6e24

Browse files
committed
Merge branch 'ps/maintenance-reflog-expire'
"git maintenance" learns a new task to expire reflog entries. * ps/maintenance-reflog-expire: builtin/maintenance: introduce "reflog-expire" task builtin/gc: split out function to expire reflog entries builtin/reflog: make functions regarding `reflog_expire_options` public builtin/reflog: stop storing per-reflog expiry dates globally builtin/reflog: stop storing default reflog expiry dates globally reflog: rename `cmd_reflog_expire_cb` to `reflog_expire_options`
2 parents 1a1661b + 8e0a1ec commit 01a6e24

File tree

7 files changed

+263
-165
lines changed

7 files changed

+263
-165
lines changed

Documentation/config/maintenance.adoc

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,3 +74,12 @@ maintenance.incremental-repack.auto::
7474
Otherwise, a positive value implies the command should run when the
7575
number of pack-files not in the multi-pack-index is at least the value
7676
of `maintenance.incremental-repack.auto`. The default value is 10.
77+
78+
maintenance.reflog-expire.auto::
79+
This integer config option controls how often the `reflog-expire` task
80+
should be run as part of `git maintenance run --auto`. If zero, then
81+
the `reflog-expire` task will not run with the `--auto` option. A
82+
negative value will force the task to run every time. Otherwise, a
83+
positive value implies the command should run when the number of
84+
expired reflog entries in the "HEAD" reflog is at least the value of
85+
`maintenance.loose-objects.auto`. The default value is 100.

Documentation/git-maintenance.adoc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,10 @@ pack-refs::
162162
need to iterate across many references. See linkgit:git-pack-refs[1]
163163
for more information.
164164

165+
reflog-expire::
166+
The `reflog-expire` task deletes any entries in the reflog older than the
167+
expiry threshold. See linkgit:git-reflog[1] for more information.
168+
165169
OPTIONS
166170
-------
167171
--auto::

builtin/gc.c

Lines changed: 61 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "pack.h"
3434
#include "pack-objects.h"
3535
#include "path.h"
36+
#include "reflog.h"
3637
#include "blob.h"
3738
#include "tree.h"
3839
#include "promisor-remote.h"
@@ -53,7 +54,6 @@ static const char * const builtin_gc_usage[] = {
5354

5455
static timestamp_t gc_log_expire_time;
5556

56-
static struct strvec reflog = STRVEC_INIT;
5757
static struct strvec repack = STRVEC_INIT;
5858
static struct strvec prune = STRVEC_INIT;
5959
static struct strvec prune_worktrees = STRVEC_INIT;
@@ -288,6 +288,58 @@ static int maintenance_task_pack_refs(struct maintenance_run_opts *opts,
288288
return run_command(&cmd);
289289
}
290290

291+
struct count_reflog_entries_data {
292+
struct expire_reflog_policy_cb policy;
293+
size_t count;
294+
size_t limit;
295+
};
296+
297+
static int count_reflog_entries(struct object_id *old_oid, struct object_id *new_oid,
298+
const char *committer, timestamp_t timestamp,
299+
int tz, const char *msg, void *cb_data)
300+
{
301+
struct count_reflog_entries_data *data = cb_data;
302+
if (should_expire_reflog_ent(old_oid, new_oid, committer, timestamp, tz, msg, &data->policy))
303+
data->count++;
304+
return data->count >= data->limit;
305+
}
306+
307+
static int reflog_expire_condition(struct gc_config *cfg UNUSED)
308+
{
309+
timestamp_t now = time(NULL);
310+
struct count_reflog_entries_data data = {
311+
.policy = {
312+
.opts = REFLOG_EXPIRE_OPTIONS_INIT(now),
313+
},
314+
};
315+
int limit = 100;
316+
317+
git_config_get_int("maintenance.reflog-expire.auto", &limit);
318+
if (!limit)
319+
return 0;
320+
if (limit < 0)
321+
return 1;
322+
data.limit = limit;
323+
324+
repo_config(the_repository, reflog_expire_config, &data.policy.opts);
325+
326+
reflog_expire_options_set_refname(&data.policy.opts, "HEAD");
327+
refs_for_each_reflog_ent(get_main_ref_store(the_repository), "HEAD",
328+
count_reflog_entries, &data);
329+
330+
reflog_expiry_cleanup(&data.policy);
331+
return data.count >= data.limit;
332+
}
333+
334+
static int maintenance_task_reflog_expire(struct maintenance_run_opts *opts UNUSED,
335+
struct gc_config *cfg UNUSED)
336+
{
337+
struct child_process cmd = CHILD_PROCESS_INIT;
338+
cmd.git_cmd = 1;
339+
strvec_pushl(&cmd.args, "reflog", "expire", "--all", NULL);
340+
return run_command(&cmd);
341+
}
342+
291343
static int too_many_loose_objects(struct gc_config *cfg)
292344
{
293345
/*
@@ -667,15 +719,8 @@ static void gc_before_repack(struct maintenance_run_opts *opts,
667719

668720
if (cfg->pack_refs && maintenance_task_pack_refs(opts, cfg))
669721
die(FAILED_RUN, "pack-refs");
670-
671-
if (cfg->prune_reflogs) {
672-
struct child_process cmd = CHILD_PROCESS_INIT;
673-
674-
cmd.git_cmd = 1;
675-
strvec_pushv(&cmd.args, reflog.v);
676-
if (run_command(&cmd))
677-
die(FAILED_RUN, reflog.v[0]);
678-
}
722+
if (cfg->prune_reflogs && maintenance_task_reflog_expire(opts, cfg))
723+
die(FAILED_RUN, "reflog");
679724
}
680725

681726
int cmd_gc(int argc,
@@ -723,7 +768,6 @@ struct repository *repo UNUSED)
723768
show_usage_with_options_if_asked(argc, argv,
724769
builtin_gc_usage, builtin_gc_options);
725770

726-
strvec_pushl(&reflog, "reflog", "expire", "--all", NULL);
727771
strvec_pushl(&repack, "repack", "-d", "-l", NULL);
728772
strvec_pushl(&prune, "prune", "--expire", NULL);
729773
strvec_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL);
@@ -1412,6 +1456,7 @@ enum maintenance_task_label {
14121456
TASK_GC,
14131457
TASK_COMMIT_GRAPH,
14141458
TASK_PACK_REFS,
1459+
TASK_REFLOG_EXPIRE,
14151460

14161461
/* Leave as final value */
14171462
TASK__COUNT
@@ -1448,6 +1493,11 @@ static struct maintenance_task tasks[] = {
14481493
maintenance_task_pack_refs,
14491494
pack_refs_condition,
14501495
},
1496+
[TASK_REFLOG_EXPIRE] = {
1497+
"reflog-expire",
1498+
maintenance_task_reflog_expire,
1499+
reflog_expire_condition,
1500+
},
14511501
};
14521502

14531503
static int compare_tasks_by_selection(const void *a_, const void *b_)

builtin/reflog.c

Lines changed: 16 additions & 137 deletions
Original file line numberDiff line numberDiff line change
@@ -72,9 +72,6 @@ static const char *const reflog_usage[] = {
7272
NULL
7373
};
7474

75-
static timestamp_t default_reflog_expire;
76-
static timestamp_t default_reflog_expire_unreachable;
77-
7875
struct worktree_reflogs {
7976
struct worktree *worktree;
8077
struct string_list reflogs;
@@ -100,147 +97,35 @@ static int collect_reflog(const char *ref, void *cb_data)
10097
return 0;
10198
}
10299

103-
static struct reflog_expire_cfg {
104-
struct reflog_expire_cfg *next;
105-
timestamp_t expire_total;
106-
timestamp_t expire_unreachable;
107-
char pattern[FLEX_ARRAY];
108-
} *reflog_expire_cfg, **reflog_expire_cfg_tail;
109-
110-
static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
111-
{
112-
struct reflog_expire_cfg *ent;
113-
114-
if (!reflog_expire_cfg_tail)
115-
reflog_expire_cfg_tail = &reflog_expire_cfg;
116-
117-
for (ent = reflog_expire_cfg; ent; ent = ent->next)
118-
if (!xstrncmpz(ent->pattern, pattern, len))
119-
return ent;
120-
121-
FLEX_ALLOC_MEM(ent, pattern, pattern, len);
122-
*reflog_expire_cfg_tail = ent;
123-
reflog_expire_cfg_tail = &(ent->next);
124-
return ent;
125-
}
126-
127-
/* expiry timer slot */
128-
#define EXPIRE_TOTAL 01
129-
#define EXPIRE_UNREACH 02
130-
131-
static int reflog_expire_config(const char *var, const char *value,
132-
const struct config_context *ctx, void *cb)
133-
{
134-
const char *pattern, *key;
135-
size_t pattern_len;
136-
timestamp_t expire;
137-
int slot;
138-
struct reflog_expire_cfg *ent;
139-
140-
if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0)
141-
return git_default_config(var, value, ctx, cb);
142-
143-
if (!strcmp(key, "reflogexpire")) {
144-
slot = EXPIRE_TOTAL;
145-
if (git_config_expiry_date(&expire, var, value))
146-
return -1;
147-
} else if (!strcmp(key, "reflogexpireunreachable")) {
148-
slot = EXPIRE_UNREACH;
149-
if (git_config_expiry_date(&expire, var, value))
150-
return -1;
151-
} else
152-
return git_default_config(var, value, ctx, cb);
153-
154-
if (!pattern) {
155-
switch (slot) {
156-
case EXPIRE_TOTAL:
157-
default_reflog_expire = expire;
158-
break;
159-
case EXPIRE_UNREACH:
160-
default_reflog_expire_unreachable = expire;
161-
break;
162-
}
163-
return 0;
164-
}
165-
166-
ent = find_cfg_ent(pattern, pattern_len);
167-
if (!ent)
168-
return -1;
169-
switch (slot) {
170-
case EXPIRE_TOTAL:
171-
ent->expire_total = expire;
172-
break;
173-
case EXPIRE_UNREACH:
174-
ent->expire_unreachable = expire;
175-
break;
176-
}
177-
return 0;
178-
}
179-
180-
static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, const char *ref)
181-
{
182-
struct reflog_expire_cfg *ent;
183-
184-
if (cb->explicit_expiry == (EXPIRE_TOTAL|EXPIRE_UNREACH))
185-
return; /* both given explicitly -- nothing to tweak */
186-
187-
for (ent = reflog_expire_cfg; ent; ent = ent->next) {
188-
if (!wildmatch(ent->pattern, ref, 0)) {
189-
if (!(cb->explicit_expiry & EXPIRE_TOTAL))
190-
cb->expire_total = ent->expire_total;
191-
if (!(cb->explicit_expiry & EXPIRE_UNREACH))
192-
cb->expire_unreachable = ent->expire_unreachable;
193-
return;
194-
}
195-
}
196-
197-
/*
198-
* If unconfigured, make stash never expire
199-
*/
200-
if (!strcmp(ref, "refs/stash")) {
201-
if (!(cb->explicit_expiry & EXPIRE_TOTAL))
202-
cb->expire_total = 0;
203-
if (!(cb->explicit_expiry & EXPIRE_UNREACH))
204-
cb->expire_unreachable = 0;
205-
return;
206-
}
207-
208-
/* Nothing matched -- use the default value */
209-
if (!(cb->explicit_expiry & EXPIRE_TOTAL))
210-
cb->expire_total = default_reflog_expire;
211-
if (!(cb->explicit_expiry & EXPIRE_UNREACH))
212-
cb->expire_unreachable = default_reflog_expire_unreachable;
213-
}
214-
215100
static int expire_unreachable_callback(const struct option *opt,
216101
const char *arg,
217102
int unset)
218103
{
219-
struct cmd_reflog_expire_cb *cmd = opt->value;
104+
struct reflog_expire_options *opts = opt->value;
220105

221106
BUG_ON_OPT_NEG(unset);
222107

223-
if (parse_expiry_date(arg, &cmd->expire_unreachable))
108+
if (parse_expiry_date(arg, &opts->expire_unreachable))
224109
die(_("invalid timestamp '%s' given to '--%s'"),
225110
arg, opt->long_name);
226111

227-
cmd->explicit_expiry |= EXPIRE_UNREACH;
112+
opts->explicit_expiry |= REFLOG_EXPIRE_UNREACH;
228113
return 0;
229114
}
230115

231116
static int expire_total_callback(const struct option *opt,
232117
const char *arg,
233118
int unset)
234119
{
235-
struct cmd_reflog_expire_cb *cmd = opt->value;
120+
struct reflog_expire_options *opts = opt->value;
236121

237122
BUG_ON_OPT_NEG(unset);
238123

239-
if (parse_expiry_date(arg, &cmd->expire_total))
124+
if (parse_expiry_date(arg, &opts->expire_total))
240125
die(_("invalid timestamp '%s' given to '--%s'"),
241126
arg, opt->long_name);
242127

243-
cmd->explicit_expiry |= EXPIRE_TOTAL;
128+
opts->explicit_expiry |= REFLOG_EXPIRE_TOTAL;
244129
return 0;
245130
}
246131

@@ -285,8 +170,8 @@ static int cmd_reflog_list(int argc, const char **argv, const char *prefix,
285170
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
286171
struct repository *repo UNUSED)
287172
{
288-
struct cmd_reflog_expire_cb cmd = { 0 };
289173
timestamp_t now = time(NULL);
174+
struct reflog_expire_options opts = REFLOG_EXPIRE_OPTIONS_INIT(now);
290175
int i, status, do_all, single_worktree = 0;
291176
unsigned int flags = 0;
292177
int verbose = 0;
@@ -301,33 +186,27 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
301186
N_("update the reference to the value of the top reflog entry"),
302187
EXPIRE_REFLOGS_UPDATE_REF),
303188
OPT_BOOL(0, "verbose", &verbose, N_("print extra information on screen")),
304-
OPT_CALLBACK_F(0, "expire", &cmd, N_("timestamp"),
189+
OPT_CALLBACK_F(0, "expire", &opts, N_("timestamp"),
305190
N_("prune entries older than the specified time"),
306191
PARSE_OPT_NONEG,
307192
expire_total_callback),
308-
OPT_CALLBACK_F(0, "expire-unreachable", &cmd, N_("timestamp"),
193+
OPT_CALLBACK_F(0, "expire-unreachable", &opts, N_("timestamp"),
309194
N_("prune entries older than <time> that are not reachable from the current tip of the branch"),
310195
PARSE_OPT_NONEG,
311196
expire_unreachable_callback),
312-
OPT_BOOL(0, "stale-fix", &cmd.stalefix,
197+
OPT_BOOL(0, "stale-fix", &opts.stalefix,
313198
N_("prune any reflog entries that point to broken commits")),
314199
OPT_BOOL(0, "all", &do_all, N_("process the reflogs of all references")),
315200
OPT_BOOL(0, "single-worktree", &single_worktree,
316201
N_("limits processing to reflogs from the current worktree only")),
317202
OPT_END()
318203
};
319204

320-
default_reflog_expire_unreachable = now - 30 * 24 * 3600;
321-
default_reflog_expire = now - 90 * 24 * 3600;
322-
git_config(reflog_expire_config, NULL);
205+
git_config(reflog_expire_config, &opts);
323206

324207
save_commit_buffer = 0;
325208
do_all = status = 0;
326209

327-
cmd.explicit_expiry = 0;
328-
cmd.expire_total = default_reflog_expire;
329-
cmd.expire_unreachable = default_reflog_expire_unreachable;
330-
331210
argc = parse_options(argc, argv, prefix, options, reflog_expire_usage, 0);
332211

333212
if (verbose)
@@ -338,7 +217,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
338217
* even in older repository. We cannot trust what's reachable
339218
* from reflog if the repository was pruned with older git.
340219
*/
341-
if (cmd.stalefix) {
220+
if (opts.stalefix) {
342221
struct rev_info revs;
343222

344223
repo_init_revisions(the_repository, &revs, prefix);
@@ -372,11 +251,11 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
372251

373252
for_each_string_list_item(item, &collected.reflogs) {
374253
struct expire_reflog_policy_cb cb = {
375-
.cmd = cmd,
254+
.opts = opts,
376255
.dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
377256
};
378257

379-
set_reflog_expiry_param(&cb.cmd, item->string);
258+
reflog_expire_options_set_refname(&cb.opts, item->string);
380259
status |= refs_reflog_expire(get_main_ref_store(the_repository),
381260
item->string, flags,
382261
reflog_expiry_prepare,
@@ -389,13 +268,13 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
389268

390269
for (i = 0; i < argc; i++) {
391270
char *ref;
392-
struct expire_reflog_policy_cb cb = { .cmd = cmd };
271+
struct expire_reflog_policy_cb cb = { .opts = opts };
393272

394273
if (!repo_dwim_log(the_repository, argv[i], strlen(argv[i]), NULL, &ref)) {
395274
status |= error(_("reflog could not be found: '%s'"), argv[i]);
396275
continue;
397276
}
398-
set_reflog_expiry_param(&cb.cmd, ref);
277+
reflog_expire_options_set_refname(&cb.opts, ref);
399278
status |= refs_reflog_expire(get_main_ref_store(the_repository),
400279
ref, flags,
401280
reflog_expiry_prepare,

0 commit comments

Comments
 (0)