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 */