Skip to content

Commit a399c2c

Browse files
committed
Merge branch 'ps/maintenance-reflog-expire' into jch
"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 30e611d + 41fd393 commit a399c2c

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
@@ -69,3 +69,12 @@ maintenance.incremental-repack.auto::
6969
Otherwise, a positive value implies the command should run when the
7070
number of pack-files not in the multi-pack-index is at least the value
7171
of `maintenance.incremental-repack.auto`. The default value is 10.
72+
73+
maintenance.reflog-expire.auto::
74+
This integer config option controls how often the `reflog-expire` task
75+
should be run as part of `git maintenance run --auto`. If zero, then
76+
the `reflog-expire` task will not run with the `--auto` option. A
77+
negative value will force the task to run every time. Otherwise, a
78+
positive value implies the command should run when the number of
79+
expired reflog entries in the "HEAD" reflog is at least the value of
80+
`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
@@ -158,6 +158,10 @@ pack-refs::
158158
need to iterate across many references. See linkgit:git-pack-refs[1]
159159
for more information.
160160

161+
reflog-expire::
162+
The `reflog-expire` task deletes any entries in the reflog older than the
163+
expiry threshold. See linkgit:git-reflog[1] for more information.
164+
161165
OPTIONS
162166
-------
163167
--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);
@@ -1392,6 +1436,7 @@ enum maintenance_task_label {
13921436
TASK_GC,
13931437
TASK_COMMIT_GRAPH,
13941438
TASK_PACK_REFS,
1439+
TASK_REFLOG_EXPIRE,
13951440

13961441
/* Leave as final value */
13971442
TASK__COUNT
@@ -1428,6 +1473,11 @@ static struct maintenance_task tasks[] = {
14281473
maintenance_task_pack_refs,
14291474
pack_refs_condition,
14301475
},
1476+
[TASK_REFLOG_EXPIRE] = {
1477+
"reflog-expire",
1478+
maintenance_task_reflog_expire,
1479+
reflog_expire_condition,
1480+
},
14311481
};
14321482

14331483
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
@@ -63,9 +63,6 @@ static const char *const reflog_usage[] = {
6363
NULL
6464
};
6565

66-
static timestamp_t default_reflog_expire;
67-
static timestamp_t default_reflog_expire_unreachable;
68-
6966
struct worktree_reflogs {
7067
struct worktree *worktree;
7168
struct string_list reflogs;
@@ -91,147 +88,35 @@ static int collect_reflog(const char *ref, void *cb_data)
9188
return 0;
9289
}
9390

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

21297
BUG_ON_OPT_NEG(unset);
21398

214-
if (parse_expiry_date(arg, &cmd->expire_unreachable))
99+
if (parse_expiry_date(arg, &opts->expire_unreachable))
215100
die(_("invalid timestamp '%s' given to '--%s'"),
216101
arg, opt->long_name);
217102

218-
cmd->explicit_expiry |= EXPIRE_UNREACH;
103+
opts->explicit_expiry |= REFLOG_EXPIRE_UNREACH;
219104
return 0;
220105
}
221106

222107
static int expire_total_callback(const struct option *opt,
223108
const char *arg,
224109
int unset)
225110
{
226-
struct cmd_reflog_expire_cb *cmd = opt->value;
111+
struct reflog_expire_options *opts = opt->value;
227112

228113
BUG_ON_OPT_NEG(unset);
229114

230-
if (parse_expiry_date(arg, &cmd->expire_total))
115+
if (parse_expiry_date(arg, &opts->expire_total))
231116
die(_("invalid timestamp '%s' given to '--%s'"),
232117
arg, opt->long_name);
233118

234-
cmd->explicit_expiry |= EXPIRE_TOTAL;
119+
opts->explicit_expiry |= REFLOG_EXPIRE_TOTAL;
235120
return 0;
236121
}
237122

@@ -276,8 +161,8 @@ static int cmd_reflog_list(int argc, const char **argv, const char *prefix,
276161
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
277162
struct repository *repo UNUSED)
278163
{
279-
struct cmd_reflog_expire_cb cmd = { 0 };
280164
timestamp_t now = time(NULL);
165+
struct reflog_expire_options opts = REFLOG_EXPIRE_OPTIONS_INIT(now);
281166
int i, status, do_all, single_worktree = 0;
282167
unsigned int flags = 0;
283168
int verbose = 0;
@@ -292,33 +177,27 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
292177
N_("update the reference to the value of the top reflog entry"),
293178
EXPIRE_REFLOGS_UPDATE_REF),
294179
OPT_BOOL(0, "verbose", &verbose, N_("print extra information on screen")),
295-
OPT_CALLBACK_F(0, "expire", &cmd, N_("timestamp"),
180+
OPT_CALLBACK_F(0, "expire", &opts, N_("timestamp"),
296181
N_("prune entries older than the specified time"),
297182
PARSE_OPT_NONEG,
298183
expire_total_callback),
299-
OPT_CALLBACK_F(0, "expire-unreachable", &cmd, N_("timestamp"),
184+
OPT_CALLBACK_F(0, "expire-unreachable", &opts, N_("timestamp"),
300185
N_("prune entries older than <time> that are not reachable from the current tip of the branch"),
301186
PARSE_OPT_NONEG,
302187
expire_unreachable_callback),
303-
OPT_BOOL(0, "stale-fix", &cmd.stalefix,
188+
OPT_BOOL(0, "stale-fix", &opts.stalefix,
304189
N_("prune any reflog entries that point to broken commits")),
305190
OPT_BOOL(0, "all", &do_all, N_("process the reflogs of all references")),
306191
OPT_BOOL(0, "single-worktree", &single_worktree,
307192
N_("limits processing to reflogs from the current worktree only")),
308193
OPT_END()
309194
};
310195

311-
default_reflog_expire_unreachable = now - 30 * 24 * 3600;
312-
default_reflog_expire = now - 90 * 24 * 3600;
313-
git_config(reflog_expire_config, NULL);
196+
git_config(reflog_expire_config, &opts);
314197

315198
save_commit_buffer = 0;
316199
do_all = status = 0;
317200

318-
cmd.explicit_expiry = 0;
319-
cmd.expire_total = default_reflog_expire;
320-
cmd.expire_unreachable = default_reflog_expire_unreachable;
321-
322201
argc = parse_options(argc, argv, prefix, options, reflog_expire_usage, 0);
323202

324203
if (verbose)
@@ -329,7 +208,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
329208
* even in older repository. We cannot trust what's reachable
330209
* from reflog if the repository was pruned with older git.
331210
*/
332-
if (cmd.stalefix) {
211+
if (opts.stalefix) {
333212
struct rev_info revs;
334213

335214
repo_init_revisions(the_repository, &revs, prefix);
@@ -363,11 +242,11 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
363242

364243
for_each_string_list_item(item, &collected.reflogs) {
365244
struct expire_reflog_policy_cb cb = {
366-
.cmd = cmd,
245+
.opts = opts,
367246
.dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
368247
};
369248

370-
set_reflog_expiry_param(&cb.cmd, item->string);
249+
reflog_expire_options_set_refname(&cb.opts, item->string);
371250
status |= refs_reflog_expire(get_main_ref_store(the_repository),
372251
item->string, flags,
373252
reflog_expiry_prepare,
@@ -380,13 +259,13 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
380259

381260
for (i = 0; i < argc; i++) {
382261
char *ref;
383-
struct expire_reflog_policy_cb cb = { .cmd = cmd };
262+
struct expire_reflog_policy_cb cb = { .opts = opts };
384263

385264
if (!repo_dwim_log(the_repository, argv[i], strlen(argv[i]), NULL, &ref)) {
386265
status |= error(_("%s points nowhere!"), argv[i]);
387266
continue;
388267
}
389-
set_reflog_expiry_param(&cb.cmd, ref);
268+
reflog_expire_options_set_refname(&cb.opts, ref);
390269
status |= refs_reflog_expire(get_main_ref_store(the_repository),
391270
ref, flags,
392271
reflog_expiry_prepare,

0 commit comments

Comments
 (0)