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
|
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
|
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.
|
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]
|
need to iterate across many references. See linkgit:git-pack-refs[1]
|
||||||
for more information.
|
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
|
OPTIONS
|
||||||
-------
|
-------
|
||||||
--auto::
|
--auto::
|
||||||
|
|
72
builtin/gc.c
72
builtin/gc.c
|
@ -33,6 +33,7 @@
|
||||||
#include "pack.h"
|
#include "pack.h"
|
||||||
#include "pack-objects.h"
|
#include "pack-objects.h"
|
||||||
#include "path.h"
|
#include "path.h"
|
||||||
|
#include "reflog.h"
|
||||||
#include "blob.h"
|
#include "blob.h"
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
#include "promisor-remote.h"
|
#include "promisor-remote.h"
|
||||||
|
@ -53,7 +54,6 @@ static const char * const builtin_gc_usage[] = {
|
||||||
|
|
||||||
static timestamp_t gc_log_expire_time;
|
static timestamp_t gc_log_expire_time;
|
||||||
|
|
||||||
static struct strvec reflog = STRVEC_INIT;
|
|
||||||
static struct strvec repack = STRVEC_INIT;
|
static struct strvec repack = STRVEC_INIT;
|
||||||
static struct strvec prune = STRVEC_INIT;
|
static struct strvec prune = STRVEC_INIT;
|
||||||
static struct strvec prune_worktrees = 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);
|
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)
|
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))
|
if (cfg->pack_refs && maintenance_task_pack_refs(opts, cfg))
|
||||||
die(FAILED_RUN, "pack-refs");
|
die(FAILED_RUN, "pack-refs");
|
||||||
|
if (cfg->prune_reflogs && maintenance_task_reflog_expire(opts, cfg))
|
||||||
if (cfg->prune_reflogs) {
|
die(FAILED_RUN, "reflog");
|
||||||
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]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int cmd_gc(int argc,
|
int cmd_gc(int argc,
|
||||||
|
@ -723,7 +768,6 @@ struct repository *repo UNUSED)
|
||||||
show_usage_with_options_if_asked(argc, argv,
|
show_usage_with_options_if_asked(argc, argv,
|
||||||
builtin_gc_usage, builtin_gc_options);
|
builtin_gc_usage, builtin_gc_options);
|
||||||
|
|
||||||
strvec_pushl(&reflog, "reflog", "expire", "--all", NULL);
|
|
||||||
strvec_pushl(&repack, "repack", "-d", "-l", NULL);
|
strvec_pushl(&repack, "repack", "-d", "-l", NULL);
|
||||||
strvec_pushl(&prune, "prune", "--expire", NULL);
|
strvec_pushl(&prune, "prune", "--expire", NULL);
|
||||||
strvec_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL);
|
strvec_pushl(&prune_worktrees, "worktree", "prune", "--expire", NULL);
|
||||||
|
@ -1412,6 +1456,7 @@ enum maintenance_task_label {
|
||||||
TASK_GC,
|
TASK_GC,
|
||||||
TASK_COMMIT_GRAPH,
|
TASK_COMMIT_GRAPH,
|
||||||
TASK_PACK_REFS,
|
TASK_PACK_REFS,
|
||||||
|
TASK_REFLOG_EXPIRE,
|
||||||
|
|
||||||
/* Leave as final value */
|
/* Leave as final value */
|
||||||
TASK__COUNT
|
TASK__COUNT
|
||||||
|
@ -1448,6 +1493,11 @@ static struct maintenance_task tasks[] = {
|
||||||
maintenance_task_pack_refs,
|
maintenance_task_pack_refs,
|
||||||
pack_refs_condition,
|
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_)
|
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
|
NULL
|
||||||
};
|
};
|
||||||
|
|
||||||
static timestamp_t default_reflog_expire;
|
|
||||||
static timestamp_t default_reflog_expire_unreachable;
|
|
||||||
|
|
||||||
struct worktree_reflogs {
|
struct worktree_reflogs {
|
||||||
struct worktree *worktree;
|
struct worktree *worktree;
|
||||||
struct string_list reflogs;
|
struct string_list reflogs;
|
||||||
|
@ -100,131 +97,19 @@ static int collect_reflog(const char *ref, void *cb_data)
|
||||||
return 0;
|
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,
|
static int expire_unreachable_callback(const struct option *opt,
|
||||||
const char *arg,
|
const char *arg,
|
||||||
int unset)
|
int unset)
|
||||||
{
|
{
|
||||||
struct cmd_reflog_expire_cb *cmd = opt->value;
|
struct reflog_expire_options *opts = opt->value;
|
||||||
|
|
||||||
BUG_ON_OPT_NEG(unset);
|
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'"),
|
die(_("invalid timestamp '%s' given to '--%s'"),
|
||||||
arg, opt->long_name);
|
arg, opt->long_name);
|
||||||
|
|
||||||
cmd->explicit_expiry |= EXPIRE_UNREACH;
|
opts->explicit_expiry |= REFLOG_EXPIRE_UNREACH;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -232,15 +117,15 @@ static int expire_total_callback(const struct option *opt,
|
||||||
const char *arg,
|
const char *arg,
|
||||||
int unset)
|
int unset)
|
||||||
{
|
{
|
||||||
struct cmd_reflog_expire_cb *cmd = opt->value;
|
struct reflog_expire_options *opts = opt->value;
|
||||||
|
|
||||||
BUG_ON_OPT_NEG(unset);
|
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'"),
|
die(_("invalid timestamp '%s' given to '--%s'"),
|
||||||
arg, opt->long_name);
|
arg, opt->long_name);
|
||||||
|
|
||||||
cmd->explicit_expiry |= EXPIRE_TOTAL;
|
opts->explicit_expiry |= REFLOG_EXPIRE_TOTAL;
|
||||||
return 0;
|
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,
|
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix,
|
||||||
struct repository *repo UNUSED)
|
struct repository *repo UNUSED)
|
||||||
{
|
{
|
||||||
struct cmd_reflog_expire_cb cmd = { 0 };
|
|
||||||
timestamp_t now = time(NULL);
|
timestamp_t now = time(NULL);
|
||||||
|
struct reflog_expire_options opts = REFLOG_EXPIRE_OPTIONS_INIT(now);
|
||||||
int i, status, do_all, single_worktree = 0;
|
int i, status, do_all, single_worktree = 0;
|
||||||
unsigned int flags = 0;
|
unsigned int flags = 0;
|
||||||
int verbose = 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"),
|
N_("update the reference to the value of the top reflog entry"),
|
||||||
EXPIRE_REFLOGS_UPDATE_REF),
|
EXPIRE_REFLOGS_UPDATE_REF),
|
||||||
OPT_BOOL(0, "verbose", &verbose, N_("print extra information on screen")),
|
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"),
|
N_("prune entries older than the specified time"),
|
||||||
PARSE_OPT_NONEG,
|
PARSE_OPT_NONEG,
|
||||||
expire_total_callback),
|
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"),
|
N_("prune entries older than <time> that are not reachable from the current tip of the branch"),
|
||||||
PARSE_OPT_NONEG,
|
PARSE_OPT_NONEG,
|
||||||
expire_unreachable_callback),
|
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")),
|
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, "all", &do_all, N_("process the reflogs of all references")),
|
||||||
OPT_BOOL(0, "single-worktree", &single_worktree,
|
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()
|
OPT_END()
|
||||||
};
|
};
|
||||||
|
|
||||||
default_reflog_expire_unreachable = now - 30 * 24 * 3600;
|
git_config(reflog_expire_config, &opts);
|
||||||
default_reflog_expire = now - 90 * 24 * 3600;
|
|
||||||
git_config(reflog_expire_config, NULL);
|
|
||||||
|
|
||||||
save_commit_buffer = 0;
|
save_commit_buffer = 0;
|
||||||
do_all = status = 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);
|
argc = parse_options(argc, argv, prefix, options, reflog_expire_usage, 0);
|
||||||
|
|
||||||
if (verbose)
|
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
|
* even in older repository. We cannot trust what's reachable
|
||||||
* from reflog if the repository was pruned with older git.
|
* from reflog if the repository was pruned with older git.
|
||||||
*/
|
*/
|
||||||
if (cmd.stalefix) {
|
if (opts.stalefix) {
|
||||||
struct rev_info revs;
|
struct rev_info revs;
|
||||||
|
|
||||||
repo_init_revisions(the_repository, &revs, prefix);
|
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) {
|
for_each_string_list_item(item, &collected.reflogs) {
|
||||||
struct expire_reflog_policy_cb cb = {
|
struct expire_reflog_policy_cb cb = {
|
||||||
.cmd = cmd,
|
.opts = opts,
|
||||||
.dry_run = !!(flags & EXPIRE_REFLOGS_DRY_RUN),
|
.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),
|
status |= refs_reflog_expire(get_main_ref_store(the_repository),
|
||||||
item->string, flags,
|
item->string, flags,
|
||||||
reflog_expiry_prepare,
|
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++) {
|
for (i = 0; i < argc; i++) {
|
||||||
char *ref;
|
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)) {
|
if (!repo_dwim_log(the_repository, argv[i], strlen(argv[i]), NULL, &ref)) {
|
||||||
status |= error(_("reflog could not be found: '%s'"), argv[i]);
|
status |= error(_("reflog could not be found: '%s'"), argv[i]);
|
||||||
continue;
|
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),
|
status |= refs_reflog_expire(get_main_ref_store(the_repository),
|
||||||
ref, flags,
|
ref, flags,
|
||||||
reflog_expiry_prepare,
|
reflog_expiry_prepare,
|
||||||
|
|
137
reflog.c
137
reflog.c
|
@ -2,13 +2,120 @@
|
||||||
#define DISABLE_SIGN_COMPARE_WARNINGS
|
#define DISABLE_SIGN_COMPARE_WARNINGS
|
||||||
|
|
||||||
#include "git-compat-util.h"
|
#include "git-compat-util.h"
|
||||||
|
#include "config.h"
|
||||||
#include "gettext.h"
|
#include "gettext.h"
|
||||||
#include "object-store-ll.h"
|
#include "object-store-ll.h"
|
||||||
|
#include "parse-options.h"
|
||||||
#include "reflog.h"
|
#include "reflog.h"
|
||||||
#include "refs.h"
|
#include "refs.h"
|
||||||
#include "revision.h"
|
#include "revision.h"
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
#include "tree-walk.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 */
|
/* Remember to update object flag allocation in object.h */
|
||||||
#define INCOMPLETE (1u<<10)
|
#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 expire_reflog_policy_cb *cb = cb_data;
|
||||||
struct commit *old_commit, *new_commit;
|
struct commit *old_commit, *new_commit;
|
||||||
|
|
||||||
if (timestamp < cb->cmd.expire_total)
|
if (timestamp < cb->opts.expire_total)
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
old_commit = new_commit = NULL;
|
old_commit = new_commit = NULL;
|
||||||
if (cb->cmd.stalefix &&
|
if (cb->opts.stalefix &&
|
||||||
(!keep_entry(&old_commit, ooid) || !keep_entry(&new_commit, noid)))
|
(!keep_entry(&old_commit, ooid) || !keep_entry(&new_commit, noid)))
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
if (timestamp < cb->cmd.expire_unreachable) {
|
if (timestamp < cb->opts.expire_unreachable) {
|
||||||
switch (cb->unreachable_expire_kind) {
|
switch (cb->unreachable_expire_kind) {
|
||||||
case UE_ALWAYS:
|
case UE_ALWAYS:
|
||||||
return 1;
|
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 1;
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -331,7 +438,7 @@ void reflog_expiry_prepare(const char *refname,
|
||||||
struct commit_list *elem;
|
struct commit_list *elem;
|
||||||
struct commit *commit = NULL;
|
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;
|
cb->unreachable_expire_kind = UE_HEAD;
|
||||||
} else {
|
} else {
|
||||||
commit = lookup_commit_reference_gently(the_repository,
|
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;
|
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;
|
cb->unreachable_expire_kind = UE_ALWAYS;
|
||||||
|
|
||||||
switch (cb->unreachable_expire_kind) {
|
switch (cb->unreachable_expire_kind) {
|
||||||
|
@ -358,7 +465,7 @@ void reflog_expiry_prepare(const char *refname,
|
||||||
/* For reflog_expiry_cleanup() below */
|
/* For reflog_expiry_cleanup() below */
|
||||||
cb->tip_commit = commit;
|
cb->tip_commit = commit;
|
||||||
}
|
}
|
||||||
cb->mark_limit = cb->cmd.expire_total;
|
cb->mark_limit = cb->opts.expire_total;
|
||||||
mark_reachable(cb);
|
mark_reachable(cb);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -390,7 +497,7 @@ int count_reflog_ent(struct object_id *ooid UNUSED,
|
||||||
timestamp_t timestamp, int tz UNUSED,
|
timestamp_t timestamp, int tz UNUSED,
|
||||||
const char *message UNUSED, void *cb_data)
|
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)
|
if (!cb->expire_total || timestamp < cb->expire_total)
|
||||||
cb->recno++;
|
cb->recno++;
|
||||||
return 0;
|
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)
|
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;
|
int status = 0;
|
||||||
reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
|
reflog_expiry_should_prune_fn *should_prune_fn = should_expire_reflog_ent;
|
||||||
const char *spec = strstr(rev, "@{");
|
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);
|
recno = strtoul(spec + 2, &ep, 10);
|
||||||
if (*ep == '}') {
|
if (*ep == '}') {
|
||||||
cmd.recno = -recno;
|
opts.recno = -recno;
|
||||||
refs_for_each_reflog_ent(get_main_ref_store(the_repository),
|
refs_for_each_reflog_ent(get_main_ref_store(the_repository),
|
||||||
ref, count_reflog_ent, &cmd);
|
ref, count_reflog_ent, &opts);
|
||||||
} else {
|
} else {
|
||||||
cmd.expire_total = approxidate(spec + 2);
|
opts.expire_total = approxidate(spec + 2);
|
||||||
refs_for_each_reflog_ent(get_main_ref_store(the_repository),
|
refs_for_each_reflog_ent(get_main_ref_store(the_repository),
|
||||||
ref, count_reflog_ent, &cmd);
|
ref, count_reflog_ent, &opts);
|
||||||
cmd.expire_total = 0;
|
opts.expire_total = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
cb.cmd = cmd;
|
cb.opts = opts;
|
||||||
status |= refs_reflog_expire(get_main_ref_store(the_repository), ref,
|
status |= refs_reflog_expire(get_main_ref_store(the_repository), ref,
|
||||||
flags,
|
flags,
|
||||||
reflog_expiry_prepare,
|
reflog_expiry_prepare,
|
||||||
|
|
35
reflog.h
35
reflog.h
|
@ -2,13 +2,44 @@
|
||||||
#define REFLOG_H
|
#define REFLOG_H
|
||||||
#include "refs.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 stalefix;
|
||||||
int explicit_expiry;
|
int explicit_expiry;
|
||||||
|
timestamp_t default_expire_total;
|
||||||
timestamp_t expire_total;
|
timestamp_t expire_total;
|
||||||
|
timestamp_t default_expire_unreachable;
|
||||||
timestamp_t expire_unreachable;
|
timestamp_t expire_unreachable;
|
||||||
int recno;
|
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 {
|
struct expire_reflog_policy_cb {
|
||||||
enum {
|
enum {
|
||||||
|
@ -18,7 +49,7 @@ struct expire_reflog_policy_cb {
|
||||||
} unreachable_expire_kind;
|
} unreachable_expire_kind;
|
||||||
struct commit_list *mark_list;
|
struct commit_list *mark_list;
|
||||||
unsigned long mark_limit;
|
unsigned long mark_limit;
|
||||||
struct cmd_reflog_expire_cb cmd;
|
struct reflog_expire_options opts;
|
||||||
struct commit *tip_commit;
|
struct commit *tip_commit;
|
||||||
struct commit_list *tips;
|
struct commit_list *tips;
|
||||||
unsigned int dry_run:1;
|
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_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_expect_success '--auto and --schedule incompatible' '
|
||||||
test_must_fail git maintenance run --auto --schedule=daily 2>err &&
|
test_must_fail git maintenance run --auto --schedule=daily 2>err &&
|
||||||
test_grep "at most one" err
|
test_grep "at most one" err
|
||||||
|
|
Loading…
Reference in New Issue