Merge branch 'dp/autoconf-curl-ssl'
[gitweb.git] / refs / files-backend.c
index 336d432572e410a05cd3468ce7912bd4fce764d3..d16feb19c57b1b74594947f8730b984e94c43def 100644 (file)
@@ -501,7 +501,7 @@ static void sort_ref_dir(struct ref_dir *dir)
        if (dir->sorted == dir->nr)
                return;
 
-       qsort(dir->entries, dir->nr, sizeof(*dir->entries), ref_entry_cmp);
+       QSORT(dir->entries, dir->nr, ref_entry_cmp);
 
        /* Remove any duplicates: */
        for (i = 0, j = 0; j < dir->nr; j++) {
@@ -997,22 +997,6 @@ static struct files_ref_store *files_downcast(
        return (struct files_ref_store *)ref_store;
 }
 
-/*
- * Return a pointer to the reference store for the specified
- * submodule. For the main repository, use submodule==NULL; such a
- * call cannot fail. For a submodule, the submodule must exist and be
- * a nonbare repository, otherwise return NULL. Verify that the
- * reference store is a files_ref_store, and cast it to that type
- * before returning it.
- */
-static struct files_ref_store *get_files_ref_store(const char *submodule,
-                                                  const char *caller)
-{
-       struct ref_store *refs = get_ref_store(submodule);
-
-       return refs ? files_downcast(refs, 1, caller) : NULL;
-}
-
 /* The length of a peeled reference line in packed-refs, including EOL: */
 #define PEELED_LINE_LENGTH 42
 
@@ -1217,13 +1201,19 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
        struct strbuf refname;
        struct strbuf path = STRBUF_INIT;
        size_t path_baselen;
+       int err = 0;
 
        if (*refs->base.submodule)
-               strbuf_git_path_submodule(&path, refs->base.submodule, "%s", dirname);
+               err = strbuf_git_path_submodule(&path, refs->base.submodule, "%s", dirname);
        else
                strbuf_git_path(&path, "%s", dirname);
        path_baselen = path.len;
 
+       if (err) {
+               strbuf_release(&path);
+               return;
+       }
+
        d = opendir(path.buf);
        if (!d) {
                strbuf_release(&path);
@@ -1814,6 +1804,10 @@ static int files_ref_iterator_advance(struct ref_iterator *ref_iterator)
        int ok;
 
        while ((ok = ref_iterator_advance(iter->iter0)) == ITER_OK) {
+               if (iter->flags & DO_FOR_EACH_PER_WORKTREE_ONLY &&
+                   ref_type(iter->iter0->refname) != REF_TYPE_PER_WORKTREE)
+                       continue;
+
                if (!(iter->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
                    !ref_resolves_to_object(iter->iter0->refname,
                                            iter->iter0->oid,
@@ -1954,14 +1948,14 @@ static int verify_lock(struct ref_lock *lock,
                        errno = save_errno;
                        return -1;
                } else {
-                       hashclr(lock->old_oid.hash);
+                       oidclr(&lock->old_oid);
                        return 0;
                }
        }
        if (old_sha1 && hashcmp(lock->old_oid.hash, old_sha1)) {
                strbuf_addf(err, "ref '%s' is at %s but expected %s",
                            lock->ref_name,
-                           sha1_to_hex(lock->old_oid.hash),
+                           oid_to_hex(&lock->old_oid),
                            sha1_to_hex(old_sha1));
                errno = EBUSY;
                return -1;
@@ -2451,10 +2445,11 @@ static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
        return 0;
 }
 
-int delete_refs(struct string_list *refnames, unsigned int flags)
+static int files_delete_refs(struct ref_store *ref_store,
+                            struct string_list *refnames, unsigned int flags)
 {
        struct files_ref_store *refs =
-               get_files_ref_store(NULL, "delete_refs");
+               files_downcast(ref_store, 0, "delete_refs");
        struct strbuf err = STRBUF_INIT;
        int i, result = 0;
 
@@ -2579,9 +2574,12 @@ static int commit_ref_update(struct files_ref_store *refs,
                             const unsigned char *sha1, const char *logmsg,
                             struct strbuf *err);
 
-int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
+static int files_rename_ref(struct ref_store *ref_store,
+                           const char *oldrefname, const char *newrefname,
+                           const char *logmsg)
 {
-       struct files_ref_store *refs = get_files_ref_store(NULL, "rename_ref");
+       struct files_ref_store *refs =
+               files_downcast(ref_store, 0, "rename_ref");
        unsigned char sha1[20], orig_sha1[20];
        int flag = 0, logmoved = 0;
        struct ref_lock *lock;
@@ -3492,6 +3490,38 @@ static const char *original_update_refname(struct ref_update *update)
        return update->refname;
 }
 
+/*
+ * Check whether the REF_HAVE_OLD and old_oid values stored in update
+ * are consistent with oid, which is the reference's current value. If
+ * everything is OK, return 0; otherwise, write an error message to
+ * err and return -1.
+ */
+static int check_old_oid(struct ref_update *update, struct object_id *oid,
+                        struct strbuf *err)
+{
+       if (!(update->flags & REF_HAVE_OLD) ||
+                  !hashcmp(oid->hash, update->old_sha1))
+               return 0;
+
+       if (is_null_sha1(update->old_sha1))
+               strbuf_addf(err, "cannot lock ref '%s': "
+                           "reference already exists",
+                           original_update_refname(update));
+       else if (is_null_oid(oid))
+               strbuf_addf(err, "cannot lock ref '%s': "
+                           "reference is missing but expected %s",
+                           original_update_refname(update),
+                           sha1_to_hex(update->old_sha1));
+       else
+               strbuf_addf(err, "cannot lock ref '%s': "
+                           "is at %s but expected %s",
+                           original_update_refname(update),
+                           oid_to_hex(oid),
+                           sha1_to_hex(update->old_sha1));
+
+       return -1;
+}
+
 /*
  * Prepare for carrying out update:
  * - Lock the reference referred to by update.
@@ -3532,20 +3562,19 @@ static int lock_ref_for_update(struct files_ref_store *refs,
 
        ret = lock_raw_ref(refs, update->refname, mustexist,
                           affected_refnames, NULL,
-                          &update->lock, &referent,
+                          &lock, &referent,
                           &update->type, err);
-
        if (ret) {
                char *reason;
 
                reason = strbuf_detach(err, NULL);
                strbuf_addf(err, "cannot lock ref '%s': %s",
-                           update->refname, reason);
+                           original_update_refname(update), reason);
                free(reason);
                return ret;
        }
 
-       lock = update->lock;
+       update->backend_data = lock;
 
        if (update->type & REF_ISSYMREF) {
                if (update->flags & REF_NODEREF) {
@@ -3554,28 +3583,17 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                         * the transaction, so we have to read it here
                         * to record and possibly check old_sha1:
                         */
-                       if (read_ref_full(update->refname,
-                                         mustexist ? RESOLVE_REF_READING : 0,
+                       if (read_ref_full(referent.buf, 0,
                                          lock->old_oid.hash, NULL)) {
                                if (update->flags & REF_HAVE_OLD) {
                                        strbuf_addf(err, "cannot lock ref '%s': "
-                                                   "can't resolve old value",
-                                                   update->refname);
-                                       return TRANSACTION_GENERIC_ERROR;
-                               } else {
-                                       hashclr(lock->old_oid.hash);
+                                                   "error reading reference",
+                                                   original_update_refname(update));
+                                       return -1;
                                }
-                       }
-                       if ((update->flags & REF_HAVE_OLD) &&
-                           hashcmp(lock->old_oid.hash, update->old_sha1)) {
-                               strbuf_addf(err, "cannot lock ref '%s': "
-                                           "is at %s but expected %s",
-                                           update->refname,
-                                           sha1_to_hex(lock->old_oid.hash),
-                                           sha1_to_hex(update->old_sha1));
+                       } else if (check_old_oid(update, &lock->old_oid, err)) {
                                return TRANSACTION_GENERIC_ERROR;
                        }
-
                } else {
                        /*
                         * Create a new update for the reference this
@@ -3593,6 +3611,9 @@ static int lock_ref_for_update(struct files_ref_store *refs,
        } else {
                struct ref_update *parent_update;
 
+               if (check_old_oid(update, &lock->old_oid, err))
+                       return TRANSACTION_GENERIC_ERROR;
+
                /*
                 * If this update is happening indirectly because of a
                 * symref update, record the old SHA-1 in the parent
@@ -3601,21 +3622,8 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                for (parent_update = update->parent_update;
                     parent_update;
                     parent_update = parent_update->parent_update) {
-                       oidcpy(&parent_update->lock->old_oid, &lock->old_oid);
-               }
-
-               if ((update->flags & REF_HAVE_OLD) &&
-                   hashcmp(lock->old_oid.hash, update->old_sha1)) {
-                       if (is_null_sha1(update->old_sha1))
-                               strbuf_addf(err, "cannot lock ref '%s': reference already exists",
-                                           original_update_refname(update));
-                       else
-                               strbuf_addf(err, "cannot lock ref '%s': is at %s but expected %s",
-                                           original_update_refname(update),
-                                           sha1_to_hex(lock->old_oid.hash),
-                                           sha1_to_hex(update->old_sha1));
-
-                       return TRANSACTION_GENERIC_ERROR;
+                       struct ref_lock *parent_lock = parent_update->backend_data;
+                       oidcpy(&parent_lock->old_oid, &lock->old_oid);
                }
        }
 
@@ -3636,9 +3644,9 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                         * The lock was freed upon failure of
                         * write_ref_to_lockfile():
                         */
-                       update->lock = NULL;
+                       update->backend_data = NULL;
                        strbuf_addf(err,
-                                   "cannot update the ref '%s': %s",
+                                   "cannot update ref '%s': %s",
                                    update->refname, write_err);
                        free(write_err);
                        return TRANSACTION_GENERIC_ERROR;
@@ -3754,7 +3762,7 @@ static int files_transaction_commit(struct ref_store *ref_store,
        /* Perform updates first so live commits remain referenced */
        for (i = 0; i < transaction->nr; i++) {
                struct ref_update *update = transaction->updates[i];
-               struct ref_lock *lock = update->lock;
+               struct ref_lock *lock = update->backend_data;
 
                if (update->flags & REF_NEEDS_COMMIT ||
                    update->flags & REF_LOG_ONLY) {
@@ -3767,7 +3775,7 @@ static int files_transaction_commit(struct ref_store *ref_store,
                                            lock->ref_name, old_msg);
                                free(old_msg);
                                unlock_ref(lock);
-                               update->lock = NULL;
+                               update->backend_data = NULL;
                                ret = TRANSACTION_GENERIC_ERROR;
                                goto cleanup;
                        }
@@ -3777,7 +3785,7 @@ static int files_transaction_commit(struct ref_store *ref_store,
                        if (commit_ref(lock)) {
                                strbuf_addf(err, "couldn't set '%s'", lock->ref_name);
                                unlock_ref(lock);
-                               update->lock = NULL;
+                               update->backend_data = NULL;
                                ret = TRANSACTION_GENERIC_ERROR;
                                goto cleanup;
                        }
@@ -3786,17 +3794,18 @@ static int files_transaction_commit(struct ref_store *ref_store,
        /* Perform deletes now that updates are safely completed */
        for (i = 0; i < transaction->nr; i++) {
                struct ref_update *update = transaction->updates[i];
+               struct ref_lock *lock = update->backend_data;
 
                if (update->flags & REF_DELETING &&
                    !(update->flags & REF_LOG_ONLY)) {
-                       if (delete_ref_loose(update->lock, update->type, err)) {
+                       if (delete_ref_loose(lock, update->type, err)) {
                                ret = TRANSACTION_GENERIC_ERROR;
                                goto cleanup;
                        }
 
                        if (!(update->flags & REF_ISPRUNING))
                                string_list_append(&refs_to_delete,
-                                                  update->lock->ref_name);
+                                                  lock->ref_name);
                }
        }
 
@@ -3812,8 +3821,8 @@ static int files_transaction_commit(struct ref_store *ref_store,
        transaction->state = REF_TRANSACTION_CLOSED;
 
        for (i = 0; i < transaction->nr; i++)
-               if (transaction->updates[i]->lock)
-                       unlock_ref(transaction->updates[i]->lock);
+               if (transaction->updates[i]->backend_data)
+                       unlock_ref(transaction->updates[i]->backend_data);
        string_list_clear(&refs_to_delete, 0);
        free(head_ref);
        string_list_clear(&affected_refnames, 0);
@@ -3829,11 +3838,12 @@ static int ref_present(const char *refname,
        return string_list_has_string(affected_refnames, refname);
 }
 
-int initial_ref_transaction_commit(struct ref_transaction *transaction,
-                                  struct strbuf *err)
+static int files_initial_transaction_commit(struct ref_store *ref_store,
+                                           struct ref_transaction *transaction,
+                                           struct strbuf *err)
 {
        struct files_ref_store *refs =
-               get_files_ref_store(NULL, "initial_ref_transaction_commit");
+               files_downcast(ref_store, 0, "initial_ref_transaction_commit");
        int ret = 0, i;
        struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
 
@@ -4056,15 +4066,36 @@ static int files_reflog_expire(struct ref_store *ref_store,
        return -1;
 }
 
+static int files_init_db(struct ref_store *ref_store, struct strbuf *err)
+{
+       /* Check validity (but we don't need the result): */
+       files_downcast(ref_store, 0, "init_db");
+
+       /*
+        * Create .git/refs/{heads,tags}
+        */
+       safe_create_dir(git_path("refs/heads"), 1);
+       safe_create_dir(git_path("refs/tags"), 1);
+       if (get_shared_repository()) {
+               adjust_shared_perm(git_path("refs/heads"));
+               adjust_shared_perm(git_path("refs/tags"));
+       }
+       return 0;
+}
+
 struct ref_storage_be refs_be_files = {
        NULL,
        "files",
        files_ref_store_create,
+       files_init_db,
        files_transaction_commit,
+       files_initial_transaction_commit,
 
        files_pack_refs,
        files_peel_ref,
        files_create_symref,
+       files_delete_refs,
+       files_rename_ref,
 
        files_ref_iterator_begin,
        files_read_raw_ref,