Skip to content

Commit 3cb22b8

Browse files
committed
Per-ref reflog expiry configuration
In addition to gc.reflogexpireunreachable and gc.reflogexpire, this lets you set gc.<pattern>.reflogexpireunreachable and gc.<pattern>.reflogexpire variables. When "git reflog expire" expires reflog entry for $ref, the expiry timers are taken from the first <pattern> that matches $ref (and if there isn't the global default value is used). For example, you could: [gc "refs/stash"] reflogexpire = never reflogexpireunreachable = never [gc "refs/remotes/*"] reflogexpire = 7 days reflogexpireunreachable = 3 days [gc] reflogexpire = 90 days reflogexpireunreachable = 30 days Signed-off-by: Junio C Hamano <[email protected]>
1 parent 336d09d commit 3cb22b8

File tree

1 file changed

+126
-19
lines changed

1 file changed

+126
-19
lines changed

builtin-reflog.c

Lines changed: 126 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,9 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
269269
int status = 0;
270270

271271
memset(&cb, 0, sizeof(cb));
272-
/* we take the lock for the ref itself to prevent it from
272+
273+
/*
274+
* we take the lock for the ref itself to prevent it from
273275
* getting updated.
274276
*/
275277
lock = lock_any_ref_for_update(ref, sha1, 0);
@@ -331,28 +333,127 @@ static int collect_reflog(const char *ref, const unsigned char *sha1, int unused
331333
return 0;
332334
}
333335

334-
static int reflog_expire_config(const char *var, const char *value, void *cb)
336+
static struct reflog_expire_cfg {
337+
struct reflog_expire_cfg *next;
338+
unsigned long expire_total;
339+
unsigned long expire_unreachable;
340+
size_t len;
341+
char pattern[FLEX_ARRAY];
342+
} *reflog_expire_cfg, **reflog_expire_cfg_tail;
343+
344+
static struct reflog_expire_cfg *find_cfg_ent(const char *pattern, size_t len)
335345
{
336-
if (!strcmp(var, "gc.reflogexpire")) {
337-
if (!value)
338-
config_error_nonbool(var);
339-
default_reflog_expire = approxidate(value);
346+
struct reflog_expire_cfg *ent;
347+
348+
if (!reflog_expire_cfg_tail)
349+
reflog_expire_cfg_tail = &reflog_expire_cfg;
350+
351+
for (ent = reflog_expire_cfg; ent; ent = ent->next)
352+
if (ent->len == len &&
353+
!memcmp(ent->pattern, pattern, len))
354+
return ent;
355+
356+
ent = xcalloc(1, (sizeof(*ent) + len));
357+
memcpy(ent->pattern, pattern, len);
358+
ent->len = len;
359+
*reflog_expire_cfg_tail = ent;
360+
reflog_expire_cfg_tail = &(ent->next);
361+
return ent;
362+
}
363+
364+
static int parse_expire_cfg_value(const char *var, const char *value, unsigned long *expire)
365+
{
366+
if (!value)
367+
return config_error_nonbool(var);
368+
if (!strcmp(value, "never") || !strcmp(value, "false")) {
369+
*expire = 0;
340370
return 0;
341371
}
342-
if (!strcmp(var, "gc.reflogexpireunreachable")) {
343-
if (!value)
344-
config_error_nonbool(var);
345-
default_reflog_expire_unreachable = approxidate(value);
372+
*expire = approxidate(value);
373+
return 0;
374+
}
375+
376+
/* expiry timer slot */
377+
#define EXPIRE_TOTAL 01
378+
#define EXPIRE_UNREACH 02
379+
380+
static int reflog_expire_config(const char *var, const char *value, void *cb)
381+
{
382+
const char *lastdot = strrchr(var, '.');
383+
unsigned long expire;
384+
int slot;
385+
struct reflog_expire_cfg *ent;
386+
387+
if (!lastdot || prefixcmp(var, "gc."))
388+
return git_default_config(var, value, cb);
389+
390+
if (!strcmp(lastdot, ".reflogexpire")) {
391+
slot = EXPIRE_TOTAL;
392+
if (parse_expire_cfg_value(var, value, &expire))
393+
return -1;
394+
} else if (!strcmp(lastdot, ".reflogexpireunreachable")) {
395+
slot = EXPIRE_UNREACH;
396+
if (parse_expire_cfg_value(var, value, &expire))
397+
return -1;
398+
} else
399+
return git_default_config(var, value, cb);
400+
401+
if (lastdot == var + 2) {
402+
switch (slot) {
403+
case EXPIRE_TOTAL:
404+
default_reflog_expire = expire;
405+
break;
406+
case EXPIRE_UNREACH:
407+
default_reflog_expire_unreachable = expire;
408+
break;
409+
}
346410
return 0;
347411
}
348-
return git_default_config(var, value, cb);
412+
413+
ent = find_cfg_ent(var + 3, lastdot - (var+3));
414+
if (!ent)
415+
return -1;
416+
switch (slot) {
417+
case EXPIRE_TOTAL:
418+
ent->expire_total = expire;
419+
break;
420+
case EXPIRE_UNREACH:
421+
ent->expire_unreachable = expire;
422+
break;
423+
}
424+
return 0;
425+
}
426+
427+
static void set_reflog_expiry_param(struct cmd_reflog_expire_cb *cb, int slot, const char *ref)
428+
{
429+
struct reflog_expire_cfg *ent;
430+
431+
if (slot == (EXPIRE_TOTAL|EXPIRE_UNREACH))
432+
return; /* both given explicitly -- nothing to tweak */
433+
434+
for (ent = reflog_expire_cfg; ent; ent = ent->next) {
435+
if (!fnmatch(ent->pattern, ref, 0)) {
436+
if (!(slot & EXPIRE_TOTAL))
437+
cb->expire_total = ent->expire_total;
438+
if (!(slot & EXPIRE_UNREACH))
439+
cb->expire_unreachable = ent->expire_unreachable;
440+
return;
441+
}
442+
}
443+
444+
/* Nothing matched -- use the default value */
445+
if (!(slot & EXPIRE_TOTAL))
446+
cb->expire_total = default_reflog_expire;
447+
if (!(slot & EXPIRE_UNREACH))
448+
cb->expire_unreachable = default_reflog_expire_unreachable;
349449
}
350450

351451
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
352452
{
353453
struct cmd_reflog_expire_cb cb;
354454
unsigned long now = time(NULL);
355455
int i, status, do_all;
456+
int explicit_expiry = 0;
356457

357458
git_config(reflog_expire_config, NULL);
358459

@@ -367,20 +468,18 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
367468
cb.expire_total = default_reflog_expire;
368469
cb.expire_unreachable = default_reflog_expire_unreachable;
369470

370-
/*
371-
* We can trust the commits and objects reachable from refs
372-
* even in older repository. We cannot trust what's reachable
373-
* from reflog if the repository was pruned with older git.
374-
*/
375-
376471
for (i = 1; i < argc; i++) {
377472
const char *arg = argv[i];
378473
if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
379474
cb.dry_run = 1;
380-
else if (!prefixcmp(arg, "--expire="))
475+
else if (!prefixcmp(arg, "--expire=")) {
381476
cb.expire_total = approxidate(arg + 9);
382-
else if (!prefixcmp(arg, "--expire-unreachable="))
477+
explicit_expiry |= EXPIRE_TOTAL;
478+
}
479+
else if (!prefixcmp(arg, "--expire-unreachable=")) {
383480
cb.expire_unreachable = approxidate(arg + 21);
481+
explicit_expiry |= EXPIRE_UNREACH;
482+
}
384483
else if (!strcmp(arg, "--stale-fix"))
385484
cb.stalefix = 1;
386485
else if (!strcmp(arg, "--rewrite"))
@@ -400,6 +499,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
400499
else
401500
break;
402501
}
502+
503+
/*
504+
* We can trust the commits and objects reachable from refs
505+
* even in older repository. We cannot trust what's reachable
506+
* from reflog if the repository was pruned with older git.
507+
*/
403508
if (cb.stalefix) {
404509
init_revisions(&cb.revs, prefix);
405510
if (cb.verbose)
@@ -417,6 +522,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
417522
for_each_reflog(collect_reflog, &collected);
418523
for (i = 0; i < collected.nr; i++) {
419524
struct collected_reflog *e = collected.e[i];
525+
set_reflog_expiry_param(&cb, explicit_expiry, e->reflog);
420526
status |= expire_reflog(e->reflog, e->sha1, 0, &cb);
421527
free(e);
422528
}
@@ -430,6 +536,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
430536
status |= error("%s points nowhere!", ref);
431537
continue;
432538
}
539+
set_reflog_expiry_param(&cb, explicit_expiry, ref);
433540
status |= expire_reflog(ref, sha1, 0, &cb);
434541
}
435542
return status;

0 commit comments

Comments
 (0)