credential-cache: add tests for XDG functionality
[gitweb.git] / refs / files-backend.c
index bce00223459b16bbdb319b9efd68fdf4665e33d2..b42df147c9c4335b0e47201f751e7e05204e4b01 100644 (file)
@@ -697,7 +697,7 @@ static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
 
        if (peel_entry(entry, 0))
                return -1;
-       hashcpy(peeled->hash, entry->u.value.peeled.hash);
+       oidcpy(peeled, &entry->u.value.peeled);
        return 0;
 }
 
@@ -912,6 +912,14 @@ struct packed_ref_cache {
  */
 struct files_ref_store {
        struct ref_store base;
+
+       /*
+        * The name of the submodule represented by this object, or
+        * NULL if it represents the main repository's reference
+        * store:
+        */
+       const char *submodule;
+
        struct ref_entry *loose;
        struct packed_ref_cache *packed;
 };
@@ -972,11 +980,24 @@ static struct ref_store *files_ref_store_create(const char *submodule)
        struct files_ref_store *refs = xcalloc(1, sizeof(*refs));
        struct ref_store *ref_store = (struct ref_store *)refs;
 
-       base_ref_store_init(ref_store, &refs_be_files, submodule);
+       base_ref_store_init(ref_store, &refs_be_files);
+
+       refs->submodule = xstrdup_or_null(submodule);
 
        return ref_store;
 }
 
+/*
+ * Die if refs is for a submodule (i.e., not for the main repository).
+ * caller is used in any necessary error messages.
+ */
+static void files_assert_main_repository(struct files_ref_store *refs,
+                                        const char *caller)
+{
+       if (refs->submodule)
+               die("BUG: %s called for a submodule", caller);
+}
+
 /*
  * Downcast ref_store to files_ref_store. Die if ref_store is not a
  * files_ref_store. If submodule_allowed is not true, then also die if
@@ -987,14 +1008,18 @@ static struct files_ref_store *files_downcast(
                struct ref_store *ref_store, int submodule_allowed,
                const char *caller)
 {
+       struct files_ref_store *refs;
+
        if (ref_store->be != &refs_be_files)
                die("BUG: ref_store is type \"%s\" not \"files\" in %s",
                    ref_store->be->name, caller);
 
+       refs = (struct files_ref_store *)ref_store;
+
        if (!submodule_allowed)
-               assert_main_repository(ref_store, caller);
+               files_assert_main_repository(refs, caller);
 
-       return (struct files_ref_store *)ref_store;
+       return refs;
 }
 
 /* The length of a peeled reference line in packed-refs, including EOL: */
@@ -1133,8 +1158,8 @@ static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *ref
 {
        char *packed_refs_file;
 
-       if (*refs->base.submodule)
-               packed_refs_file = git_pathdup_submodule(refs->base.submodule,
+       if (refs->submodule)
+               packed_refs_file = git_pathdup_submodule(refs->submodule,
                                                         "packed-refs");
        else
                packed_refs_file = git_pathdup("packed-refs");
@@ -1203,8 +1228,8 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
        size_t path_baselen;
        int err = 0;
 
-       if (*refs->base.submodule)
-               err = strbuf_git_path_submodule(&path, refs->base.submodule, "%s", dirname);
+       if (refs->submodule)
+               err = strbuf_git_path_submodule(&path, refs->submodule, "%s", dirname);
        else
                strbuf_git_path(&path, "%s", dirname);
        path_baselen = path.len;
@@ -1242,20 +1267,10 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
                                         create_dir_entry(refs, refname.buf,
                                                          refname.len, 1));
                } else {
-                       int read_ok;
-
-                       if (*refs->base.submodule) {
-                               hashclr(sha1);
-                               flag = 0;
-                               read_ok = !resolve_gitlink_ref(refs->base.submodule,
-                                                              refname.buf, sha1);
-                       } else {
-                               read_ok = !read_ref_full(refname.buf,
-                                                        RESOLVE_REF_READING,
-                                                        sha1, &flag);
-                       }
-
-                       if (!read_ok) {
+                       if (!resolve_ref_recursively(&refs->base,
+                                                    refname.buf,
+                                                    RESOLVE_REF_READING,
+                                                    sha1, &flag)) {
                                hashclr(sha1);
                                flag |= REF_ISBROKEN;
                        } else if (is_null_sha1(sha1)) {
@@ -1358,8 +1373,8 @@ static int files_read_raw_ref(struct ref_store *ref_store,
        *type = 0;
        strbuf_reset(&sb_path);
 
-       if (*refs->base.submodule)
-               strbuf_git_path_submodule(&sb_path, refs->base.submodule, "%s", refname);
+       if (refs->submodule)
+               strbuf_git_path_submodule(&sb_path, refs->submodule, "%s", refname);
        else
                strbuf_git_path(&sb_path, "%s", refname);
 
@@ -1540,7 +1555,7 @@ static int lock_raw_ref(struct files_ref_store *refs,
        int ret = TRANSACTION_GENERIC_ERROR;
 
        assert(err);
-       assert_main_repository(&refs->base, "lock_raw_ref");
+       files_assert_main_repository(refs, "lock_raw_ref");
 
        *type = 0;
 
@@ -2011,7 +2026,7 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
        int resolve_flags = RESOLVE_REF_NO_RECURSE;
        int resolved;
 
-       assert_main_repository(&refs->base, "lock_ref_sha1_basic");
+       files_assert_main_repository(refs, "lock_ref_sha1_basic");
        assert(err);
 
        lock = xcalloc(1, sizeof(struct ref_lock));
@@ -2134,7 +2149,7 @@ static int lock_packed_refs(struct files_ref_store *refs, int flags)
        static int timeout_value = 1000;
        struct packed_ref_cache *packed_ref_cache;
 
-       assert_main_repository(&refs->base, "lock_packed_refs");
+       files_assert_main_repository(refs, "lock_packed_refs");
 
        if (!timeout_configured) {
                git_config_get_int("core.packedrefstimeout", &timeout_value);
@@ -2172,7 +2187,7 @@ static int commit_packed_refs(struct files_ref_store *refs)
        int save_errno = 0;
        FILE *out;
 
-       assert_main_repository(&refs->base, "commit_packed_refs");
+       files_assert_main_repository(refs, "commit_packed_refs");
 
        if (!packed_ref_cache->lock)
                die("internal error: packed-refs not locked");
@@ -2205,7 +2220,7 @@ static void rollback_packed_refs(struct files_ref_store *refs)
        struct packed_ref_cache *packed_ref_cache =
                get_packed_ref_cache(refs);
 
-       assert_main_repository(&refs->base, "rollback_packed_refs");
+       files_assert_main_repository(refs, "rollback_packed_refs");
 
        if (!packed_ref_cache->lock)
                die("internal error: packed-refs not locked");
@@ -2346,7 +2361,6 @@ static void prune_ref(struct ref_to_prune *r)
        }
        ref_transaction_free(transaction);
        strbuf_release(&err);
-       try_remove_empty_parents(r->name, REMOVE_EMPTY_PARENTS_REF);
 }
 
 static void prune_refs(struct ref_to_prune *r)
@@ -2393,7 +2407,7 @@ static int repack_without_refs(struct files_ref_store *refs,
        struct string_list_item *refname;
        int ret, needs_repacking = 0, removed = 0;
 
-       assert_main_repository(&refs->base, "repack_without_refs");
+       files_assert_main_repository(refs, "repack_without_refs");
        assert(err);
 
        /* Look for a packed ref */
@@ -2467,7 +2481,7 @@ static int files_delete_refs(struct ref_store *ref_store,
        for (i = 0; i < refnames->nr; i++) {
                const char *refname = refnames->items[i].string;
 
-               if (delete_ref(refname, NULL, flags))
+               if (delete_ref(NULL, refname, NULL, flags))
                        result |= error(_("could not remove reference %s"), refname);
        }
 
@@ -2582,7 +2596,7 @@ static int files_rename_ref(struct ref_store *ref_store,
                return error("unable to move logfile logs/%s to "TMP_RENAMED_LOG": %s",
                        oldrefname, strerror(errno));
 
-       if (delete_ref(oldrefname, orig_sha1, REF_NODEREF)) {
+       if (delete_ref(logmsg, oldrefname, orig_sha1, REF_NODEREF)) {
                error("unable to delete old %s", oldrefname);
                goto rollback;
        }
@@ -2596,7 +2610,7 @@ static int files_rename_ref(struct ref_store *ref_store,
         */
        if (!read_ref_full(newrefname, RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
                           sha1, NULL) &&
-           delete_ref(newrefname, NULL, REF_NODEREF)) {
+           delete_ref(NULL, newrefname, NULL, REF_NODEREF)) {
                if (errno == EISDIR) {
                        struct strbuf path = STRBUF_INIT;
                        int result;
@@ -2648,7 +2662,7 @@ static int files_rename_ref(struct ref_store *ref_store,
        }
 
        flag = log_all_ref_updates;
-       log_all_ref_updates = 0;
+       log_all_ref_updates = LOG_REFS_NONE;
        if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
            commit_ref_update(refs, lock, orig_sha1, NULL, &err)) {
                error("unable to write current sha1 into %s: %s", oldrefname, err.buf);
@@ -2823,8 +2837,8 @@ int files_log_ref_write(const char *refname, const unsigned char *old_sha1,
 {
        int logfd, result;
 
-       if (log_all_ref_updates < 0)
-               log_all_ref_updates = !is_bare_repository();
+       if (log_all_ref_updates == LOG_REFS_UNSET)
+               log_all_ref_updates = is_bare_repository() ? LOG_REFS_NONE : LOG_REFS_NORMAL;
 
        result = log_ref_setup(refname, flags & REF_FORCE_CREATE_REFLOG,
                               &logfd, err);
@@ -2903,7 +2917,7 @@ static int commit_ref_update(struct files_ref_store *refs,
                             const unsigned char *sha1, const char *logmsg,
                             struct strbuf *err)
 {
-       assert_main_repository(&refs->base, "commit_ref_update");
+       files_assert_main_repository(refs, "commit_ref_update");
 
        clear_loose_ref_cache(refs);
        if (files_log_ref_write(lock->ref_name, lock->old_oid.hash, sha1,
@@ -3030,7 +3044,7 @@ static int files_create_symref(struct ref_store *ref_store,
        return ret;
 }
 
-int set_worktree_head_symref(const char *gitdir, const char *target)
+int set_worktree_head_symref(const char *gitdir, const char *target, const char *logmsg)
 {
        static struct lock_file head_lock;
        struct ref_lock *lock;
@@ -3058,7 +3072,7 @@ int set_worktree_head_symref(const char *gitdir, const char *target)
        lock->lk = &head_lock;
        lock->ref_name = xstrdup(head_rel);
 
-       ret = create_symref_locked(lock, head_rel, target, NULL);
+       ret = create_symref_locked(lock, head_rel, target, logmsg);
 
        unlock_ref(lock); /* will free lock */
        strbuf_release(&head_path);
@@ -3535,7 +3549,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
        int ret;
        struct ref_lock *lock;
 
-       assert_main_repository(&refs->base, "lock_ref_for_update");
+       files_assert_main_repository(refs, "lock_ref_for_update");
 
        if ((update->flags & REF_HAVE_NEW) && is_null_sha1(update->new_sha1))
                update->flags |= REF_DELETING;
@@ -3794,6 +3808,7 @@ static int files_transaction_commit(struct ref_store *ref_store,
                                        ret = TRANSACTION_GENERIC_ERROR;
                                        goto cleanup;
                                }
+                               update->flags |= REF_DELETED_LOOSE;
                        }
 
                        if (!(update->flags & REF_ISPRUNING))
@@ -3806,16 +3821,38 @@ static int files_transaction_commit(struct ref_store *ref_store,
                ret = TRANSACTION_GENERIC_ERROR;
                goto cleanup;
        }
-       for_each_string_list_item(ref_to_delete, &refs_to_delete)
-               unlink_or_warn(git_path("logs/%s", ref_to_delete->string));
+
+       /* Delete the reflogs of any references that were deleted: */
+       for_each_string_list_item(ref_to_delete, &refs_to_delete) {
+               if (!unlink_or_warn(git_path("logs/%s", ref_to_delete->string)))
+                       try_remove_empty_parents(ref_to_delete->string,
+                                                REMOVE_EMPTY_PARENTS_REFLOG);
+       }
+
        clear_loose_ref_cache(refs);
 
 cleanup:
        transaction->state = REF_TRANSACTION_CLOSED;
 
-       for (i = 0; i < transaction->nr; i++)
-               if (transaction->updates[i]->backend_data)
-                       unlock_ref(transaction->updates[i]->backend_data);
+       for (i = 0; i < transaction->nr; i++) {
+               struct ref_update *update = transaction->updates[i];
+               struct ref_lock *lock = update->backend_data;
+
+               if (lock)
+                       unlock_ref(lock);
+
+               if (update->flags & REF_DELETED_LOOSE) {
+                       /*
+                        * The loose reference was deleted. Delete any
+                        * empty parent directories. (Note that this
+                        * can only work because we have already
+                        * removed the lockfile.)
+                        */
+                       try_remove_empty_parents(update->refname,
+                                                REMOVE_EMPTY_PARENTS_REF);
+               }
+       }
+
        string_list_clear(&refs_to_delete, 0);
        free(head_ref);
        string_list_clear(&affected_refnames, 0);