Merge branch 'rs/reflog-exists'
authorJunio C Hamano <gitster@pobox.com>
Fri, 6 Jun 2014 18:23:04 +0000 (11:23 -0700)
committerJunio C Hamano <gitster@pobox.com>
Fri, 6 Jun 2014 18:23:04 +0000 (11:23 -0700)
* rs/reflog-exists:
checkout.c: use ref_exists instead of file_exist
refs.c: add new functions reflog_exists and delete_reflog

1  2 
builtin/checkout.c
refs.c
refs.h
diff --combined builtin/checkout.c
index ff4492162d9543598c1cdc8951f8604a4712516d,c4db4ca9310b134ffc50537d1bc8da9e4cc5acfc..f1dc56e55f7b2200412142b10517458ccfda2952
@@@ -624,7 -624,7 +624,7 @@@ static void update_refs_for_switch(cons
                /* Nothing to do. */
        } else if (opts->force_detach || !new->path) {  /* No longer on any branch. */
                update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
 -                         REF_NODEREF, DIE_ON_ERR);
 +                         REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
                if (!opts->quiet) {
                        if (old->path && advice_detached_head)
                                detach_advice(new->name);
                        }
                }
                if (old->path && old->name) {
-                       char log_file[PATH_MAX], ref_file[PATH_MAX];
-                       git_snpath(log_file, sizeof(log_file), "logs/%s", old->path);
-                       git_snpath(ref_file, sizeof(ref_file), "%s", old->path);
-                       if (!file_exists(ref_file) && file_exists(log_file))
-                               remove_path(log_file);
+                       if (!ref_exists(old->path) && reflog_exists(old->path))
+                               delete_reflog(old->path);
                }
        }
        remove_branch_state();
diff --combined refs.c
index 728a76164830368637e95390d95ce3f7c7df659e,2c6509d5afdcc8f97d7fd3f03c8b4a3d3e9c3e2a..68982637ed89a2d679d5e2a9a86e76e6fb56a7b4
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -1999,7 -1999,6 +1999,6 @@@ int dwim_log(const char *str, int len, 
  
        *log = NULL;
        for (p = ref_rev_parse_rules; *p; p++) {
-               struct stat st;
                unsigned char hash[20];
                char path[PATH_MAX];
                const char *ref, *it;
                ref = resolve_ref_unsafe(path, hash, 1, NULL);
                if (!ref)
                        continue;
-               if (!stat(git_path("logs/%s", path), &st) &&
-                   S_ISREG(st.st_mode))
+               if (reflog_exists(path))
                        it = path;
-               else if (strcmp(ref, path) &&
-                        !stat(git_path("logs/%s", ref), &st) &&
-                        S_ISREG(st.st_mode))
+               else if (strcmp(ref, path) && reflog_exists(ref))
                        it = ref;
                else
                        continue;
@@@ -3046,6 -3042,19 +3042,19 @@@ int read_ref_at(const char *refname, un
        return 1;
  }
  
+ int reflog_exists(const char *refname)
+ {
+       struct stat st;
+       return !lstat(git_path("logs/%s", refname), &st) &&
+               S_ISREG(st.st_mode);
+ }
+ int delete_reflog(const char *refname)
+ {
+       return remove_path(git_path("logs/%s", refname));
+ }
  static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data)
  {
        unsigned char osha1[20], nsha1[20];
@@@ -3243,9 -3252,9 +3252,9 @@@ static struct ref_lock *update_ref_lock
        if (!lock) {
                const char *str = "Cannot lock the ref '%s'.";
                switch (onerr) {
 -              case MSG_ON_ERR: error(str, refname); break;
 -              case DIE_ON_ERR: die(str, refname); break;
 -              case QUIET_ON_ERR: break;
 +              case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break;
 +              case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break;
 +              case UPDATE_REFS_QUIET_ON_ERR: break;
                }
        }
        return lock;
@@@ -3258,118 -3267,15 +3267,118 @@@ static int update_ref_write(const char 
        if (write_ref_sha1(lock, sha1, action) < 0) {
                const char *str = "Cannot update the ref '%s'.";
                switch (onerr) {
 -              case MSG_ON_ERR: error(str, refname); break;
 -              case DIE_ON_ERR: die(str, refname); break;
 -              case QUIET_ON_ERR: break;
 +              case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break;
 +              case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break;
 +              case UPDATE_REFS_QUIET_ON_ERR: break;
                }
                return 1;
        }
        return 0;
  }
  
 +/**
 + * Information needed for a single ref update.  Set new_sha1 to the
 + * new value or to zero to delete the ref.  To check the old value
 + * while locking the ref, set have_old to 1 and set old_sha1 to the
 + * value or to zero to ensure the ref does not exist before update.
 + */
 +struct ref_update {
 +      unsigned char new_sha1[20];
 +      unsigned char old_sha1[20];
 +      int flags; /* REF_NODEREF? */
 +      int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
 +      struct ref_lock *lock;
 +      int type;
 +      const char refname[FLEX_ARRAY];
 +};
 +
 +/*
 + * Data structure for holding a reference transaction, which can
 + * consist of checks and updates to multiple references, carried out
 + * as atomically as possible.  This structure is opaque to callers.
 + */
 +struct ref_transaction {
 +      struct ref_update **updates;
 +      size_t alloc;
 +      size_t nr;
 +};
 +
 +struct ref_transaction *ref_transaction_begin(void)
 +{
 +      return xcalloc(1, sizeof(struct ref_transaction));
 +}
 +
 +static void ref_transaction_free(struct ref_transaction *transaction)
 +{
 +      int i;
 +
 +      for (i = 0; i < transaction->nr; i++)
 +              free(transaction->updates[i]);
 +
 +      free(transaction->updates);
 +      free(transaction);
 +}
 +
 +void ref_transaction_rollback(struct ref_transaction *transaction)
 +{
 +      ref_transaction_free(transaction);
 +}
 +
 +static struct ref_update *add_update(struct ref_transaction *transaction,
 +                                   const char *refname)
 +{
 +      size_t len = strlen(refname);
 +      struct ref_update *update = xcalloc(1, sizeof(*update) + len + 1);
 +
 +      strcpy((char *)update->refname, refname);
 +      ALLOC_GROW(transaction->updates, transaction->nr + 1, transaction->alloc);
 +      transaction->updates[transaction->nr++] = update;
 +      return update;
 +}
 +
 +void ref_transaction_update(struct ref_transaction *transaction,
 +                          const char *refname,
 +                          unsigned char *new_sha1, unsigned char *old_sha1,
 +                          int flags, int have_old)
 +{
 +      struct ref_update *update = add_update(transaction, refname);
 +
 +      hashcpy(update->new_sha1, new_sha1);
 +      update->flags = flags;
 +      update->have_old = have_old;
 +      if (have_old)
 +              hashcpy(update->old_sha1, old_sha1);
 +}
 +
 +void ref_transaction_create(struct ref_transaction *transaction,
 +                          const char *refname,
 +                          unsigned char *new_sha1,
 +                          int flags)
 +{
 +      struct ref_update *update = add_update(transaction, refname);
 +
 +      assert(!is_null_sha1(new_sha1));
 +      hashcpy(update->new_sha1, new_sha1);
 +      hashclr(update->old_sha1);
 +      update->flags = flags;
 +      update->have_old = 1;
 +}
 +
 +void ref_transaction_delete(struct ref_transaction *transaction,
 +                          const char *refname,
 +                          unsigned char *old_sha1,
 +                          int flags, int have_old)
 +{
 +      struct ref_update *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);
 +      }
 +}
 +
  int update_ref(const char *action, const char *refname,
               const unsigned char *sha1, const unsigned char *oldval,
               int flags, enum action_on_err onerr)
@@@ -3385,7 -3291,7 +3394,7 @@@ static int ref_update_compare(const voi
  {
        const struct ref_update * const *u1 = r1;
        const struct ref_update * const *u2 = r2;
 -      return strcmp((*u1)->ref_name, (*u2)->ref_name);
 +      return strcmp((*u1)->refname, (*u2)->refname);
  }
  
  static int ref_update_reject_duplicates(struct ref_update **updates, int n,
  {
        int i;
        for (i = 1; i < n; i++)
 -              if (!strcmp(updates[i - 1]->ref_name, updates[i]->ref_name)) {
 +              if (!strcmp(updates[i - 1]->refname, updates[i]->refname)) {
                        const char *str =
                                "Multiple updates for ref '%s' not allowed.";
                        switch (onerr) {
 -                      case MSG_ON_ERR:
 -                              error(str, updates[i]->ref_name); break;
 -                      case DIE_ON_ERR:
 -                              die(str, updates[i]->ref_name); break;
 -                      case QUIET_ON_ERR:
 +                      case UPDATE_REFS_MSG_ON_ERR:
 +                              error(str, updates[i]->refname); break;
 +                      case UPDATE_REFS_DIE_ON_ERR:
 +                              die(str, updates[i]->refname); break;
 +                      case UPDATE_REFS_QUIET_ON_ERR:
                                break;
                        }
                        return 1;
        return 0;
  }
  
 -int update_refs(const char *action, const struct ref_update **updates_orig,
 -              int n, enum action_on_err onerr)
 +int ref_transaction_commit(struct ref_transaction *transaction,
 +                         const char *msg, enum action_on_err onerr)
  {
        int ret = 0, delnum = 0, i;
 -      struct ref_update **updates;
 -      int *types;
 -      struct ref_lock **locks;
        const char **delnames;
 +      int n = transaction->nr;
 +      struct ref_update **updates = transaction->updates;
  
 -      if (!updates_orig || !n)
 +      if (!n)
                return 0;
  
        /* Allocate work space */
 -      updates = xmalloc(sizeof(*updates) * n);
 -      types = xmalloc(sizeof(*types) * n);
 -      locks = xcalloc(n, sizeof(*locks));
        delnames = xmalloc(sizeof(*delnames) * n);
  
        /* Copy, sort, and reject duplicate refs */
 -      memcpy(updates, updates_orig, sizeof(*updates) * n);
        qsort(updates, n, sizeof(*updates), ref_update_compare);
        ret = ref_update_reject_duplicates(updates, n, onerr);
        if (ret)
  
        /* Acquire all locks while verifying old values */
        for (i = 0; i < n; i++) {
 -              locks[i] = update_ref_lock(updates[i]->ref_name,
 -                                         (updates[i]->have_old ?
 -                                          updates[i]->old_sha1 : NULL),
 -                                         updates[i]->flags,
 -                                         &types[i], onerr);
 -              if (!locks[i]) {
 +              struct ref_update *update = updates[i];
 +
 +              update->lock = update_ref_lock(update->refname,
 +                                             (update->have_old ?
 +                                              update->old_sha1 : NULL),
 +                                             update->flags,
 +                                             &update->type, onerr);
 +              if (!update->lock) {
                        ret = 1;
                        goto cleanup;
                }
        }
  
        /* Perform updates first so live commits remain referenced */
 -      for (i = 0; i < n; i++)
 -              if (!is_null_sha1(updates[i]->new_sha1)) {
 -                      ret = update_ref_write(action,
 -                                             updates[i]->ref_name,
 -                                             updates[i]->new_sha1,
 -                                             locks[i], onerr);
 -                      locks[i] = NULL; /* freed by update_ref_write */
 +      for (i = 0; i < n; i++) {
 +              struct ref_update *update = updates[i];
 +
 +              if (!is_null_sha1(update->new_sha1)) {
 +                      ret = update_ref_write(msg,
 +                                             update->refname,
 +                                             update->new_sha1,
 +                                             update->lock, onerr);
 +                      update->lock = NULL; /* freed by update_ref_write */
                        if (ret)
                                goto cleanup;
                }
 +      }
  
        /* Perform deletes now that updates are safely completed */
 -      for (i = 0; i < n; i++)
 -              if (locks[i]) {
 -                      delnames[delnum++] = locks[i]->ref_name;
 -                      ret |= delete_ref_loose(locks[i], types[i]);
 +      for (i = 0; i < n; i++) {
 +              struct ref_update *update = updates[i];
 +
 +              if (update->lock) {
 +                      delnames[delnum++] = update->lock->ref_name;
 +                      ret |= delete_ref_loose(update->lock, update->type);
                }
 +      }
 +
        ret |= repack_without_refs(delnames, delnum);
        for (i = 0; i < delnum; i++)
                unlink_or_warn(git_path("logs/%s", delnames[i]));
  
  cleanup:
        for (i = 0; i < n; i++)
 -              if (locks[i])
 -                      unlock_ref(locks[i]);
 -      free(updates);
 -      free(types);
 -      free(locks);
 +              if (updates[i]->lock)
 +                      unlock_ref(updates[i]->lock);
        free(delnames);
 +      ref_transaction_free(transaction);
        return ret;
  }
  
diff --combined refs.h
index 0f08def21020a166902de169dd46c63c85fdfd0e,c9dab1b2f76f3fc038f39e6f400e3c60dfb967fd..09ff483c19ec9484359864640da86f9a40418ecc
--- 1/refs.h
--- 2/refs.h
+++ b/refs.h
@@@ -10,7 -10,19 +10,7 @@@ struct ref_lock 
        int force_write;
  };
  
 -/**
 - * Information needed for a single ref update.  Set new_sha1 to the
 - * new value or to zero to delete the ref.  To check the old value
 - * while locking the ref, set have_old to 1 and set old_sha1 to the
 - * value or to zero to ensure the ref does not exist before update.
 - */
 -struct ref_update {
 -      const char *ref_name;
 -      unsigned char new_sha1[20];
 -      unsigned char old_sha1[20];
 -      int flags; /* REF_NODEREF? */
 -      int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
 -};
 +struct ref_transaction;
  
  /*
   * Bit values set in the flags argument passed to each_ref_fn():
@@@ -154,13 -166,19 +154,19 @@@ extern void unlock_ref(struct ref_lock 
  extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
  
  /** Setup reflog before using. **/
 -int log_ref_setup(const char *ref_name, char *logfile, int bufsize);
 +int log_ref_setup(const char *refname, char *logfile, int bufsize);
  
  /** Reads log for the value of ref during at_time. **/
  extern int read_ref_at(const char *refname, unsigned long at_time, int cnt,
                       unsigned char *sha1, char **msg,
                       unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt);
  
+ /** Check if a particular reflog exists */
+ extern int reflog_exists(const char *refname);
+ /** Delete a reflog */
+ extern int delete_reflog(const char *refname);
  /* iterate over reflog entries */
  typedef int each_reflog_ent_fn(unsigned char *osha1, unsigned char *nsha1, const char *, unsigned long, int, const char *, void *);
  int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data);
@@@ -202,80 -220,18 +208,80 @@@ extern int rename_ref(const char *oldre
   */
  extern int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1);
  
 -/** lock a ref and then write its file */
 -enum action_on_err { MSG_ON_ERR, DIE_ON_ERR, QUIET_ON_ERR };
 +enum action_on_err {
 +      UPDATE_REFS_MSG_ON_ERR,
 +      UPDATE_REFS_DIE_ON_ERR,
 +      UPDATE_REFS_QUIET_ON_ERR
 +};
 +
 +/*
 + * Begin a reference transaction.  The reference transaction must
 + * eventually be commited using ref_transaction_commit() or rolled
 + * back using ref_transaction_rollback().
 + */
 +struct ref_transaction *ref_transaction_begin(void);
 +
 +/*
 + * Roll back a ref_transaction and free all associated data.
 + */
 +void ref_transaction_rollback(struct ref_transaction *transaction);
 +
 +
 +/*
 + * The following functions add a reference check or update to a
 + * ref_transaction.  In all of them, refname is the name of the
 + * reference to be affected.  The functions make internal copies of
 + * refname, so the caller retains ownership of the parameter.  flags
 + * can be REF_NODEREF; it is passed to update_ref_lock().
 + */
 +
 +
 +/*
 + * Add a reference update to transaction.  new_sha1 is the value that
 + * the reference should have after the update, or zeros 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.
 + */
 +void ref_transaction_update(struct ref_transaction *transaction,
 +                          const char *refname,
 +                          unsigned char *new_sha1, unsigned char *old_sha1,
 +                          int flags, int have_old);
 +
 +/*
 + * Add a reference creation to transaction.  new_sha1 is the value
 + * that the reference should have after the update; it must not be the
 + * null SHA-1.  It is verified that the reference does not exist
 + * already.
 + */
 +void ref_transaction_create(struct ref_transaction *transaction,
 +                          const char *refname,
 +                          unsigned char *new_sha1,
 +                          int flags);
 +
 +/*
 + * Add a reference deletion to transaction.  If have_old is true, then
 + * old_sha1 holds the value that the reference should have had before
 + * the update (which must not be the null SHA-1).
 + */
 +void ref_transaction_delete(struct ref_transaction *transaction,
 +                          const char *refname,
 +                          unsigned char *old_sha1,
 +                          int flags, int have_old);
 +
 +/*
 + * Commit all of the changes that have been queued in transaction, as
 + * atomically as possible.  Return a nonzero value if there is a
 + * problem.  The ref_transaction is freed by this function.
 + */
 +int ref_transaction_commit(struct ref_transaction *transaction,
 +                         const char *msg, enum action_on_err onerr);
 +
 +/** Lock a ref and then write its file */
  int update_ref(const char *action, const char *refname,
                const unsigned char *sha1, const unsigned char *oldval,
                int flags, enum action_on_err onerr);
  
 -/**
 - * Lock all refs and then perform all modifications.
 - */
 -int update_refs(const char *action, const struct ref_update **updates,
 -              int n, enum action_on_err onerr);
 -
  extern int parse_hide_refs_config(const char *var, const char *value, const char *);
  extern int ref_is_hidden(const char *);