]> git.ipfire.org Git - thirdparty/git.git/blobdiff - refs/files-backend.c
Merge branch 'en/ort-perf-batch-9'
[thirdparty/git.git] / refs / files-backend.c
index a9a066dcfb60d83a10e27d1c0d1131faf2ae97a4..119972ee16f89f629e96b552e520f0b8190e54a7 100644 (file)
@@ -10,6 +10,7 @@
 #include "../object.h"
 #include "../dir.h"
 #include "../chdir-notify.h"
+#include "worktree.h"
 
 /*
  * This backend uses the following flags in `ref_update::flags` for
  */
 #define REF_NEEDS_COMMIT (1 << 6)
 
-/*
- * Used as a flag in ref_update::flags when we want to log a ref
- * update but not actually perform it.  This is used when a symbolic
- * ref update is split up.
- */
-#define REF_LOG_ONLY (1 << 7)
-
 /*
  * Used as a flag in ref_update::flags when the ref_update was via an
  * update to HEAD.
@@ -66,7 +60,6 @@ struct files_ref_store {
        struct ref_store base;
        unsigned int store_flags;
 
-       char *gitdir;
        char *gitcommondir;
 
        struct ref_cache *loose;
@@ -93,18 +86,17 @@ static struct ref_store *files_ref_store_create(const char *gitdir,
        struct ref_store *ref_store = (struct ref_store *)refs;
        struct strbuf sb = STRBUF_INIT;
 
+       ref_store->gitdir = xstrdup(gitdir);
        base_ref_store_init(ref_store, &refs_be_files);
        refs->store_flags = flags;
 
-       refs->gitdir = xstrdup(gitdir);
        get_common_dir_noenv(&sb, gitdir);
        refs->gitcommondir = strbuf_detach(&sb, NULL);
        strbuf_addf(&sb, "%s/packed-refs", refs->gitcommondir);
        refs->packed_ref_store = packed_ref_store_create(sb.buf, flags);
        strbuf_release(&sb);
 
-       chdir_notify_reparent("files-backend $GIT_DIR",
-                             &refs->gitdir);
+       chdir_notify_reparent("files-backend $GIT_DIR", &refs->base.gitdir);
        chdir_notify_reparent("files-backend $GIT_COMMONDIR",
                              &refs->gitcommondir);
 
@@ -149,6 +141,25 @@ static struct files_ref_store *files_downcast(struct ref_store *ref_store,
        return refs;
 }
 
+static void files_reflog_path_other_worktrees(struct files_ref_store *refs,
+                                             struct strbuf *sb,
+                                             const char *refname)
+{
+       const char *real_ref;
+       const char *worktree_name;
+       int length;
+
+       if (parse_worktree_ref(refname, &worktree_name, &length, &real_ref))
+               BUG("refname %s is not a other-worktree ref", refname);
+
+       if (worktree_name)
+               strbuf_addf(sb, "%s/worktrees/%.*s/logs/%s", refs->gitcommondir,
+                           length, worktree_name, real_ref);
+       else
+               strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir,
+                           real_ref);
+}
+
 static void files_reflog_path(struct files_ref_store *refs,
                              struct strbuf *sb,
                              const char *refname)
@@ -156,7 +167,11 @@ static void files_reflog_path(struct files_ref_store *refs,
        switch (ref_type(refname)) {
        case REF_TYPE_PER_WORKTREE:
        case REF_TYPE_PSEUDOREF:
-               strbuf_addf(sb, "%s/logs/%s", refs->gitdir, refname);
+               strbuf_addf(sb, "%s/logs/%s", refs->base.gitdir, refname);
+               break;
+       case REF_TYPE_OTHER_PSEUDOREF:
+       case REF_TYPE_MAIN_PSEUDOREF:
+               files_reflog_path_other_worktrees(refs, sb, refname);
                break;
        case REF_TYPE_NORMAL:
                strbuf_addf(sb, "%s/logs/%s", refs->gitcommondir, refname);
@@ -174,8 +189,13 @@ static void files_ref_path(struct files_ref_store *refs,
        switch (ref_type(refname)) {
        case REF_TYPE_PER_WORKTREE:
        case REF_TYPE_PSEUDOREF:
-               strbuf_addf(sb, "%s/%s", refs->gitdir, refname);
+               strbuf_addf(sb, "%s/%s", refs->base.gitdir, refname);
                break;
+       case REF_TYPE_MAIN_PSEUDOREF:
+               if (!skip_prefix(refname, "main-worktree/", &refname))
+                       BUG("ref %s is not a main pseudoref", refname);
+               /* fallthrough */
+       case REF_TYPE_OTHER_PSEUDOREF:
        case REF_TYPE_NORMAL:
                strbuf_addf(sb, "%s/%s", refs->gitcommondir, refname);
                break;
@@ -185,6 +205,33 @@ static void files_ref_path(struct files_ref_store *refs,
        }
 }
 
+/*
+ * Manually add refs/bisect, refs/rewritten and refs/worktree, which, being
+ * per-worktree, might not appear in the directory listing for
+ * refs/ in the main repo.
+ */
+static void add_per_worktree_entries_to_dir(struct ref_dir *dir, const char *dirname)
+{
+       const char *prefixes[] = { "refs/bisect/", "refs/worktree/", "refs/rewritten/" };
+       int ip;
+
+       if (strcmp(dirname, "refs/"))
+               return;
+
+       for (ip = 0; ip < ARRAY_SIZE(prefixes); ip++) {
+               const char *prefix = prefixes[ip];
+               int prefix_len = strlen(prefix);
+               struct ref_entry *child_entry;
+               int pos;
+
+               pos = search_ref_dir(dir, prefix, prefix_len);
+               if (pos >= 0)
+                       continue;
+               child_entry = create_dir_entry(dir->cache, prefix, prefix_len, 1);
+               add_entry_to_dir(dir, child_entry);
+       }
+}
+
 /*
  * Read the loose references from the namespace dirname into dir
  * (without recursing).  dirname must end with '/'.  dir must be the
@@ -268,20 +315,7 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
        strbuf_release(&path);
        closedir(d);
 
-       /*
-        * Manually add refs/bisect, which, being per-worktree, might
-        * not appear in the directory listing for refs/ in the main
-        * repo.
-        */
-       if (!strcmp(dirname, "refs/")) {
-               int pos = search_ref_dir(dir, "refs/bisect/", 12);
-
-               if (pos < 0) {
-                       struct ref_entry *child_entry = create_dir_entry(
-                                       dir->cache, "refs/bisect/", 12, 1);
-                       add_entry_to_dir(dir, child_entry);
-               }
-       }
+       add_per_worktree_entries_to_dir(dir, dirname);
 }
 
 static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
@@ -317,7 +351,6 @@ static int files_read_raw_ref(struct ref_store *ref_store,
        struct strbuf sb_path = STRBUF_INIT;
        const char *path;
        const char *buf;
-       const char *p;
        struct stat st;
        int fd;
        int ret = -1;
@@ -363,7 +396,7 @@ stat_ref:
        /* Follow "normalized" - ie "refs/.." symlinks by hand */
        if (S_ISLNK(st.st_mode)) {
                strbuf_reset(&sb_contents);
-               if (strbuf_readlink(&sb_contents, path, 0) < 0) {
+               if (strbuf_readlink(&sb_contents, path, st.st_size) < 0) {
                        if (errno == ENOENT || errno == EINVAL)
                                /* inconsistent with lstat; retry */
                                goto stat_ref;
@@ -422,37 +455,41 @@ stat_ref:
        close(fd);
        strbuf_rtrim(&sb_contents);
        buf = sb_contents.buf;
-       if (starts_with(buf, "ref:")) {
-               buf += 4;
+
+       ret = parse_loose_ref_contents(buf, oid, referent, type);
+
+out:
+       save_errno = errno;
+       strbuf_release(&sb_path);
+       strbuf_release(&sb_contents);
+       errno = save_errno;
+       return ret;
+}
+
+int parse_loose_ref_contents(const char *buf, struct object_id *oid,
+                            struct strbuf *referent, unsigned int *type)
+{
+       const char *p;
+       if (skip_prefix(buf, "ref:", &buf)) {
                while (isspace(*buf))
                        buf++;
 
                strbuf_reset(referent);
                strbuf_addstr(referent, buf);
                *type |= REF_ISSYMREF;
-               ret = 0;
-               goto out;
+               return 0;
        }
 
        /*
-        * Please note that FETCH_HEAD has additional
-        * data after the sha.
+        * FETCH_HEAD has additional data after the sha.
         */
        if (parse_oid_hex(buf, oid, &p) ||
            (*p != '\0' && !isspace(*p))) {
                *type |= REF_ISBROKEN;
                errno = EINVAL;
-               goto out;
+               return -1;
        }
-
-       ret = 0;
-
-out:
-       save_errno = errno;
-       strbuf_release(&sb_path);
-       strbuf_release(&sb_contents);
-       errno = save_errno;
-       return ret;
+       return 0;
 }
 
 static void unlock_ref(struct ref_lock *lock)
@@ -512,7 +549,7 @@ static int lock_raw_ref(struct files_ref_store *refs,
 
        /* First lock the file so it can't change out from under us. */
 
-       *lock_p = lock = xcalloc(1, sizeof(*lock));
+       *lock_p = CALLOC_ARRAY(lock, 1);
 
        lock->ref_name = xstrdup(refname);
        files_ref_path(refs, &ref_file, refname);
@@ -806,7 +843,7 @@ static struct ref_iterator *files_ref_iterator_begin(
 
        overlay_iter = overlay_ref_iterator_begin(loose_iter, packed_iter);
 
-       iter = xcalloc(1, sizeof(*iter));
+       CALLOC_ARRAY(iter, 1);
        ref_iterator = &iter->base;
        base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable,
                               overlay_iter->ordered);
@@ -841,7 +878,7 @@ static int verify_lock(struct ref_store *ref_store, struct ref_lock *lock,
                        return 0;
                }
        }
-       if (old_oid && oidcmp(&lock->old_oid, old_oid)) {
+       if (old_oid && !oideq(&lock->old_oid, old_oid)) {
                strbuf_addf(err, "ref '%s' is at %s but expected %s",
                            lock->ref_name,
                            oid_to_hex(&lock->old_oid),
@@ -893,7 +930,7 @@ static struct ref_lock *lock_ref_oid_basic(struct files_ref_store *refs,
        files_assert_main_repository(refs, "lock_ref_oid_basic");
        assert(err);
 
-       lock = xcalloc(1, sizeof(struct ref_lock));
+       CALLOC_ARRAY(lock, 1);
 
        if (mustexist)
                resolve_flags |= RESOLVE_REF_READING;
@@ -1284,7 +1321,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
 {
        struct files_ref_store *refs =
                files_downcast(ref_store, REF_STORE_WRITE, "rename_ref");
-       struct object_id oid, orig_oid;
+       struct object_id orig_oid;
        int flag = 0, logmoved = 0;
        struct ref_lock *lock;
        struct stat loginfo;
@@ -1352,7 +1389,7 @@ static int files_copy_or_rename_ref(struct ref_store *ref_store,
         */
        if (!copy && !refs_read_ref_full(&refs->base, newrefname,
                                RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
-                               &oid, NULL) &&
+                               NULL, NULL) &&
            refs_delete_ref(&refs->base, NULL, newrefname,
                            NULL, REF_NO_DEREF)) {
                if (errno == EISDIR) {
@@ -1582,26 +1619,19 @@ static int log_ref_write_fd(int fd, const struct object_id *old_oid,
                            const struct object_id *new_oid,
                            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 = xsnprintf(logrec, maxlen, "%s %s %s\n",
-                       oid_to_hex(old_oid),
-                       oid_to_hex(new_oid),
-                       committer);
-       if (msglen)
-               len += copy_reflog_msg(logrec + len - 1, msg) - 1;
-
-       written = len <= maxlen ? write_in_full(fd, logrec, len) : -1;
-       free(logrec);
-       if (written < 0)
-               return -1;
+       struct strbuf sb = STRBUF_INIT;
+       int ret = 0;
 
-       return 0;
+       strbuf_addf(&sb, "%s %s %s", oid_to_hex(old_oid), oid_to_hex(new_oid), committer);
+       if (msg && *msg) {
+               strbuf_addch(&sb, '\t');
+               strbuf_addstr(&sb, msg);
+       }
+       strbuf_addch(&sb, '\n');
+       if (write_in_full(fd, sb.buf, sb.len) < 0)
+               ret = -1;
+       strbuf_release(&sb);
+       return ret;
 }
 
 static int files_log_ref_write(struct files_ref_store *refs,
@@ -1660,7 +1690,7 @@ static int write_ref_to_lockfile(struct ref_lock *lock,
        struct object *o;
        int fd;
 
-       o = parse_object(oid);
+       o = parse_object(the_repository, oid);
        if (!o) {
                strbuf_addf(err,
                            "trying to write ref '%s' with nonexistent object %s",
@@ -1676,7 +1706,7 @@ static int write_ref_to_lockfile(struct ref_lock *lock,
                return -1;
        }
        fd = get_lock_file_fd(&lock->lk);
-       if (write_in_full(fd, oid_to_hex(oid), GIT_SHA1_HEXSZ) < 0 ||
+       if (write_in_full(fd, oid_to_hex(oid), the_hash_algo->hexsz) < 0 ||
            write_in_full(fd, &term, 1) < 0 ||
            close_ref_gently(lock) < 0) {
                strbuf_addf(err,
@@ -1794,12 +1824,12 @@ static int create_symref_locked(struct files_ref_store *refs,
 
        if (!fdopen_lock_file(&lock->lk, "w"))
                return error("unable to fdopen %s: %s",
-                            lock->lk.tempfile->filename.buf, strerror(errno));
+                            get_lock_file_path(&lock->lk), strerror(errno));
 
        update_symref_reflog(refs, lock, refname, target, logmsg);
 
        /* no error check; commit_ref will check ferror */
-       fprintf(lock->lk.tempfile->fp, "ref: %s\n", target);
+       fprintf(get_lock_file_fp(&lock->lk), "ref: %s\n", target);
        if (commit_ref(lock) < 0)
                return error("unable to write symref for %s: %s", refname,
                             strerror(errno));
@@ -2109,13 +2139,24 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {
 static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
                                                  const char *gitdir)
 {
-       struct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));
-       struct ref_iterator *ref_iterator = &iter->base;
+       struct dir_iterator *diter;
+       struct files_reflog_iterator *iter;
+       struct ref_iterator *ref_iterator;
        struct strbuf sb = STRBUF_INIT;
 
-       base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable, 0);
        strbuf_addf(&sb, "%s/logs", gitdir);
-       iter->dir_iterator = dir_iterator_begin(sb.buf);
+
+       diter = dir_iterator_begin(sb.buf, 0);
+       if (!diter) {
+               strbuf_release(&sb);
+               return empty_ref_iterator_begin();
+       }
+
+       CALLOC_ARRAY(iter, 1);
+       ref_iterator = &iter->base;
+
+       base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable, 0);
+       iter->dir_iterator = diter;
        iter->ref_store = ref_store;
        strbuf_release(&sb);
 
@@ -2153,12 +2194,11 @@ static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_st
                files_downcast(ref_store, REF_STORE_READ,
                               "reflog_iterator_begin");
 
-       if (!strcmp(refs->gitdir, refs->gitcommondir)) {
+       if (!strcmp(refs->base.gitdir, refs->gitcommondir)) {
                return reflog_iterator_begin(ref_store, refs->gitcommondir);
        } else {
                return merge_ref_iterator_begin(
-                       0,
-                       reflog_iterator_begin(ref_store, refs->gitdir),
+                       0, reflog_iterator_begin(ref_store, refs->base.gitdir),
                        reflog_iterator_begin(ref_store, refs->gitcommondir),
                        reflog_iterator_select, refs);
        }
@@ -2226,8 +2266,7 @@ static int split_head_update(struct ref_update *update,
  * Note that the new update will itself be subject to splitting when
  * the iteration gets to it.
  */
-static int split_symref_update(struct files_ref_store *refs,
-                              struct ref_update *update,
+static int split_symref_update(struct ref_update *update,
                               const char *referent,
                               struct ref_transaction *transaction,
                               struct string_list *affected_refnames,
@@ -2316,7 +2355,7 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid,
                         struct strbuf *err)
 {
        if (!(update->flags & REF_HAVE_OLD) ||
-                  !oidcmp(oid, &update->old_oid))
+                  oideq(oid, &update->old_oid))
                return 0;
 
        if (is_null_oid(&update->old_oid))
@@ -2421,7 +2460,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
                         * of processing the split-off update, so we
                         * don't have to do it here.
                         */
-                       ret = split_symref_update(refs, update,
+                       ret = split_symref_update(update,
                                                  referent.buf, transaction,
                                                  affected_refnames, err);
                        if (ret)
@@ -2452,7 +2491,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
            !(update->flags & REF_DELETING) &&
            !(update->flags & REF_LOG_ONLY)) {
                if (!(update->type & REF_ISSYMREF) &&
-                   !oidcmp(&lock->old_oid, &update->new_oid)) {
+                   oideq(&lock->old_oid, &update->new_oid)) {
                        /*
                         * The reference already has the desired
                         * value, so we don't need to write it.
@@ -2522,16 +2561,18 @@ static void files_transaction_cleanup(struct files_ref_store *refs,
                }
        }
 
-       if (backend_data->packed_transaction &&
-           ref_transaction_abort(backend_data->packed_transaction, &err)) {
-               error("error aborting transaction: %s", err.buf);
-               strbuf_release(&err);
-       }
+       if (backend_data) {
+               if (backend_data->packed_transaction &&
+                   ref_transaction_abort(backend_data->packed_transaction, &err)) {
+                       error("error aborting transaction: %s", err.buf);
+                       strbuf_release(&err);
+               }
 
-       if (backend_data->packed_refs_locked)
-               packed_refs_unlock(refs->packed_ref_store);
+               if (backend_data->packed_refs_locked)
+                       packed_refs_unlock(refs->packed_ref_store);
 
-       free(backend_data);
+               free(backend_data);
+       }
 
        transaction->state = REF_TRANSACTION_CLOSED;
 }
@@ -2556,7 +2597,7 @@ static int files_transaction_prepare(struct ref_store *ref_store,
        if (!transaction->nr)
                goto cleanup;
 
-       backend_data = xcalloc(1, sizeof(*backend_data));
+       CALLOC_ARRAY(backend_data, 1);
        transaction->backend_data = backend_data;
 
        /*
@@ -2668,18 +2709,32 @@ static int files_transaction_prepare(struct ref_store *ref_store,
                if (is_packed_transaction_needed(refs->packed_ref_store,
                                                 packed_transaction)) {
                        ret = ref_transaction_prepare(packed_transaction, err);
+                       /*
+                        * A failure during the prepare step will abort
+                        * itself, but not free. Do that now, and disconnect
+                        * from the files_transaction so it does not try to
+                        * abort us when we hit the cleanup code below.
+                        */
+                       if (ret) {
+                               ref_transaction_free(packed_transaction);
+                               backend_data->packed_transaction = NULL;
+                       }
                } else {
                        /*
                         * We can skip rewriting the `packed-refs`
                         * file. But we do need to leave it locked, so
                         * that somebody else doesn't pack a reference
                         * that we are trying to delete.
+                        *
+                        * We need to disconnect our transaction from
+                        * backend_data, since the abort (whether successful or
+                        * not) will free it.
                         */
+                       backend_data->packed_transaction = NULL;
                        if (ref_transaction_abort(packed_transaction, err)) {
                                ret = TRANSACTION_GENERIC_ERROR;
                                goto cleanup;
                        }
-                       backend_data->packed_transaction = NULL;
                }
        }
 
@@ -3070,7 +3125,7 @@ static int files_reflog_expire(struct ref_store *ref_store,
                        rollback_lock_file(&reflog_lock);
                } else if (update &&
                           (write_in_full(get_lock_file_fd(&lock->lk),
-                               oid_to_hex(&cb.last_kept_oid), GIT_SHA1_HEXSZ) < 0 ||
+                               oid_to_hex(&cb.last_kept_oid), the_hash_algo->hexsz) < 0 ||
                            write_str_in_full(get_lock_file_fd(&lock->lk), "\n") < 0 ||
                            close_ref_gently(lock) < 0)) {
                        status |= error("couldn't write %s",