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`main
commit
01a6e244f9
|
@ -74,3 +74,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.
|
||||
|
|
|
@ -162,6 +162,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::
|
||||
|
|
72
builtin/gc.c
72
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"
|
||||
|
@ -53,7 +54,6 @@ static const char * const builtin_gc_usage[] = {
|
|||
|
||||
static timestamp_t gc_log_expire_time;
|
||||
|
||||
static struct strvec reflog = STRVEC_INIT;
|
||||
static struct strvec repack = STRVEC_INIT;
|
||||
static struct strvec prune = STRVEC_INIT;
|
||||
static struct strvec prune_worktrees = STRVEC_INIT;
|
||||
|
@ -288,6 +288,58 @@ 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)
|
||||
{
|
||||
struct child_process cmd = CHILD_PROCESS_INIT;
|
||||
cmd.git_cmd = 1;
|
||||
strvec_pushl(&cmd.args, "reflog", "expire", "--all", NULL);
|
||||
return run_command(&cmd);
|
||||
}
|
||||
|
||||
static int too_many_loose_objects(struct gc_config *cfg)
|
||||
{
|
||||
/*
|
||||
|
@ -667,15 +719,8 @@ static void gc_before_repack(struct maintenance_run_opts *opts,
|
|||
|
||||
if (cfg->pack_refs && maintenance_task_pack_refs(opts, cfg))
|
||||
die(FAILED_RUN, "pack-refs");
|
||||
|
||||
if (cfg->prune_reflogs) {
|
||||
struct child_process cmd = CHILD_PROCESS_INIT;
|
||||
|
||||
cmd.git_cmd = 1;
|
||||
strvec_pushv(&cmd.args, reflog.v);
|
||||
if (run_command(&cmd))
|
||||
die(FAILED_RUN, reflog.v[0]);
|
||||
}
|
||||
if (cfg->prune_reflogs && maintenance_task_reflog_expire(opts, cfg))
|
||||
die(FAILED_RUN, "reflog");
|
||||
}
|
||||
|
||||
int cmd_gc(int argc,
|
||||
|
@ -723,7 +768,6 @@ struct repository *repo UNUSED)
|
|||
show_usage_with_options_if_asked(argc, argv,
|
||||
builtin_gc_usage, builtin_gc_options);
|
||||
|
||||
strvec_pushl(&reflog, "reflog", "expire", "--all", NULL);
|
||||
strvec_pushl(&repack, "repack", "-d", "-l", NULL);
|
||||
strvec_pushl(&prune, "prune", "--expire", NULL);
|
||||
strvec_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL);
|
||||
|
@ -1412,6 +1456,7 @@ enum maintenance_task_label {
|
|||
TASK_GC,
|
||||
TASK_COMMIT_GRAPH,
|
||||
TASK_PACK_REFS,
|
||||
TASK_REFLOG_EXPIRE,
|
||||
|
||||
/* Leave as final value */
|
||||
TASK__COUNT
|
||||
|
@ -1448,6 +1493,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_)
|
||||
|
|
153
builtin/reflog.c
153
builtin/reflog.c
|
@ -72,9 +72,6 @@ static const char *const reflog_usage[] = {
|
|||
NULL
|
||||
};
|
||||
|
||||
static timestamp_t default_reflog_expire;
|
||||
static timestamp_t default_reflog_expire_unreachable;
|
||||
|
||||
struct worktree_reflogs {
|
||||
struct worktree *worktree;
|
||||
struct string_list reflogs;
|
||||
|
@ -100,131 +97,19 @@ static int collect_reflog(const char *ref, void *cb_data)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static struct reflog_expire_cfg {
|
||||
struct reflog_expire_cfg *next;
|
||||
timestamp_t expire_total;
|
||||
timestamp_t expire_unreachable;
|
||||
char pattern[FLEX_ARRAY];
|
||||
} *reflog_expire_cfg, **reflog_expire_cfg_tail;
|
||||
|
||||
static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
|
||||
{
|
||||
struct reflog_expire_cfg *ent;
|
||||
|
||||
if (!reflog_expire_cfg_tail)
|
||||
reflog_expire_cfg_tail = &reflog_expire_cfg;
|
||||
|
||||
for (ent = reflog_expire_cfg; ent; ent = ent->next)
|
||||
if (!xstrncmpz(ent->pattern, pattern, len))
|
||||
return ent;
|
||||
|
||||
FLEX_ALLOC_MEM(ent, pattern, pattern, len);
|
||||
*reflog_expire_cfg_tail = ent;
|
||||
reflog_expire_cfg_tail = &(ent->next);
|
||||
return ent;
|
||||
}
|
||||
|
||||
/* expiry timer slot */
|
||||
#define EXPIRE_TOTAL 01
|
||||
#define EXPIRE_UNREACH 02
|
||||
|
||||
static int reflog_expire_config(const char *var, const char *value,
|
||||
const struct config_context *ctx, void *cb)
|
||||
{
|
||||
const char *pattern, *key;
|
||||
size_t pattern_len;
|
||||
timestamp_t expire;
|
||||
int slot;
|
||||
struct reflog_expire_cfg *ent;
|
||||
|
||||
if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0)
|
||||
return git_default_config(var, value, ctx, cb);
|
||||
|
||||
if (!strcmp(key, "reflogexpire")) {
|
||||
slot = EXPIRE_TOTAL;
|
||||
if (git_config_expiry_date(&expire, var, value))
|
||||
return -1;
|
||||
} else if (!strcmp(key, "reflogexpireunreachable")) {
|
||||
slot = EXPIRE_UNREACH;
|
||||
if (git_config_expiry_date(&expire, var, value))
|
||||
return -1;
|
||||
} else
|
||||
return git_default_config(var, value, ctx, cb);
|
||||
|
||||
if (!pattern) {
|
||||
switch (slot) {
|
||||
case EXPIRE_TOTAL:
|
||||
default_reflog_expire = expire;
|
||||
break;
|
||||
case EXPIRE_UNREACH:
|
||||
default_reflog_expire_unreachable = expire;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ent = find_cfg_ent(pattern, pattern_len);
|
||||
if (!ent)
|
||||
return -1;
|
||||
switch (slot) {
|
||||
case EXPIRE_TOTAL:
|
||||
ent->expire_total = expire;
|
||||
break;
|
||||
case EXPIRE_UNREACH:
|
||||
ent->expire_unreachable = expire;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, const char *ref)
|
||||
{
|
||||
struct reflog_expire_cfg *ent;
|
||||
|
||||
if (cb->explicit_expiry == (EXPIRE_TOTAL|EXPIRE_UNREACH))
|
||||
return; /* both given explicitly -- nothing to tweak */
|
||||
|
||||
for (ent = reflog_expire_cfg; ent; ent = ent->next) {
|
||||
if (!wildmatch(ent->pattern, ref, 0)) {
|
||||
if (!(cb->explicit_expiry & EXPIRE_TOTAL))
|
||||
cb->expire_total = ent->expire_total;
|
||||
if (!(cb->explicit_expiry & EXPIRE_UNREACH))
|
||||
cb->expire_unreachable = ent->expire_unreachable;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If unconfigured, make stash never expire
|
||||
*/
|
||||
if (!strcmp(ref, "refs/stash")) {
|
||||
if (!(cb->explicit_expiry & EXPIRE_TOTAL))
|
||||
cb->expire_total = 0;
|
||||
if (!(cb->explicit_expiry & EXPIRE_UNREACH))
|
||||
cb->expire_unreachable = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Nothing matched -- use the default value */
|
||||
if (!(cb->explicit_expiry & EXPIRE_TOTAL))
|
||||
cb->expire_total = default_reflog_expire;
|
||||
if (!(cb->explicit_expiry & EXPIRE_UNREACH))
|
||||
cb->expire_unreachable = default_reflog_expire_unreachable;
|
||||
}
|
||||
|
||||
static int expire_unreachable_callback(const struct option *opt,
|
||||
const char *arg,
|
||||
int unset)
|
||||
{
|
||||
struct cmd_reflog_expire_cb *cmd = opt->value;
|
||||
struct reflog_expire_options *opts = opt->value;
|
||||
|
||||
BUG_ON_OPT_NEG(unset);
|
||||
|
||||
if (parse_expiry_date(arg, &cmd->expire_unreachable))
|
||||
if (parse_expiry_date(arg, &opts->expire_unreachable))
|
||||
die(_("invalid timestamp '%s' given to '--%s'"),
|
||||
arg, opt->long_name);
|
||||
|
||||
cmd->explicit_expiry |= EXPIRE_UNREACH;
|
||||
opts->explicit_expiry |= REFLOG_EXPIRE_UNREACH;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -232,15 +117,15 @@ static int expire_total_callback(const struct option *opt,
|
|||
const char *arg,
|
||||
int unset)
|
||||
{
|
||||
struct cmd_reflog_expire_cb *cmd = opt->value;
|
||||
struct reflog_expire_options *opts = opt->value;
|
||||
|
||||
BUG_ON_OPT_NEG(unset);
|
||||
|
||||
if (parse_expiry_date(arg, &cmd->expire_total))
|
||||
if (parse_expiry_date(arg, &opts->expire_total))
|
||||
die(_("invalid timestamp '%s' given to '--%s'"),
|
||||
arg, opt->long_name);
|
||||
|
||||
cmd->explicit_expiry |= EXPIRE_TOTAL;
|
||||
opts->explicit_expiry |= REFLOG_EXPIRE_TOTAL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -285,8 +170,8 @@ static int cmd_reflog_list(int argc, const char **argv, const char *prefix,
|
|||
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
|
||||
struct repository *repo UNUSED)
|
||||
{
|
||||
struct cmd_reflog_expire_cb cmd = { 0 };
|
||||
timestamp_t now = time(NULL);
|
||||
struct reflog_expire_options opts = REFLOG_EXPIRE_OPTIONS_INIT(now);
|
||||
int i, status, do_all, single_worktree = 0;
|
||||
unsigned int flags = 0;
|
||||
int verbose = 0;
|
||||
|
@ -301,15 +186,15 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
|
|||
N_("update the reference to the value of the top reflog entry"),
|
||||
EXPIRE_REFLOGS_UPDATE_REF),
|
||||
OPT_BOOL(0, "verbose", &verbose, N_("print extra information on screen")),
|
||||
OPT_CALLBACK_F(0, "expire", &cmd, N_("timestamp"),
|
||||
OPT_CALLBACK_F(0, "expire", &opts, N_("timestamp"),
|
||||
N_("prune entries older than the specified time"),
|
||||
PARSE_OPT_NONEG,
|
||||
expire_total_callback),
|
||||
OPT_CALLBACK_F(0, "expire-unreachable", &cmd, N_("timestamp"),
|
||||
OPT_CALLBACK_F(0, "expire-unreachable", &opts, N_("timestamp"),
|
||||
N_("prune entries older than <time> that are not reachable from the current tip of the branch"),
|
||||
PARSE_OPT_NONEG,
|
||||
expire_unreachable_callback),
|
||||
OPT_BOOL(0, "stale-fix", &cmd.stalefix,
|
||||
OPT_BOOL(0, "stale-fix", &opts.stalefix,
|
||||
N_("prune any reflog entries that point to broken commits")),
|
||||
OPT_BOOL(0, "all", &do_all, N_("process the reflogs of all references")),
|
||||
OPT_BOOL(0, "single-worktree", &single_worktree,
|
||||
|
@ -317,17 +202,11 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
|
|||
OPT_END()
|
||||
};
|
||||
|
||||
default_reflog_expire_unreachable = now - 30 * 24 * 3600;
|
||||
default_reflog_expire = now - 90 * 24 * 3600;
|
||||
git_config(reflog_expire_config, NULL);
|
||||
git_config(reflog_expire_config, &opts);
|
||||
|
||||
save_commit_buffer = 0;
|
||||
do_all = status = 0;
|
||||
|
||||
cmd.explicit_expiry = 0;
|
||||
cmd.expire_total = default_reflog_expire;
|
||||
cmd.expire_unreachable = default_reflog_expire_unreachable;
|
||||
|
||||
argc = parse_options(argc, argv, prefix, options, reflog_expire_usage, 0);
|
||||
|
||||
if (verbose)
|
||||
|
@ -338,7 +217,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
|
|||
* even in older repository. We cannot trust what's reachable
|
||||
* from reflog if the repository was pruned with older git.
|
||||
*/
|
||||
if (cmd.stalefix) {
|
||||
if (opts.stalefix) {
|
||||
struct rev_info revs;
|
||||
|
||||
repo_init_revisions(the_repository, &revs, prefix);
|
||||
|
@ -372,11 +251,11 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
|
|||
|
||||
for_each_string_list_item(item, &collected.reflogs) {
|
||||
struct expire_reflog_policy_cb cb = {
|
||||
.cmd = cmd,
|
||||
.opts = opts,
|
||||
.dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
|
||||
};
|
||||
|
||||
set_reflog_expiry_param(&cb.cmd, item->string);
|
||||
reflog_expire_options_set_refname(&cb.opts, item->string);
|
||||
status |= refs_reflog_expire(get_main_ref_store(the_repository),
|
||||
item->string, flags,
|
||||
reflog_expiry_prepare,
|
||||
|
@ -389,13 +268,13 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
|
|||
|
||||
for (i = 0; i < argc; i++) {
|
||||
char *ref;
|
||||
struct expire_reflog_policy_cb cb = { .cmd = cmd };
|
||||
struct expire_reflog_policy_cb cb = { .opts = opts };
|
||||
|
||||
if (!repo_dwim_log(the_repository, argv[i], strlen(argv[i]), NULL, &ref)) {
|
||||
status |= error(_("reflog could not be found: '%s'"), argv[i]);
|
||||
continue;
|
||||
}
|
||||
set_reflog_expiry_param(&cb.cmd, ref);
|
||||
reflog_expire_options_set_refname(&cb.opts, ref);
|
||||
status |= refs_reflog_expire(get_main_ref_store(the_repository),
|
||||
ref, flags,
|
||||
reflog_expiry_prepare,
|
||||
|
|
137
reflog.c
137
reflog.c
|
@ -2,13 +2,120 @@
|
|||
#define DISABLE_SIGN_COMPARE_WARNINGS
|
||||
|
||||
#include "git-compat-util.h"
|
||||
#include "config.h"
|
||||
#include "gettext.h"
|
||||
#include "object-store-ll.h"
|
||||
#include "parse-options.h"
|
||||
#include "reflog.h"
|
||||
#include "refs.h"
|
||||
#include "revision.h"
|
||||
#include "tree.h"
|
||||
#include "tree-walk.h"
|
||||
#include "wildmatch.h"
|
||||
|
||||
static struct reflog_expire_entry_option *find_cfg_ent(struct reflog_expire_options *opts,
|
||||
const char *pattern, size_t len)
|
||||
{
|
||||
struct reflog_expire_entry_option *ent;
|
||||
|
||||
if (!opts->entries_tail)
|
||||
opts->entries_tail = &opts->entries;
|
||||
|
||||
for (ent = opts->entries; ent; ent = ent->next)
|
||||
if (!xstrncmpz(ent->pattern, pattern, len))
|
||||
return ent;
|
||||
|
||||
FLEX_ALLOC_MEM(ent, pattern, pattern, len);
|
||||
*opts->entries_tail = ent;
|
||||
opts->entries_tail = &(ent->next);
|
||||
return ent;
|
||||
}
|
||||
|
||||
int reflog_expire_config(const char *var, const char *value,
|
||||
const struct config_context *ctx, void *cb)
|
||||
{
|
||||
struct reflog_expire_options *opts = cb;
|
||||
const char *pattern, *key;
|
||||
size_t pattern_len;
|
||||
timestamp_t expire;
|
||||
int slot;
|
||||
struct reflog_expire_entry_option *ent;
|
||||
|
||||
if (parse_config_key(var, "gc", &pattern, &pattern_len, &key) < 0)
|
||||
return git_default_config(var, value, ctx, cb);
|
||||
|
||||
if (!strcmp(key, "reflogexpire")) {
|
||||
slot = REFLOG_EXPIRE_TOTAL;
|
||||
if (git_config_expiry_date(&expire, var, value))
|
||||
return -1;
|
||||
} else if (!strcmp(key, "reflogexpireunreachable")) {
|
||||
slot = REFLOG_EXPIRE_UNREACH;
|
||||
if (git_config_expiry_date(&expire, var, value))
|
||||
return -1;
|
||||
} else
|
||||
return git_default_config(var, value, ctx, cb);
|
||||
|
||||
if (!pattern) {
|
||||
switch (slot) {
|
||||
case REFLOG_EXPIRE_TOTAL:
|
||||
opts->default_expire_total = expire;
|
||||
break;
|
||||
case REFLOG_EXPIRE_UNREACH:
|
||||
opts->default_expire_unreachable = expire;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
ent = find_cfg_ent(opts, pattern, pattern_len);
|
||||
if (!ent)
|
||||
return -1;
|
||||
switch (slot) {
|
||||
case REFLOG_EXPIRE_TOTAL:
|
||||
ent->expire_total = expire;
|
||||
break;
|
||||
case REFLOG_EXPIRE_UNREACH:
|
||||
ent->expire_unreachable = expire;
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void reflog_expire_options_set_refname(struct reflog_expire_options *cb,
|
||||
const char *ref)
|
||||
{
|
||||
struct reflog_expire_entry_option *ent;
|
||||
|
||||
if (cb->explicit_expiry == (REFLOG_EXPIRE_TOTAL|REFLOG_EXPIRE_UNREACH))
|
||||
return; /* both given explicitly -- nothing to tweak */
|
||||
|
||||
for (ent = cb->entries; ent; ent = ent->next) {
|
||||
if (!wildmatch(ent->pattern, ref, 0)) {
|
||||
if (!(cb->explicit_expiry & REFLOG_EXPIRE_TOTAL))
|
||||
cb->expire_total = ent->expire_total;
|
||||
if (!(cb->explicit_expiry & REFLOG_EXPIRE_UNREACH))
|
||||
cb->expire_unreachable = ent->expire_unreachable;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* If unconfigured, make stash never expire
|
||||
*/
|
||||
if (!strcmp(ref, "refs/stash")) {
|
||||
if (!(cb->explicit_expiry & REFLOG_EXPIRE_TOTAL))
|
||||
cb->expire_total = 0;
|
||||
if (!(cb->explicit_expiry & REFLOG_EXPIRE_UNREACH))
|
||||
cb->expire_unreachable = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
/* Nothing matched -- use the default value */
|
||||
if (!(cb->explicit_expiry & REFLOG_EXPIRE_TOTAL))
|
||||
cb->expire_total = cb->default_expire_total;
|
||||
if (!(cb->explicit_expiry & REFLOG_EXPIRE_UNREACH))
|
||||
cb->expire_unreachable = cb->default_expire_unreachable;
|
||||
}
|
||||
|
||||
/* Remember to update object flag allocation in object.h */
|
||||
#define INCOMPLETE (1u<<10)
|
||||
|
@ -252,15 +359,15 @@ int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
|
|||
struct expire_reflog_policy_cb *cb = cb_data;
|
||||
struct commit *old_commit, *new_commit;
|
||||
|
||||
if (timestamp < cb->cmd.expire_total)
|
||||
if (timestamp < cb->opts.expire_total)
|
||||
return 1;
|
||||
|
||||
old_commit = new_commit = NULL;
|
||||
if (cb->cmd.stalefix &&
|
||||
if (cb->opts.stalefix &&
|
||||
(!keep_entry(&old_commit, ooid) || !keep_entry(&new_commit, noid)))
|
||||
return 1;
|
||||
|
||||
if (timestamp < cb->cmd.expire_unreachable) {
|
||||
if (timestamp < cb->opts.expire_unreachable) {
|
||||
switch (cb->unreachable_expire_kind) {
|
||||
case UE_ALWAYS:
|
||||
return 1;
|
||||
|
@ -272,7 +379,7 @@ int should_expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
|
|||
}
|
||||
}
|
||||
|
||||
if (cb->cmd.recno && --(cb->cmd.recno) == 0)
|
||||
if (cb->opts.recno && --(cb->opts.recno) == 0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
@ -331,7 +438,7 @@ void reflog_expiry_prepare(const char *refname,
|
|||
struct commit_list *elem;
|
||||
struct commit *commit = NULL;
|
||||
|
||||
if (!cb->cmd.expire_unreachable || is_head(refname)) {
|
||||
if (!cb->opts.expire_unreachable || is_head(refname)) {
|
||||
cb->unreachable_expire_kind = UE_HEAD;
|
||||
} else {
|
||||
commit = lookup_commit_reference_gently(the_repository,
|
||||
|
@ -341,7 +448,7 @@ void reflog_expiry_prepare(const char *refname,
|
|||
cb->unreachable_expire_kind = commit ? UE_NORMAL : UE_ALWAYS;
|
||||
}
|
||||
|
||||
if (cb->cmd.expire_unreachable <= cb->cmd.expire_total)
|
||||
if (cb->opts.expire_unreachable <= cb->opts.expire_total)
|
||||
cb->unreachable_expire_kind = UE_ALWAYS;
|
||||
|
||||
switch (cb->unreachable_expire_kind) {
|
||||
|
@ -358,7 +465,7 @@ void reflog_expiry_prepare(const char *refname,
|
|||
/* For reflog_expiry_cleanup() below */
|
||||
cb->tip_commit = commit;
|
||||
}
|
||||
cb->mark_limit = cb->cmd.expire_total;
|
||||
cb->mark_limit = cb->opts.expire_total;
|
||||
mark_reachable(cb);
|
||||
}
|
||||
|
||||
|
@ -390,7 +497,7 @@ int count_reflog_ent(struct object_id *ooid UNUSED,
|
|||
timestamp_t timestamp, int tz UNUSED,
|
||||
const char *message UNUSED, void *cb_data)
|
||||
{
|
||||
struct cmd_reflog_expire_cb *cb = cb_data;
|
||||
struct reflog_expire_options *cb = cb_data;
|
||||
if (!cb->expire_total || timestamp < cb->expire_total)
|
||||
cb->recno++;
|
||||
return 0;
|
||||
|
@ -398,7 +505,7 @@ int count_reflog_ent(struct object_id *ooid UNUSED,
|
|||
|
||||
int reflog_delete(const char *rev, enum expire_reflog_flags flags, int verbose)
|
||||
{
|
||||
struct cmd_reflog_expire_cb cmd = { 0 };
|
||||
struct reflog_expire_options opts = { 0 };
|
||||
int status = 0;
|
||||
reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
|
||||
const char *spec = strstr(rev, "@{");
|
||||
|
@ -421,17 +528,17 @@ int reflog_delete(const char *rev, enum expire_reflog_flags flags, int verbose)
|
|||
|
||||
recno = strtoul(spec + 2, &ep, 10);
|
||||
if (*ep == '}') {
|
||||
cmd.recno = -recno;
|
||||
opts.recno = -recno;
|
||||
refs_for_each_reflog_ent(get_main_ref_store(the_repository),
|
||||
ref, count_reflog_ent, &cmd);
|
||||
ref, count_reflog_ent, &opts);
|
||||
} else {
|
||||
cmd.expire_total = approxidate(spec + 2);
|
||||
opts.expire_total = approxidate(spec + 2);
|
||||
refs_for_each_reflog_ent(get_main_ref_store(the_repository),
|
||||
ref, count_reflog_ent, &cmd);
|
||||
cmd.expire_total = 0;
|
||||
ref, count_reflog_ent, &opts);
|
||||
opts.expire_total = 0;
|
||||
}
|
||||
|
||||
cb.cmd = cmd;
|
||||
cb.opts = opts;
|
||||
status |= refs_reflog_expire(get_main_ref_store(the_repository), ref,
|
||||
flags,
|
||||
reflog_expiry_prepare,
|
||||
|
|
35
reflog.h
35
reflog.h
|
@ -2,13 +2,44 @@
|
|||
#define REFLOG_H
|
||||
#include "refs.h"
|
||||
|
||||
struct cmd_reflog_expire_cb {
|
||||
#define REFLOG_EXPIRE_TOTAL (1 << 0)
|
||||
#define REFLOG_EXPIRE_UNREACH (1 << 1)
|
||||
|
||||
struct reflog_expire_entry_option {
|
||||
struct reflog_expire_entry_option *next;
|
||||
timestamp_t expire_total;
|
||||
timestamp_t expire_unreachable;
|
||||
char pattern[FLEX_ARRAY];
|
||||
};
|
||||
|
||||
struct reflog_expire_options {
|
||||
struct reflog_expire_entry_option *entries, **entries_tail;
|
||||
int stalefix;
|
||||
int explicit_expiry;
|
||||
timestamp_t default_expire_total;
|
||||
timestamp_t expire_total;
|
||||
timestamp_t default_expire_unreachable;
|
||||
timestamp_t expire_unreachable;
|
||||
int recno;
|
||||
};
|
||||
#define REFLOG_EXPIRE_OPTIONS_INIT(now) { \
|
||||
.default_expire_total = now - 30 * 24 * 3600, \
|
||||
.default_expire_unreachable = now - 90 * 24 * 3600, \
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse the reflog expire configuration. This should be used with
|
||||
* `repo_config()`.
|
||||
*/
|
||||
int reflog_expire_config(const char *var, const char *value,
|
||||
const struct config_context *ctx, void *cb);
|
||||
|
||||
/*
|
||||
* Adapt the options so that they apply to the given refname. This applies any
|
||||
* per-reference reflog expiry configuration that may exist to the options.
|
||||
*/
|
||||
void reflog_expire_options_set_refname(struct reflog_expire_options *cb,
|
||||
const char *refname);
|
||||
|
||||
struct expire_reflog_policy_cb {
|
||||
enum {
|
||||
|
@ -18,7 +49,7 @@ struct expire_reflog_policy_cb {
|
|||
} unreachable_expire_kind;
|
||||
struct commit_list *mark_list;
|
||||
unsigned long mark_limit;
|
||||
struct cmd_reflog_expire_cb cmd;
|
||||
struct reflog_expire_options opts;
|
||||
struct commit *tip_commit;
|
||||
struct commit_list *tips;
|
||||
unsigned int dry_run:1;
|
||||
|
|
|
@ -475,6 +475,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
|
||||
|
|
Loading…
Reference in New Issue