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 <gitster@pobox.com>
Junio C Hamano committed
Jun 15, 2008 at 23:48 UTC
3cb22b8efe2cab60dc0ef5e265c414bd826d83ef
1 file changed
+126
-19
builtin-reflog.c
+126
-19
@@ -269,7 +269,9 @@ static int expire_reflog(const char *ref, const unsigned char *sha1, int unused,
269
int status = 0;
270
271
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
275
* getting updated.
276
*/
277
lock = lock_any_ref_for_update(ref, sha1, 0);
@@ -331,21 +333,119 @@ static int collect_reflog(const char *ref, const unsigned char *sha1, int unused
333
return 0;
334
}
335
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)
345
{
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;
370
return 0;
371
}
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
+ }
410
return 0;
411
}
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;
449
}
450
451
static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
@@ -353,6 +453,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
453
struct cmd_reflog_expire_cb cb;
454
unsigned long now = time(NULL);
455
int i, status, do_all;
456
+ int explicit_expiry = 0;
457
458
git_config(reflog_expire_config, NULL);
459
@@ -367,20 +468,18 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
468
cb.expire_total = default_reflog_expire;
469
cb.expire_unreachable = default_reflog_expire_unreachable;
470
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
-
471
for (i = 1; i < argc; i++) {
472
const char *arg = argv[i];
473
if (!strcmp(arg, "--dry-run") || !strcmp(arg, "-n"))
474
cb.dry_run = 1;
380
- else if (!prefixcmp(arg, "--expire="))
475
+ else if (!prefixcmp(arg, "--expire=")) {
476
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=")) {
480
cb.expire_unreachable = approxidate(arg + 21);
481
+ explicit_expiry |= EXPIRE_UNREACH;
482
+ }
483
else if (!strcmp(arg, "--stale-fix"))
484
cb.stalefix = 1;
485
else if (!strcmp(arg, "--rewrite"))
@@ -400,6 +499,12 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
499
else
500
break;
501
}
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
+ */
508
if (cb.stalefix) {
509
init_revisions(&cb.revs, prefix);
510
if (cb.verbose)
@@ -417,6 +522,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
522
for_each_reflog(collect_reflog, &collected);
523
for (i = 0; i < collected.nr; i++) {
524
struct collected_reflog *e = collected.e[i];
525
+ set_reflog_expiry_param(&cb, explicit_expiry, e->reflog);
526
status |= expire_reflog(e->reflog, e->sha1, 0, &cb);
527
free(e);
528
}
@@ -430,6 +536,7 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
536
status |= error("%s points nowhere!", ref);
537
continue;
538
}
539
+ set_reflog_expiry_param(&cb, explicit_expiry, ref);
540
status |= expire_reflog(ref, sha1, 0, &cb);
541
}
542
return status;