Merge branch 'mh/reflog-expire' into mh/ref-trans-value-check
authorJunio C Hamano <gitster@pobox.com>
Mon, 9 Feb 2015 22:37:01 +0000 (14:37 -0800)
committerJunio C Hamano <gitster@pobox.com>
Mon, 9 Feb 2015 22:37:01 +0000 (14:37 -0800)
* mh/reflog-expire: (24 commits)
refs.c: let fprintf handle the formatting
refs.c: don't expose the internal struct ref_lock in the header file
lock_any_ref_for_update(): inline function
refs.c: remove unlock_ref/close_ref/commit_ref from the refs api
reflog_expire(): new function in the reference API
expire_reflog(): treat the policy callback data as opaque
Move newlog and last_kept_sha1 to "struct expire_reflog_cb"
expire_reflog(): move rewrite to flags argument
expire_reflog(): move verbose to flags argument
expire_reflog(): pass flags through to expire_reflog_ent()
struct expire_reflog_cb: a new callback data type
Rename expire_reflog_cb to expire_reflog_policy_cb
expire_reflog(): move updateref to flags argument
expire_reflog(): move dry_run to flags argument
expire_reflog(): add a "flags" argument
expire_reflog(): extract two policy-related functions
Extract function should_expire_reflog_ent()
expire_reflog(): use a lock_file for rewriting the reflog file
expire_reflog(): return early if the reference has no reflog
expire_reflog(): rename "ref" parameter to "refname"
...

1  2 
refs.c
refs.h
diff --combined refs.c
index ed3b2cb405cc576f16e5b94d83683953b94e1e89,14e52caea5aadb44a9b1b84f25019437159dd460..c5fa70947f76127dc6a569da68d3ed947a128e7a
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -6,6 -6,15 +6,15 @@@
  #include "dir.h"
  #include "string-list.h"
  
+ struct ref_lock {
+       char *ref_name;
+       char *orig_ref_name;
+       struct lock_file *lk;
+       unsigned char old_sha1[20];
+       int lock_fd;
+       int force_write;
+ };
  /*
   * How to handle various characters in refnames:
   * 0: An acceptable character for refs
@@@ -1068,10 -1077,8 +1077,10 @@@ static const char PACKED_REFS_HEADER[] 
   * Return a pointer to the refname within the line (null-terminated),
   * or NULL if there was a problem.
   */
 -static const char *parse_ref_line(char *line, unsigned char *sha1)
 +static const char *parse_ref_line(struct strbuf *line, unsigned char *sha1)
  {
 +      const char *ref;
 +
        /*
         * 42: the answer to everything.
         *
         *  +1 (space in between hex and name)
         *  +1 (newline at the end of the line)
         */
 -      int len = strlen(line) - 42;
 -
 -      if (len <= 0)
 +      if (line->len <= 42)
                return NULL;
 -      if (get_sha1_hex(line, sha1) < 0)
 +
 +      if (get_sha1_hex(line->buf, sha1) < 0)
                return NULL;
 -      if (!isspace(line[40]))
 +      if (!isspace(line->buf[40]))
                return NULL;
 -      line += 41;
 -      if (isspace(*line))
 +
 +      ref = line->buf + 41;
 +      if (isspace(*ref))
                return NULL;
 -      if (line[len] != '\n')
 +
 +      if (line->buf[line->len - 1] != '\n')
                return NULL;
 -      line[len] = 0;
 +      line->buf[--line->len] = 0;
  
 -      return line;
 +      return ref;
  }
  
  /*
  static void read_packed_refs(FILE *f, struct ref_dir *dir)
  {
        struct ref_entry *last = NULL;
 -      char refline[PATH_MAX];
 +      struct strbuf line = STRBUF_INIT;
        enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled = PEELED_NONE;
  
 -      while (fgets(refline, sizeof(refline), f)) {
 +      while (strbuf_getwholeline(&line, f, '\n') != EOF) {
                unsigned char sha1[20];
                const char *refname;
 -              static const char header[] = "# pack-refs with:";
 +              const char *traits;
  
 -              if (!strncmp(refline, header, sizeof(header)-1)) {
 -                      const char *traits = refline + sizeof(header) - 1;
 +              if (skip_prefix(line.buf, "# pack-refs with:", &traits)) {
                        if (strstr(traits, " fully-peeled "))
                                peeled = PEELED_FULLY;
                        else if (strstr(traits, " peeled "))
                        continue;
                }
  
 -              refname = parse_ref_line(refline, sha1);
 +              refname = parse_ref_line(&line, sha1);
                if (refname) {
                        int flag = REF_ISPACKED;
  
                        continue;
                }
                if (last &&
 -                  refline[0] == '^' &&
 -                  strlen(refline) == PEELED_LINE_LENGTH &&
 -                  refline[PEELED_LINE_LENGTH - 1] == '\n' &&
 -                  !get_sha1_hex(refline + 1, sha1)) {
 +                  line.buf[0] == '^' &&
 +                  line.len == PEELED_LINE_LENGTH &&
 +                  line.buf[PEELED_LINE_LENGTH - 1] == '\n' &&
 +                  !get_sha1_hex(line.buf + 1, sha1)) {
                        hashcpy(last->u.value.peeled, sha1);
                        /*
                         * Regardless of what the file header said,
                        last->flag |= REF_KNOWS_PEELED;
                }
        }
 +
 +      strbuf_release(&line);
  }
  
  /*
@@@ -2094,6 -2099,16 +2103,16 @@@ int refname_match(const char *abbrev_na
        return 0;
  }
  
+ static void unlock_ref(struct ref_lock *lock)
+ {
+       /* Do not free lock->lk -- atexit() still looks at them */
+       if (lock->lk)
+               rollback_lock_file(lock->lk);
+       free(lock->ref_name);
+       free(lock->orig_ref_name);
+       free(lock);
+ }
  /* This function should make sure errno is meaningful on error */
  static struct ref_lock *verify_lock(struct ref_lock *lock,
        const unsigned char *old_sha1, int mustexist)
@@@ -2322,7 -2337,6 +2341,7 @@@ static struct ref_lock *lock_ref_sha1_b
  
        lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, lflags);
        if (lock->lock_fd < 0) {
 +              last_errno = errno;
                if (errno == ENOENT && --attempts_remaining > 0)
                        /*
                         * Maybe somebody just deleted one of the
                         * again:
                         */
                        goto retry;
 -              else
 -                      unable_to_lock_die(ref_file, errno);
 +              else {
 +                      struct strbuf err = STRBUF_INIT;
 +                      unable_to_lock_message(ref_file, errno, &err);
 +                      error("%s", err.buf);
 +                      strbuf_release(&err);
 +                      goto error_return;
 +              }
        }
        return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
  
        return NULL;
  }
  
- struct ref_lock *lock_any_ref_for_update(const char *refname,
-                                        const unsigned char *old_sha1,
-                                        int flags, int *type_p)
- {
-       return lock_ref_sha1_basic(refname, old_sha1, NULL, flags, type_p);
- }
  /*
   * Write an entry to the packed-refs file for the specified refname.
   * If peeled is non-NULL, write it as the entry's peeled value.
@@@ -2649,25 -2651,22 +2661,25 @@@ static int curate_packed_ref_fn(struct 
        return 0;
  }
  
 -int repack_without_refs(const char **refnames, int n, struct strbuf *err)
 +int repack_without_refs(struct string_list *refnames, struct strbuf *err)
  {
        struct ref_dir *packed;
        struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
 -      struct string_list_item *ref_to_delete;
 -      int i, ret, removed = 0;
 +      struct string_list_item *refname, *ref_to_delete;
 +      int ret, needs_repacking = 0, removed = 0;
  
        assert(err);
  
        /* Look for a packed ref */
 -      for (i = 0; i < n; i++)
 -              if (get_packed_ref(refnames[i]))
 +      for_each_string_list_item(refname, refnames) {
 +              if (get_packed_ref(refname->string)) {
 +                      needs_repacking = 1;
                        break;
 +              }
 +      }
  
        /* Avoid locking if we have nothing to do */
 -      if (i == n)
 +      if (!needs_repacking)
                return 0; /* no refname exists in packed refs */
  
        if (lock_packed_refs(0)) {
        packed = get_packed_refs(&ref_cache);
  
        /* Remove refnames from the cache */
 -      for (i = 0; i < n; i++)
 -              if (remove_entry(packed, refnames[i]) != -1)
 +      for_each_string_list_item(refname, refnames)
 +              if (remove_entry(packed, refname->string) != -1)
                        removed = 1;
        if (!removed) {
                /*
@@@ -2901,7 -2900,7 +2913,7 @@@ int rename_ref(const char *oldrefname, 
        return 1;
  }
  
- int close_ref(struct ref_lock *lock)
static int close_ref(struct ref_lock *lock)
  {
        if (close_lock_file(lock->lk))
                return -1;
        return 0;
  }
  
- int commit_ref(struct ref_lock *lock)
static int commit_ref(struct ref_lock *lock)
  {
        if (commit_lock_file(lock->lk))
                return -1;
        return 0;
  }
  
- void unlock_ref(struct ref_lock *lock)
- {
-       /* Do not free lock->lk -- atexit() still looks at them */
-       if (lock->lk)
-               rollback_lock_file(lock->lk);
-       free(lock->ref_name);
-       free(lock->orig_ref_name);
-       free(lock);
- }
  /*
   * copy the reflog message msg to buf, which has been allocated sufficiently
   * large, while cleaning up the whitespaces.  Especially, convert LF to space,
@@@ -3003,15 -2992,37 +3005,37 @@@ int log_ref_setup(const char *refname, 
        return 0;
  }
  
+ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
+                           const unsigned char *new_sha1,
+                           const char *committer, const char *msg)
+ {
+       int msglen, written;
+       unsigned maxlen, len;
+       char *logrec;
+       msglen = msg ? strlen(msg) : 0;
+       maxlen = strlen(committer) + msglen + 100;
+       logrec = xmalloc(maxlen);
+       len = sprintf(logrec, "%s %s %s\n",
+                     sha1_to_hex(old_sha1),
+                     sha1_to_hex(new_sha1),
+                     committer);
+       if (msglen)
+               len += copy_msg(logrec + len - 1, msg) - 1;
+       written = len <= maxlen ? write_in_full(fd, logrec, len) : -1;
+       free(logrec);
+       if (written != len)
+               return -1;
+       return 0;
+ }
  static int log_ref_write(const char *refname, const unsigned char *old_sha1,
                         const unsigned char *new_sha1, const char *msg)
  {
-       int logfd, result, written, oflags = O_APPEND | O_WRONLY;
-       unsigned maxlen, len;
-       int msglen;
+       int logfd, result, oflags = O_APPEND | O_WRONLY;
        char log_file[PATH_MAX];
-       char *logrec;
-       const char *committer;
  
        if (log_all_ref_updates < 0)
                log_all_ref_updates = !is_bare_repository();
        logfd = open(log_file, oflags);
        if (logfd < 0)
                return 0;
-       msglen = msg ? strlen(msg) : 0;
-       committer = git_committer_info(0);
-       maxlen = strlen(committer) + msglen + 100;
-       logrec = xmalloc(maxlen);
-       len = sprintf(logrec, "%s %s %s\n",
-                     sha1_to_hex(old_sha1),
-                     sha1_to_hex(new_sha1),
-                     committer);
-       if (msglen)
-               len += copy_msg(logrec + len - 1, msg) - 1;
-       written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1;
-       free(logrec);
-       if (written != len) {
+       result = log_ref_write_fd(logfd, old_sha1, new_sha1,
+                                 git_committer_info(0), msg);
+       if (result) {
                int save_errno = errno;
                close(logfd);
                error("Unable to append to %s", log_file);
@@@ -3417,54 -3418,29 +3431,54 @@@ int for_each_reflog_ent_reverse(const c
  
                        bp = find_beginning_of_line(buf, scanp);
  
 -                      if (*bp != '\n') {
 -                              strbuf_splice(&sb, 0, 0, buf, endp - buf);
 -                              if (pos)
 -                                      break; /* need to fill another block */
 -                              scanp = buf - 1; /* leave loop */
 -                      } else {
 +                      if (*bp == '\n') {
                                /*
 -                               * (bp + 1) thru endp is the beginning of the
 -                               * current line we have in sb
 +                               * The newline is the end of the previous line,
 +                               * so we know we have complete line starting
 +                               * at (bp + 1). Prefix it onto any prior data
 +                               * we collected for the line and process it.
                                 */
                                strbuf_splice(&sb, 0, 0, bp + 1, endp - (bp + 1));
                                scanp = bp;
                                endp = bp + 1;
 +                              ret = show_one_reflog_ent(&sb, fn, cb_data);
 +                              strbuf_reset(&sb);
 +                              if (ret)
 +                                      break;
 +                      } else if (!pos) {
 +                              /*
 +                               * We are at the start of the buffer, and the
 +                               * start of the file; there is no previous
 +                               * line, and we have everything for this one.
 +                               * Process it, and we can end the loop.
 +                               */
 +                              strbuf_splice(&sb, 0, 0, buf, endp - buf);
 +                              ret = show_one_reflog_ent(&sb, fn, cb_data);
 +                              strbuf_reset(&sb);
 +                              break;
                        }
 -                      ret = show_one_reflog_ent(&sb, fn, cb_data);
 -                      strbuf_reset(&sb);
 -                      if (ret)
 +
 +                      if (bp == buf) {
 +                              /*
 +                               * We are at the start of the buffer, and there
 +                               * is more file to read backwards. Which means
 +                               * we are in the middle of a line. Note that we
 +                               * may get here even if *bp was a newline; that
 +                               * just means we are at the exact end of the
 +                               * previous line, rather than some spot in the
 +                               * middle.
 +                               *
 +                               * Save away what we have to be combined with
 +                               * the data from the next read.
 +                               */
 +                              strbuf_splice(&sb, 0, 0, buf, endp - buf);
                                break;
 +                      }
                }
  
        }
        if (!ret && sb.len)
 -              ret = show_one_reflog_ent(&sb, fn, cb_data);
 +              die("BUG: reverse reflog parser had leftover data");
  
        fclose(logfp);
        strbuf_release(&sb);
@@@ -3661,31 -3637,8 +3675,8 @@@ int ref_transaction_create(struct ref_t
                           int flags, const char *msg,
                           struct strbuf *err)
  {
-       struct ref_update *update;
-       assert(err);
-       if (transaction->state != REF_TRANSACTION_OPEN)
-               die("BUG: create called for transaction that is not open");
-       if (!new_sha1 || is_null_sha1(new_sha1))
-               die("BUG: create ref with null new_sha1");
-       if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
-               strbuf_addf(err, "refusing to create ref with bad name %s",
-                           refname);
-               return -1;
-       }
-       update = add_update(transaction, refname);
-       hashcpy(update->new_sha1, new_sha1);
-       hashclr(update->old_sha1);
-       update->flags = flags;
-       update->have_old = 1;
-       if (msg)
-               update->msg = xstrdup(msg);
-       return 0;
+       return ref_transaction_update(transaction, refname, new_sha1,
+                                     null_sha1, flags, 1, msg, err);
  }
  
  int ref_transaction_delete(struct ref_transaction *transaction,
                           int flags, int have_old, const char *msg,
                           struct strbuf *err)
  {
-       struct ref_update *update;
-       assert(err);
-       if (transaction->state != REF_TRANSACTION_OPEN)
-               die("BUG: delete called for transaction that is not open");
-       if (have_old && !old_sha1)
-               die("BUG: have_old is true but old_sha1 is NULL");
-       update = add_update(transaction, refname);
-       update->flags = flags;
-       update->have_old = have_old;
-       if (have_old) {
-               assert(!is_null_sha1(old_sha1));
-               hashcpy(update->old_sha1, old_sha1);
-       }
-       if (msg)
-               update->msg = xstrdup(msg);
-       return 0;
+       return ref_transaction_update(transaction, refname, null_sha1,
+                                     old_sha1, flags, have_old, msg, err);
  }
  
  int update_ref(const char *action, const char *refname,
@@@ -3776,11 -3711,10 +3749,11 @@@ static int ref_update_reject_duplicates
  int ref_transaction_commit(struct ref_transaction *transaction,
                           struct strbuf *err)
  {
 -      int ret = 0, delnum = 0, i;
 -      const char **delnames;
 +      int ret = 0, i;
        int n = transaction->nr;
        struct ref_update **updates = transaction->updates;
 +      struct string_list refs_to_delete = STRING_LIST_INIT_NODUP;
 +      struct string_list_item *ref_to_delete;
  
        assert(err);
  
                return 0;
        }
  
 -      /* Allocate work space */
 -      delnames = xmalloc(sizeof(*delnames) * n);
 -
        /* Copy, sort, and reject duplicate refs */
        qsort(updates, n, sizeof(*updates), ref_update_compare);
        if (ref_update_reject_duplicates(updates, n, err)) {
                        }
  
                        if (!(update->flags & REF_ISPRUNING))
 -                              delnames[delnum++] = update->lock->ref_name;
 +                              string_list_append(&refs_to_delete,
 +                                                 update->lock->ref_name);
                }
        }
  
 -      if (repack_without_refs(delnames, delnum, err)) {
 +      if (repack_without_refs(&refs_to_delete, err)) {
                ret = TRANSACTION_GENERIC_ERROR;
                goto cleanup;
        }
 -      for (i = 0; i < delnum; i++)
 -              unlink_or_warn(git_path("logs/%s", delnames[i]));
 +      for_each_string_list_item(ref_to_delete, &refs_to_delete)
 +              unlink_or_warn(git_path("logs/%s", ref_to_delete->string));
        clear_loose_ref_cache(&ref_cache);
  
  cleanup:
        for (i = 0; i < n; i++)
                if (updates[i]->lock)
                        unlock_ref(updates[i]->lock);
 -      free(delnames);
 +      string_list_clear(&refs_to_delete, 0);
        return ret;
  }
  
@@@ -4009,3 -3945,129 +3982,129 @@@ int ref_is_hidden(const char *refname
        }
        return 0;
  }
+ struct expire_reflog_cb {
+       unsigned int flags;
+       reflog_expiry_should_prune_fn *should_prune_fn;
+       void *policy_cb;
+       FILE *newlog;
+       unsigned char last_kept_sha1[20];
+ };
+ static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+                            const char *email, unsigned long timestamp, int tz,
+                            const char *message, void *cb_data)
+ {
+       struct expire_reflog_cb *cb = cb_data;
+       struct expire_reflog_policy_cb *policy_cb = cb->policy_cb;
+       if (cb->flags & EXPIRE_REFLOGS_REWRITE)
+               osha1 = cb->last_kept_sha1;
+       if ((*cb->should_prune_fn)(osha1, nsha1, email, timestamp, tz,
+                                  message, policy_cb)) {
+               if (!cb->newlog)
+                       printf("would prune %s", message);
+               else if (cb->flags & EXPIRE_REFLOGS_VERBOSE)
+                       printf("prune %s", message);
+       } else {
+               if (cb->newlog) {
+                       fprintf(cb->newlog, "%s %s %s %lu %+05d\t%s",
+                               sha1_to_hex(osha1), sha1_to_hex(nsha1),
+                               email, timestamp, tz, message);
+                       hashcpy(cb->last_kept_sha1, nsha1);
+               }
+               if (cb->flags & EXPIRE_REFLOGS_VERBOSE)
+                       printf("keep %s", message);
+       }
+       return 0;
+ }
+ int reflog_expire(const char *refname, const unsigned char *sha1,
+                unsigned int flags,
+                reflog_expiry_prepare_fn prepare_fn,
+                reflog_expiry_should_prune_fn should_prune_fn,
+                reflog_expiry_cleanup_fn cleanup_fn,
+                void *policy_cb_data)
+ {
+       static struct lock_file reflog_lock;
+       struct expire_reflog_cb cb;
+       struct ref_lock *lock;
+       char *log_file;
+       int status = 0;
+       memset(&cb, 0, sizeof(cb));
+       cb.flags = flags;
+       cb.policy_cb = policy_cb_data;
+       cb.should_prune_fn = should_prune_fn;
+       /*
+        * The reflog file is locked by holding the lock on the
+        * reference itself, plus we might need to update the
+        * reference if --updateref was specified:
+        */
+       lock = lock_ref_sha1_basic(refname, sha1, NULL, 0, NULL);
+       if (!lock)
+               return error("cannot lock ref '%s'", refname);
+       if (!reflog_exists(refname)) {
+               unlock_ref(lock);
+               return 0;
+       }
+       log_file = git_pathdup("logs/%s", refname);
+       if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {
+               /*
+                * Even though holding $GIT_DIR/logs/$reflog.lock has
+                * no locking implications, we use the lock_file
+                * machinery here anyway because it does a lot of the
+                * work we need, including cleaning up if the program
+                * exits unexpectedly.
+                */
+               if (hold_lock_file_for_update(&reflog_lock, log_file, 0) < 0) {
+                       struct strbuf err = STRBUF_INIT;
+                       unable_to_lock_message(log_file, errno, &err);
+                       error("%s", err.buf);
+                       strbuf_release(&err);
+                       goto failure;
+               }
+               cb.newlog = fdopen_lock_file(&reflog_lock, "w");
+               if (!cb.newlog) {
+                       error("cannot fdopen %s (%s)",
+                             reflog_lock.filename.buf, strerror(errno));
+                       goto failure;
+               }
+       }
+       (*prepare_fn)(refname, sha1, cb.policy_cb);
+       for_each_reflog_ent(refname, expire_reflog_ent, &cb);
+       (*cleanup_fn)(cb.policy_cb);
+       if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {
+               if (close_lock_file(&reflog_lock)) {
+                       status |= error("couldn't write %s: %s", log_file,
+                                       strerror(errno));
+               } else if ((flags & EXPIRE_REFLOGS_UPDATE_REF) &&
+                       (write_in_full(lock->lock_fd,
+                               sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
+                        write_str_in_full(lock->lock_fd, "\n") != 1 ||
+                        close_ref(lock) < 0)) {
+                       status |= error("couldn't write %s",
+                                       lock->lk->filename.buf);
+                       rollback_lock_file(&reflog_lock);
+               } else if (commit_lock_file(&reflog_lock)) {
+                       status |= error("unable to commit reflog '%s' (%s)",
+                                       log_file, strerror(errno));
+               } else if ((flags & EXPIRE_REFLOGS_UPDATE_REF) && commit_ref(lock)) {
+                       status |= error("couldn't set %s", lock->ref_name);
+               }
+       }
+       free(log_file);
+       unlock_ref(lock);
+       return status;
+  failure:
+       rollback_lock_file(&reflog_lock);
+       free(log_file);
+       unlock_ref(lock);
+       return -1;
+ }
diff --combined refs.h
index 405c6572f1135d06b68b6f60543e67343b117227,fc88ba6b3062d29d53a93188d1134d8a513792f7..afa3c4decd5562c85978e49a9e1c2a34c128b055
--- 1/refs.h
--- 2/refs.h
+++ b/refs.h
@@@ -1,15 -1,6 +1,6 @@@
  #ifndef REFS_H
  #define REFS_H
  
- struct ref_lock {
-       char *ref_name;
-       char *orig_ref_name;
-       struct lock_file *lk;
-       unsigned char old_sha1[20];
-       int lock_fd;
-       int force_write;
- };
  /*
   * A ref_transaction represents a collection of ref updates
   * that should succeed or fail together.
@@@ -163,15 -154,7 +154,15 @@@ extern void rollback_packed_refs(void)
   */
  int pack_refs(unsigned int flags);
  
 -extern int repack_without_refs(const char **refnames, int n,
 +/*
 + * Rewrite the packed-refs file, omitting any refs listed in
 + * 'refnames'. On error, packed-refs will be unchanged, the return
 + * value is nonzero, and a message about the error is written to the
 + * 'err' strbuf.
 + *
 + * The refs in 'refnames' needn't be sorted. `err` must not be NULL.
 + */
 +extern int repack_without_refs(struct string_list *refnames,
                               struct strbuf *err);
  
  extern int ref_exists(const char *);
@@@ -189,8 -172,7 +180,7 @@@ extern int is_branch(const char *refnam
  extern int peel_ref(const char *refname, unsigned char *sha1);
  
  /*
-  * Flags controlling lock_any_ref_for_update(), ref_transaction_update(),
-  * ref_transaction_create(), etc.
+  * Flags controlling ref_transaction_update(), ref_transaction_create(), etc.
   * REF_NODEREF: act on the ref directly, instead of dereferencing
   *              symbolic references.
   * REF_DELETING: tolerate broken refs
   */
  #define REF_NODEREF   0x01
  #define REF_DELETING  0x02
- /*
-  * This function sets errno to something meaningful on failure.
-  */
- extern struct ref_lock *lock_any_ref_for_update(const char *refname,
-                                               const unsigned char *old_sha1,
-                                               int flags, int *type_p);
- /** Close the file descriptor owned by a lock and return the status */
- extern int close_ref(struct ref_lock *lock);
- /** Close and commit the ref locked by the lock */
- extern int commit_ref(struct ref_lock *lock);
- /** Release any lock taken but not written. **/
- extern void unlock_ref(struct ref_lock *lock);
  
  /*
   * Setup reflog before using. Set errno to something meaningful on failure.
@@@ -291,7 -258,7 +266,7 @@@ struct ref_transaction *ref_transaction
  
  /*
   * Add a reference update to transaction.  new_sha1 is the value that
-  * the reference should have after the update, or zeros if it should
+  * the reference should have after the update, or null_sha1 if it should
   * be deleted.  If have_old is true, then old_sha1 holds the value
   * that the reference should have had before the update, or zeros if
   * it must not have existed beforehand.
@@@ -361,4 -328,50 +336,50 @@@ int update_ref(const char *action, cons
  extern int parse_hide_refs_config(const char *var, const char *value, const char *);
  extern int ref_is_hidden(const char *);
  
+ enum expire_reflog_flags {
+       EXPIRE_REFLOGS_DRY_RUN = 1 << 0,
+       EXPIRE_REFLOGS_UPDATE_REF = 1 << 1,
+       EXPIRE_REFLOGS_VERBOSE = 1 << 2,
+       EXPIRE_REFLOGS_REWRITE = 1 << 3
+ };
+ /*
+  * The following interface is used for reflog expiration. The caller
+  * calls reflog_expire(), supplying it with three callback functions,
+  * of the following types. The callback functions define the
+  * expiration policy that is desired.
+  *
+  * reflog_expiry_prepare_fn -- Called once after the reference is
+  *     locked.
+  *
+  * reflog_expiry_should_prune_fn -- Called once for each entry in the
+  *     existing reflog. It should return true iff that entry should be
+  *     pruned.
+  *
+  * reflog_expiry_cleanup_fn -- Called once before the reference is
+  *     unlocked again.
+  */
+ typedef void reflog_expiry_prepare_fn(const char *refname,
+                                     const unsigned char *sha1,
+                                     void *cb_data);
+ typedef int reflog_expiry_should_prune_fn(unsigned char *osha1,
+                                         unsigned char *nsha1,
+                                         const char *email,
+                                         unsigned long timestamp, int tz,
+                                         const char *message, void *cb_data);
+ typedef void reflog_expiry_cleanup_fn(void *cb_data);
+ /*
+  * Expire reflog entries for the specified reference. sha1 is the old
+  * value of the reference. flags is a combination of the constants in
+  * enum expire_reflog_flags. The three function pointers are described
+  * above. On success, return zero.
+  */
+ extern int reflog_expire(const char *refname, const unsigned char *sha1,
+                        unsigned int flags,
+                        reflog_expiry_prepare_fn prepare_fn,
+                        reflog_expiry_should_prune_fn should_prune_fn,
+                        reflog_expiry_cleanup_fn cleanup_fn,
+                        void *policy_cb_data);
  #endif /* REFS_H */