Skip to content

Commit 7be9467

Browse files
committed
Merge branch 'jc/reflog-expire' into maint
* jc/reflog-expire: Make default expiration period of reflog used for stash infinite Per-ref reflog expiry configuration
2 parents 1e040c0 + 60bce2b commit 7be9467

File tree

1 file changed

+137
-19
lines changed

1 file changed

+137
-19
lines changed

builtin-reflog.c

Lines changed: 137 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,138 @@ 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+
/*
445+
* If unconfigured, make stash never expire
446+
*/
447+
if (!strcmp(ref, "refs/stash")) {
448+
if (!(slot & EXPIRE_TOTAL))
449+
cb->expire_total = 0;
450+
if (!(slot & EXPIRE_UNREACH))
451+
cb->expire_unreachable = 0;
452+
return;
453+
}
454+
455+
/* Nothing matched -- use the default value */
456+
if (!(slot & EXPIRE_TOTAL))
457+
cb->expire_total = default_reflog_expire;
458+
if (!(slot & EXPIRE_UNREACH))
459+
cb->expire_unreachable = default_reflog_expire_unreachable;
349460
}
350461

351462
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
352463
{
353464
struct cmd_reflog_expire_cb cb;
354465
unsigned long now = time(NULL);
355466
int i, status, do_all;
467+
int explicit_expiry = 0;
356468

357469
git_config(reflog_expire_config, NULL);
358470

@@ -367,20 +479,18 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
367479
cb.expire_total = default_reflog_expire;
368480
cb.expire_unreachable = default_reflog_expire_unreachable;
369481

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-
376482
for (i = 1; i < argc; i++) {
377483
const char *arg = argv[i];
378484
if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
379485
cb.dry_run = 1;
380-
else if (!prefixcmp(arg, "--expire="))
486+
else if (!prefixcmp(arg, "--expire=")) {
381487
cb.expire_total = approxidate(arg + 9);
382-
else if (!prefixcmp(arg, "--expire-unreachable="))
488+
explicit_expiry |= EXPIRE_TOTAL;
489+
}
490+
else if (!prefixcmp(arg, "--expire-unreachable=")) {
383491
cb.expire_unreachable = approxidate(arg + 21);
492+
explicit_expiry |= EXPIRE_UNREACH;
493+
}
384494
else if (!strcmp(arg, "--stale-fix"))
385495
cb.stalefix = 1;
386496
else if (!strcmp(arg, "--rewrite"))
@@ -400,6 +510,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
400510
else
401511
break;
402512
}
513+
514+
/*
515+
* We can trust the commits and objects reachable from refs
516+
* even in older repository. We cannot trust what's reachable
517+
* from reflog if the repository was pruned with older git.
518+
*/
403519
if (cb.stalefix) {
404520
init_revisions(&cb.revs, prefix);
405521
if (cb.verbose)
@@ -417,6 +533,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
417533
for_each_reflog(collect_reflog, &collected);
418534
for (i = 0; i < collected.nr; i++) {
419535
struct collected_reflog *e = collected.e[i];
536+
set_reflog_expiry_param(&cb, explicit_expiry, e->reflog);
420537
status |= expire_reflog(e->reflog, e->sha1, 0, &cb);
421538
free(e);
422539
}
@@ -430,6 +547,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
430547
status |= error("%s points nowhere!", ref);
431548
continue;
432549
}
550+
set_reflog_expiry_param(&cb, explicit_expiry, ref);
433551
status |= expire_reflog(ref, sha1, 0, &cb);
434552
}
435553
return status;

0 commit comments

Comments
 (0)