reflog_expire(): new function in the reference API
Move expire_reflog() into refs.c and rename it to reflog_expire(). Turn the three policy functions into function pointers that are passed into reflog_expire(). Add function prototypes and documentation to refs.h. [jc: squashed in $gmane/261582, drop "extern" in function definition] Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu> Reviewed-by: Stefan Beller <sbeller@google.com> Tweaked-by: Ramsay Jones Signed-off-by: Junio C Hamano <gitster@pobox.com>
Michael Haggerty committed
Dec 12, 2014 at 09:56 UTC
fa5b1830b0605b39295631f6e167ddb133f2dbde
3 files changed
+190
-133
builtin/reflog.c
+15
-133
@@ -20,13 +20,6 @@ static const char reflog_delete_usage[] =
20
static unsigned long default_reflog_expire;
21
static unsigned long default_reflog_expire_unreachable;
22
23
-enum expire_reflog_flags {
24
- EXPIRE_REFLOGS_DRY_RUN = 1 << 0,
25
- EXPIRE_REFLOGS_UPDATE_REF = 1 << 1,
26
- EXPIRE_REFLOGS_VERBOSE = 1 << 2,
27
- EXPIRE_REFLOGS_REWRITE = 1 << 3
28
-};
29
-
23
struct cmd_reflog_expire_cb {
24
struct rev_info revs;
25
int stalefix;
@@ -48,13 +41,6 @@ struct expire_reflog_policy_cb {
41
struct commit_list *tips;
42
};
43
51
-struct expire_reflog_cb {
52
- unsigned int flags;
53
- void *policy_cb;
54
- FILE *newlog;
55
- unsigned char last_kept_sha1[20];
56
-};
57
-
44
struct collected_reflog {
45
unsigned char sha1[20];
46
char reflog[FLEX_ARRAY];
@@ -330,38 +316,6 @@ static int should_expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
316
return 0;
317
}
318
333
-static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
334
- const char *email, unsigned long timestamp, int tz,
335
- const char *message, void *cb_data)
336
-{
337
- struct expire_reflog_cb *cb = cb_data;
338
- struct expire_reflog_policy_cb *policy_cb = cb->policy_cb;
339
-
340
- if (cb->flags & EXPIRE_REFLOGS_REWRITE)
341
- osha1 = cb->last_kept_sha1;
342
-
343
- if (should_expire_reflog_ent(osha1, nsha1, email, timestamp, tz,
344
- message, policy_cb)) {
345
- if (!cb->newlog)
346
- printf("would prune %s", message);
347
- else if (cb->flags & EXPIRE_REFLOGS_VERBOSE)
348
- printf("prune %s", message);
349
- } else {
350
- if (cb->newlog) {
351
- char sign = (tz < 0) ? '-' : '+';
352
- int zone = (tz < 0) ? (-tz) : tz;
353
- fprintf(cb->newlog, "%s %s %s %lu %c%04d\t%s",
354
- sha1_to_hex(osha1), sha1_to_hex(nsha1),
355
- email, timestamp, sign, zone,
356
- message);
357
- hashcpy(cb->last_kept_sha1, nsha1);
358
- }
359
- if (cb->flags & EXPIRE_REFLOGS_VERBOSE)
360
- printf("keep %s", message);
361
- }
362
- return 0;
363
-}
364
-
319
static int push_tip_to_list(const char *refname, const unsigned char *sha1,
320
int flags, void *cb_data)
321
{
@@ -428,90 +382,6 @@ static void reflog_expiry_cleanup(void *cb_data)
382
}
383
}
384
431
-static int expire_reflog(const char *refname, const unsigned char *sha1,
432
- unsigned int flags, void *policy_cb_data)
433
-{
434
- static struct lock_file reflog_lock;
435
- struct expire_reflog_cb cb;
436
- struct ref_lock *lock;
437
- char *log_file;
438
- int status = 0;
439
-
440
- memset(&cb, 0, sizeof(cb));
441
- cb.flags = flags;
442
- cb.policy_cb = policy_cb_data;
443
-
444
- /*
445
- * The reflog file is locked by holding the lock on the
446
- * reference itself, plus we might need to update the
447
- * reference if --updateref was specified:
448
- */
449
- lock = lock_any_ref_for_update(refname, sha1, 0, NULL);
450
- if (!lock)
451
- return error("cannot lock ref '%s'", refname);
452
- if (!reflog_exists(refname)) {
453
- unlock_ref(lock);
454
- return 0;
455
- }
456
-
457
- log_file = git_pathdup("logs/%s", refname);
458
- if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {
459
- /*
460
- * Even though holding $GIT_DIR/logs/$reflog.lock has
461
- * no locking implications, we use the lock_file
462
- * machinery here anyway because it does a lot of the
463
- * work we need, including cleaning up if the program
464
- * exits unexpectedly.
465
- */
466
- if (hold_lock_file_for_update(&reflog_lock, log_file, 0) < 0) {
467
- struct strbuf err = STRBUF_INIT;
468
- unable_to_lock_message(log_file, errno, &err);
469
- error("%s", err.buf);
470
- strbuf_release(&err);
471
- goto failure;
472
- }
473
- cb.newlog = fdopen_lock_file(&reflog_lock, "w");
474
- if (!cb.newlog) {
475
- error("cannot fdopen %s (%s)",
476
- reflog_lock.filename.buf, strerror(errno));
477
- goto failure;
478
- }
479
- }
480
-
481
- reflog_expiry_prepare(refname, sha1, cb.policy_cb);
482
- for_each_reflog_ent(refname, expire_reflog_ent, &cb);
483
- reflog_expiry_cleanup(cb.policy_cb);
484
-
485
- if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {
486
- if (close_lock_file(&reflog_lock)) {
487
- status |= error("couldn't write %s: %s", log_file,
488
- strerror(errno));
489
- } else if ((flags & EXPIRE_REFLOGS_UPDATE_REF) &&
490
- (write_in_full(lock->lock_fd,
491
- sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
492
- write_str_in_full(lock->lock_fd, "\n") != 1 ||
493
- close_ref(lock) < 0)) {
494
- status |= error("couldn't write %s",
495
- lock->lk->filename.buf);
496
- rollback_lock_file(&reflog_lock);
497
- } else if (commit_lock_file(&reflog_lock)) {
498
- status |= error("unable to commit reflog '%s' (%s)",
499
- log_file, strerror(errno));
500
- } else if ((flags & EXPIRE_REFLOGS_UPDATE_REF) && commit_ref(lock)) {
501
- status |= error("couldn't set %s", lock->ref_name);
502
- }
503
- }
504
- free(log_file);
505
- unlock_ref(lock);
506
- return status;
507
-
508
- failure:
509
- rollback_lock_file(&reflog_lock);
510
- free(log_file);
511
- unlock_ref(lock);
512
- return -1;
513
-}
514
-
385
static int collect_reflog(const char *ref, const unsigned char *sha1, int unused, void *cb_data)
386
{
387
struct collected_reflog *e;
@@ -727,7 +597,11 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
597
for (i = 0; i < collected.nr; i++) {
598
struct collected_reflog *e = collected.e[i];
599
set_reflog_expiry_param(&cb.cmd, explicit_expiry, e->reflog);
730
- status |= expire_reflog(e->reflog, e->sha1, flags, &cb);
600
+ status |= reflog_expire(e->reflog, e->sha1, flags,
601
+ reflog_expiry_prepare,
602
+ should_expire_reflog_ent,
603
+ reflog_expiry_cleanup,
604
+ &cb);
605
free(e);
606
}
607
free(collected.e);
@@ -741,7 +615,11 @@ static int cmd_reflog_expire(int argc, const char **argv, const char *prefix)
615
continue;
616
}
617
set_reflog_expiry_param(&cb.cmd, explicit_expiry, ref);
744
- status |= expire_reflog(ref, sha1, flags, &cb);
618
+ status |= reflog_expire(ref, sha1, flags,
619
+ reflog_expiry_prepare,
620
+ should_expire_reflog_ent,
621
+ reflog_expiry_cleanup,
622
+ &cb);
623
}
624
return status;
625
}
@@ -813,7 +691,11 @@ static int cmd_reflog_delete(int argc, const char **argv, const char *prefix)
691
cb.cmd.expire_total = 0;
692
}
693
816
- status |= expire_reflog(ref, sha1, flags, &cb);
694
+ status |= reflog_expire(ref, sha1, flags,
695
+ reflog_expiry_prepare,
696
+ should_expire_reflog_ent,
697
+ reflog_expiry_cleanup,
698
+ &cb);
699
free(ref);
700
}
701
return status;
refs.c
+129
@@ -3943,3 +3943,132 @@ int ref_is_hidden(const char *refname)
3943
}
3944
return 0;
3945
}
3946
+
3947
+struct expire_reflog_cb {
3948
+ unsigned int flags;
3949
+ reflog_expiry_should_prune_fn *should_prune_fn;
3950
+ void *policy_cb;
3951
+ FILE *newlog;
3952
+ unsigned char last_kept_sha1[20];
3953
+};
3954
+
3955
+static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
3956
+ const char *email, unsigned long timestamp, int tz,
3957
+ const char *message, void *cb_data)
3958
+{
3959
+ struct expire_reflog_cb *cb = cb_data;
3960
+ struct expire_reflog_policy_cb *policy_cb = cb->policy_cb;
3961
+
3962
+ if (cb->flags & EXPIRE_REFLOGS_REWRITE)
3963
+ osha1 = cb->last_kept_sha1;
3964
+
3965
+ if ((*cb->should_prune_fn)(osha1, nsha1, email, timestamp, tz,
3966
+ message, policy_cb)) {
3967
+ if (!cb->newlog)
3968
+ printf("would prune %s", message);
3969
+ else if (cb->flags & EXPIRE_REFLOGS_VERBOSE)
3970
+ printf("prune %s", message);
3971
+ } else {
3972
+ if (cb->newlog) {
3973
+ char sign = (tz < 0) ? '-' : '+';
3974
+ int zone = (tz < 0) ? (-tz) : tz;
3975
+ fprintf(cb->newlog, "%s %s %s %lu %c%04d\t%s",
3976
+ sha1_to_hex(osha1), sha1_to_hex(nsha1),
3977
+ email, timestamp, sign, zone,
3978
+ message);
3979
+ hashcpy(cb->last_kept_sha1, nsha1);
3980
+ }
3981
+ if (cb->flags & EXPIRE_REFLOGS_VERBOSE)
3982
+ printf("keep %s", message);
3983
+ }
3984
+ return 0;
3985
+}
3986
+
3987
+int reflog_expire(const char *refname, const unsigned char *sha1,
3988
+ unsigned int flags,
3989
+ reflog_expiry_prepare_fn prepare_fn,
3990
+ reflog_expiry_should_prune_fn should_prune_fn,
3991
+ reflog_expiry_cleanup_fn cleanup_fn,
3992
+ void *policy_cb_data)
3993
+{
3994
+ static struct lock_file reflog_lock;
3995
+ struct expire_reflog_cb cb;
3996
+ struct ref_lock *lock;
3997
+ char *log_file;
3998
+ int status = 0;
3999
+
4000
+ memset(&cb, 0, sizeof(cb));
4001
+ cb.flags = flags;
4002
+ cb.policy_cb = policy_cb_data;
4003
+ cb.should_prune_fn = should_prune_fn;
4004
+
4005
+ /*
4006
+ * The reflog file is locked by holding the lock on the
4007
+ * reference itself, plus we might need to update the
4008
+ * reference if --updateref was specified:
4009
+ */
4010
+ lock = lock_any_ref_for_update(refname, sha1, 0, NULL);
4011
+ if (!lock)
4012
+ return error("cannot lock ref '%s'", refname);
4013
+ if (!reflog_exists(refname)) {
4014
+ unlock_ref(lock);
4015
+ return 0;
4016
+ }
4017
+
4018
+ log_file = git_pathdup("logs/%s", refname);
4019
+ if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {
4020
+ /*
4021
+ * Even though holding $GIT_DIR/logs/$reflog.lock has
4022
+ * no locking implications, we use the lock_file
4023
+ * machinery here anyway because it does a lot of the
4024
+ * work we need, including cleaning up if the program
4025
+ * exits unexpectedly.
4026
+ */
4027
+ if (hold_lock_file_for_update(&reflog_lock, log_file, 0) < 0) {
4028
+ struct strbuf err = STRBUF_INIT;
4029
+ unable_to_lock_message(log_file, errno, &err);
4030
+ error("%s", err.buf);
4031
+ strbuf_release(&err);
4032
+ goto failure;
4033
+ }
4034
+ cb.newlog = fdopen_lock_file(&reflog_lock, "w");
4035
+ if (!cb.newlog) {
4036
+ error("cannot fdopen %s (%s)",
4037
+ reflog_lock.filename.buf, strerror(errno));
4038
+ goto failure;
4039
+ }
4040
+ }
4041
+
4042
+ (*prepare_fn)(refname, sha1, cb.policy_cb);
4043
+ for_each_reflog_ent(refname, expire_reflog_ent, &cb);
4044
+ (*cleanup_fn)(cb.policy_cb);
4045
+
4046
+ if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {
4047
+ if (close_lock_file(&reflog_lock)) {
4048
+ status |= error("couldn't write %s: %s", log_file,
4049
+ strerror(errno));
4050
+ } else if ((flags & EXPIRE_REFLOGS_UPDATE_REF) &&
4051
+ (write_in_full(lock->lock_fd,
4052
+ sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
4053
+ write_str_in_full(lock->lock_fd, "\n") != 1 ||
4054
+ close_ref(lock) < 0)) {
4055
+ status |= error("couldn't write %s",
4056
+ lock->lk->filename.buf);
4057
+ rollback_lock_file(&reflog_lock);
4058
+ } else if (commit_lock_file(&reflog_lock)) {
4059
+ status |= error("unable to commit reflog '%s' (%s)",
4060
+ log_file, strerror(errno));
4061
+ } else if ((flags & EXPIRE_REFLOGS_UPDATE_REF) && commit_ref(lock)) {
4062
+ status |= error("couldn't set %s", lock->ref_name);
4063
+ }
4064
+ }
4065
+ free(log_file);
4066
+ unlock_ref(lock);
4067
+ return status;
4068
+
4069
+ failure:
4070
+ rollback_lock_file(&reflog_lock);
4071
+ free(log_file);
4072
+ unlock_ref(lock);
4073
+ return -1;
4074
+}
refs.h
+46
@@ -353,4 +353,50 @@ int update_ref(const char *action, const char *refname,
353
extern int parse_hide_refs_config(const char *var, const char *value, const char *);
354
extern int ref_is_hidden(const char *);
355
356
+enum expire_reflog_flags {
357
+ EXPIRE_REFLOGS_DRY_RUN = 1 << 0,
358
+ EXPIRE_REFLOGS_UPDATE_REF = 1 << 1,
359
+ EXPIRE_REFLOGS_VERBOSE = 1 << 2,
360
+ EXPIRE_REFLOGS_REWRITE = 1 << 3
361
+};
362
+
363
+/*
364
+ * The following interface is used for reflog expiration. The caller
365
+ * calls reflog_expire(), supplying it with three callback functions,
366
+ * of the following types. The callback functions define the
367
+ * expiration policy that is desired.
368
+ *
369
+ * reflog_expiry_prepare_fn -- Called once after the reference is
370
+ * locked.
371
+ *
372
+ * reflog_expiry_should_prune_fn -- Called once for each entry in the
373
+ * existing reflog. It should return true iff that entry should be
374
+ * pruned.
375
+ *
376
+ * reflog_expiry_cleanup_fn -- Called once before the reference is
377
+ * unlocked again.
378
+ */
379
+typedef void reflog_expiry_prepare_fn(const char *refname,
380
+ const unsigned char *sha1,
381
+ void *cb_data);
382
+typedef int reflog_expiry_should_prune_fn(unsigned char *osha1,
383
+ unsigned char *nsha1,
384
+ const char *email,
385
+ unsigned long timestamp, int tz,
386
+ const char *message, void *cb_data);
387
+typedef void reflog_expiry_cleanup_fn(void *cb_data);
388
+
389
+/*
390
+ * Expire reflog entries for the specified reference. sha1 is the old
391
+ * value of the reference. flags is a combination of the constants in
392
+ * enum expire_reflog_flags. The three function pointers are described
393
+ * above. On success, return zero.
394
+ */
395
+extern int reflog_expire(const char *refname, const unsigned char *sha1,
396
+ unsigned int flags,
397
+ reflog_expiry_prepare_fn prepare_fn,
398
+ reflog_expiry_should_prune_fn should_prune_fn,
399
+ reflog_expiry_cleanup_fn cleanup_fn,
400
+ void *policy_cb_data);
401
+
402
#endif /* REFS_H */