diff options
Diffstat (limited to 'refs/files-backend.c')
-rw-r--r-- | refs/files-backend.c | 215 |
1 files changed, 123 insertions, 92 deletions
diff --git a/refs/files-backend.c b/refs/files-backend.c index db5c0c7a72..940beac1f9 100644 --- a/refs/files-backend.c +++ b/refs/files-backend.c @@ -1,5 +1,4 @@ #include "../git-compat-util.h" -#include "../config.h" #include "../copy.h" #include "../environment.h" #include "../gettext.h" @@ -19,7 +18,6 @@ #include "../dir.h" #include "../chdir-notify.h" #include "../setup.h" -#include "../worktree.h" #include "../wrapper.h" #include "../write-or-die.h" #include "../revision.h" @@ -231,6 +229,38 @@ static void add_per_worktree_entries_to_dir(struct ref_dir *dir, const char *dir } } +static void loose_fill_ref_dir_regular_file(struct files_ref_store *refs, + const char *refname, + struct ref_dir *dir) +{ + struct object_id oid; + int flag; + + if (!refs_resolve_ref_unsafe(&refs->base, refname, RESOLVE_REF_READING, + &oid, &flag)) { + oidclr(&oid); + flag |= REF_ISBROKEN; + } else if (is_null_oid(&oid)) { + /* + * It is so astronomically unlikely + * that null_oid is the OID of an + * actual object that we consider its + * appearance in a loose reference + * file to be repo corruption + * (probably due to a software bug). + */ + flag |= REF_ISBROKEN; + } + + if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) { + if (!refname_is_safe(refname)) + die("loose refname is dangerous: %s", refname); + oidclr(&oid); + flag |= REF_BAD_NAME | REF_ISBROKEN; + } + add_entry_to_dir(dir, create_ref_entry(refname, &oid, flag)); +} + /* * Read the loose references from the namespace dirname into dir * (without recursing). dirname must end with '/'. dir must be the @@ -259,8 +289,6 @@ static void loose_fill_ref_dir(struct ref_store *ref_store, strbuf_add(&refname, dirname, dirnamelen); while ((de = readdir(d)) != NULL) { - struct object_id oid; - int flag; unsigned char dtype; if (de->d_name[0] == '.') @@ -276,33 +304,7 @@ static void loose_fill_ref_dir(struct ref_store *ref_store, create_dir_entry(dir->cache, refname.buf, refname.len)); } else if (dtype == DT_REG) { - if (!refs_resolve_ref_unsafe(&refs->base, - refname.buf, - RESOLVE_REF_READING, - &oid, &flag)) { - oidclr(&oid); - flag |= REF_ISBROKEN; - } else if (is_null_oid(&oid)) { - /* - * It is so astronomically unlikely - * that null_oid is the OID of an - * actual object that we consider its - * appearance in a loose reference - * file to be repo corruption - * (probably due to a software bug). - */ - flag |= REF_ISBROKEN; - } - - if (check_refname_format(refname.buf, - REFNAME_ALLOW_ONELEVEL)) { - if (!refname_is_safe(refname.buf)) - die("loose refname is dangerous: %s", refname.buf); - oidclr(&oid); - flag |= REF_BAD_NAME | REF_ISBROKEN; - } - add_entry_to_dir(dir, - create_ref_entry(refname.buf, &oid, flag)); + loose_fill_ref_dir_regular_file(refs, refname.buf, dir); } strbuf_setlen(&refname, dirnamelen); } @@ -313,9 +315,58 @@ static void loose_fill_ref_dir(struct ref_store *ref_store, add_per_worktree_entries_to_dir(dir, dirname); } -static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs) +/* + * Add pseudorefs and HEAD to the ref dir by parsing the directory + * for any files which follow the pseudoref syntax. + */ +static void add_pseudoref_like_entries(struct ref_store *ref_store, + struct ref_dir *dir, + const char *dirname) +{ + struct files_ref_store *refs = + files_downcast(ref_store, REF_STORE_READ, "fill_ref_dir"); + struct strbuf path = STRBUF_INIT, refname = STRBUF_INIT; + struct dirent *de; + size_t dirnamelen; + DIR *d; + + files_ref_path(refs, &path, dirname); + + d = opendir(path.buf); + if (!d) { + strbuf_release(&path); + return; + } + + strbuf_addstr(&refname, dirname); + dirnamelen = refname.len; + + while ((de = readdir(d)) != NULL) { + unsigned char dtype; + + if (de->d_name[0] == '.') + continue; + if (ends_with(de->d_name, ".lock")) + continue; + strbuf_addstr(&refname, de->d_name); + + dtype = get_dtype(de, &path, 1); + if (dtype == DT_REG && is_pseudoref_syntax(de->d_name)) + loose_fill_ref_dir_regular_file(refs, refname.buf, dir); + + strbuf_setlen(&refname, dirnamelen); + } + strbuf_release(&refname); + strbuf_release(&path); + closedir(d); +} + +static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs, + unsigned int flags) { if (!refs->loose) { + struct ref_dir *dir; + /* * Mark the top-level directory complete because we * are about to read the only subdirectory that can @@ -326,12 +377,17 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs) /* We're going to fill the top level ourselves: */ refs->loose->root->flag &= ~REF_INCOMPLETE; + dir = get_ref_dir(refs->loose->root); + + if (flags & DO_FOR_EACH_INCLUDE_ALL_REFS) + add_pseudoref_like_entries(dir->cache->ref_store, dir, + refs->loose->root->name); + /* * Add an incomplete entry for "refs/" (to be filled * lazily): */ - add_entry_to_dir(get_ref_dir(refs->loose->root), - create_dir_entry(refs->loose, "refs/", 5)); + add_entry_to_dir(dir, create_dir_entry(refs->loose, "refs/", 5)); } return refs->loose; } @@ -859,7 +915,7 @@ static struct ref_iterator *files_ref_iterator_begin( * disk, and re-reads it if not. */ - loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), + loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, flags), prefix, ref_store->repo, 1); /* @@ -1220,7 +1276,7 @@ static int files_pack_refs(struct ref_store *ref_store, packed_refs_lock(refs->packed_ref_store, LOCK_DIE_ON_ERROR, &err); - iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), NULL, + iter = cache_ref_iterator_begin(get_loose_ref_cache(refs, 0), NULL, the_repository, 0); while ((ok = ref_iterator_advance(iter)) == ITER_OK) { /* @@ -1265,54 +1321,6 @@ static int files_pack_refs(struct ref_store *ref_store, return 0; } -static int files_delete_refs(struct ref_store *ref_store, const char *msg, - struct string_list *refnames, unsigned int flags) -{ - struct files_ref_store *refs = - files_downcast(ref_store, REF_STORE_WRITE, "delete_refs"); - struct strbuf err = STRBUF_INIT; - int i, result = 0; - - if (!refnames->nr) - return 0; - - if (packed_refs_lock(refs->packed_ref_store, 0, &err)) - goto error; - - if (refs_delete_refs(refs->packed_ref_store, msg, refnames, flags)) { - packed_refs_unlock(refs->packed_ref_store); - goto error; - } - - packed_refs_unlock(refs->packed_ref_store); - - for (i = 0; i < refnames->nr; i++) { - const char *refname = refnames->items[i].string; - - if (refs_delete_ref(&refs->base, msg, refname, NULL, flags)) - result |= error(_("could not remove reference %s"), refname); - } - - strbuf_release(&err); - return result; - -error: - /* - * If we failed to rewrite the packed-refs file, then it is - * unsafe to try to remove loose refs, because doing so might - * expose an obsolete packed value for a reference that might - * even point at an object that has been garbage collected. - */ - if (refnames->nr == 1) - error(_("could not delete reference %s: %s"), - refnames->items[0].string, err.buf); - else - error(_("could not delete references: %s"), err.buf); - - strbuf_release(&err); - return -1; -} - /* * People using contrib's git-new-workdir have .git/logs/refs -> * /some/other/path/.git/logs/refs, and that may live on another device. @@ -3268,28 +3276,52 @@ 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 UNUSED) +static int files_init_db(struct ref_store *ref_store, + int flags, + struct strbuf *err UNUSED) { struct files_ref_store *refs = files_downcast(ref_store, REF_STORE_WRITE, "init_db"); struct strbuf sb = STRBUF_INIT; /* - * Create .git/refs/{heads,tags} + * We need to create a "refs" dir in any case so that older versions of + * Git can tell that this is a repository. This serves two main purposes: + * + * - Clients will know to stop walking the parent-directory chain when + * detecting the Git repository. Otherwise they may end up detecting + * a Git repository in a parent directory instead. + * + * - Instead of failing to detect a repository with unknown reference + * format altogether, old clients will print an error saying that + * they do not understand the reference format extension. */ - files_ref_path(refs, &sb, "refs/heads"); + strbuf_addf(&sb, "%s/refs", ref_store->gitdir); safe_create_dir(sb.buf, 1); + adjust_shared_perm(sb.buf); - strbuf_reset(&sb); - files_ref_path(refs, &sb, "refs/tags"); - safe_create_dir(sb.buf, 1); + /* + * There is no need to create directories for common refs when creating + * a worktree ref store. + */ + if (!(flags & REFS_INIT_DB_IS_WORKTREE)) { + /* + * Create .git/refs/{heads,tags} + */ + strbuf_reset(&sb); + files_ref_path(refs, &sb, "refs/heads"); + safe_create_dir(sb.buf, 1); + + strbuf_reset(&sb); + files_ref_path(refs, &sb, "refs/tags"); + safe_create_dir(sb.buf, 1); + } strbuf_release(&sb); return 0; } struct ref_storage_be refs_be_files = { - .next = NULL, .name = "files", .init = files_ref_store_create, .init_db = files_init_db, @@ -3300,7 +3332,6 @@ struct ref_storage_be refs_be_files = { .pack_refs = files_pack_refs, .create_symref = files_create_symref, - .delete_refs = files_delete_refs, .rename_ref = files_rename_ref, .copy_ref = files_copy_ref, |