summaryrefslogtreecommitdiff
path: root/refs/files-backend.c
diff options
context:
space:
mode:
Diffstat (limited to 'refs/files-backend.c')
-rw-r--r--refs/files-backend.c1405
1 files changed, 546 insertions, 859 deletions
diff --git a/refs/files-backend.c b/refs/files-backend.c
index 83ea080e01..8cc1e07fdb 100644
--- a/refs/files-backend.c
+++ b/refs/files-backend.c
@@ -1,7 +1,9 @@
#include "../cache.h"
+#include "../config.h"
#include "../refs.h"
#include "refs-internal.h"
#include "ref-cache.h"
+#include "packed-backend.h"
#include "../iterator.h"
#include "../dir-iterator.h"
#include "../lockfile.h"
@@ -10,53 +12,11 @@
struct ref_lock {
char *ref_name;
- struct lock_file *lk;
+ struct lock_file lk;
struct object_id old_oid;
};
/*
- * Return true if refname, which has the specified oid and flags, can
- * be resolved to an object in the database. If the referred-to object
- * does not exist, emit a warning and return false.
- */
-static int ref_resolves_to_object(const char *refname,
- const struct object_id *oid,
- unsigned int flags)
-{
- if (flags & REF_ISBROKEN)
- return 0;
- if (!has_sha1_file(oid->hash)) {
- error("%s does not point to a valid object!", refname);
- return 0;
- }
- return 1;
-}
-
-struct packed_ref_cache {
- struct ref_cache *cache;
-
- /*
- * Count of references to the data structure in this instance,
- * including the pointer from files_ref_store::packed if any.
- * The data will not be freed as long as the reference count
- * is nonzero.
- */
- unsigned int referrers;
-
- /*
- * Iff the packed-refs file associated with this instance is
- * currently locked for writing, this points at the associated
- * lock (which is owned by somebody else). The referrer count
- * is also incremented when the file is locked and decremented
- * when it is unlocked.
- */
- struct lock_file *lock;
-
- /* The metadata from when this packed-refs cache was read */
- struct stat_validity validity;
-};
-
-/*
* Future: need to be in "struct repository"
* when doing a full libification.
*/
@@ -66,50 +26,11 @@ struct files_ref_store {
char *gitdir;
char *gitcommondir;
- char *packed_refs_path;
struct ref_cache *loose;
- struct packed_ref_cache *packed;
-};
-
-/* Lock used for the main packed-refs file: */
-static struct lock_file packlock;
-/*
- * Increment the reference count of *packed_refs.
- */
-static void acquire_packed_ref_cache(struct packed_ref_cache *packed_refs)
-{
- packed_refs->referrers++;
-}
-
-/*
- * Decrease the reference count of *packed_refs. If it goes to zero,
- * free *packed_refs and return true; otherwise return false.
- */
-static int release_packed_ref_cache(struct packed_ref_cache *packed_refs)
-{
- if (!--packed_refs->referrers) {
- free_ref_cache(packed_refs->cache);
- stat_validity_clear(&packed_refs->validity);
- free(packed_refs);
- return 1;
- } else {
- return 0;
- }
-}
-
-static void clear_packed_ref_cache(struct files_ref_store *refs)
-{
- if (refs->packed) {
- struct packed_ref_cache *packed_refs = refs->packed;
-
- if (packed_refs->lock)
- die("internal error: packed-ref cache cleared while locked");
- refs->packed = NULL;
- release_packed_ref_cache(packed_refs);
- }
-}
+ struct ref_store *packed_ref_store;
+};
static void clear_loose_ref_cache(struct files_ref_store *refs)
{
@@ -137,7 +58,8 @@ static struct ref_store *files_ref_store_create(const char *gitdir,
get_common_dir_noenv(&sb, gitdir);
refs->gitcommondir = strbuf_detach(&sb, NULL);
strbuf_addf(&sb, "%s/packed-refs", refs->gitcommondir);
- refs->packed_refs_path = strbuf_detach(&sb, NULL);
+ refs->packed_ref_store = packed_ref_store_create(sb.buf, flags);
+ strbuf_release(&sb);
return ref_store;
}
@@ -180,152 +102,10 @@ static struct files_ref_store *files_downcast(struct ref_store *ref_store,
return refs;
}
-/* The length of a peeled reference line in packed-refs, including EOL: */
-#define PEELED_LINE_LENGTH 42
-
-/*
- * The packed-refs header line that we write out. Perhaps other
- * traits will be added later. The trailing space is required.
- */
-static const char PACKED_REFS_HEADER[] =
- "# pack-refs with: peeled fully-peeled \n";
-
-/*
- * Parse one line from a packed-refs file. Write the SHA1 to sha1.
- * Return a pointer to the refname within the line (null-terminated),
- * or NULL if there was a problem.
- */
-static const char *parse_ref_line(struct strbuf *line, unsigned char *sha1)
-{
- const char *ref;
-
- /*
- * 42: the answer to everything.
- *
- * In this case, it happens to be the answer to
- * 40 (length of sha1 hex representation)
- * +1 (space in between hex and name)
- * +1 (newline at the end of the line)
- */
- if (line->len <= 42)
- return NULL;
-
- if (get_sha1_hex(line->buf, sha1) < 0)
- return NULL;
- if (!isspace(line->buf[40]))
- return NULL;
-
- ref = line->buf + 41;
- if (isspace(*ref))
- return NULL;
-
- if (line->buf[line->len - 1] != '\n')
- return NULL;
- line->buf[--line->len] = 0;
-
- return ref;
-}
-
-/*
- * Read f, which is a packed-refs file, into dir.
- *
- * A comment line of the form "# pack-refs with: " may contain zero or
- * more traits. We interpret the traits as follows:
- *
- * No traits:
- *
- * Probably no references are peeled. But if the file contains a
- * peeled value for a reference, we will use it.
- *
- * peeled:
- *
- * References under "refs/tags/", if they *can* be peeled, *are*
- * peeled in this file. References outside of "refs/tags/" are
- * probably not peeled even if they could have been, but if we find
- * a peeled value for such a reference we will use it.
- *
- * fully-peeled:
- *
- * All references in the file that can be peeled are peeled.
- * Inversely (and this is more important), any references in the
- * file for which no peeled value is recorded is not peelable. This
- * trait should typically be written alongside "peeled" for
- * compatibility with older clients, but we do not require it
- * (i.e., "peeled" is a no-op if "fully-peeled" is set).
- */
-static void read_packed_refs(FILE *f, struct ref_dir *dir)
-{
- struct ref_entry *last = NULL;
- struct strbuf line = STRBUF_INIT;
- enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled = PEELED_NONE;
-
- while (strbuf_getwholeline(&line, f, '\n') != EOF) {
- unsigned char sha1[20];
- const char *refname;
- const char *traits;
-
- if (skip_prefix(line.buf, "# pack-refs with:", &traits)) {
- if (strstr(traits, " fully-peeled "))
- peeled = PEELED_FULLY;
- else if (strstr(traits, " peeled "))
- peeled = PEELED_TAGS;
- /* perhaps other traits later as well */
- continue;
- }
-
- refname = parse_ref_line(&line, sha1);
- if (refname) {
- int flag = REF_ISPACKED;
-
- if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
- if (!refname_is_safe(refname))
- die("packed refname is dangerous: %s", refname);
- hashclr(sha1);
- flag |= REF_BAD_NAME | REF_ISBROKEN;
- }
- last = create_ref_entry(refname, sha1, flag, 0);
- if (peeled == PEELED_FULLY ||
- (peeled == PEELED_TAGS && starts_with(refname, "refs/tags/")))
- last->flag |= REF_KNOWS_PEELED;
- add_ref_entry(dir, last);
- continue;
- }
- if (last &&
- line.buf[0] == '^' &&
- line.len == PEELED_LINE_LENGTH &&
- line.buf[PEELED_LINE_LENGTH - 1] == '\n' &&
- !get_sha1_hex(line.buf + 1, sha1)) {
- hashcpy(last->u.value.peeled.hash, sha1);
- /*
- * Regardless of what the file header said,
- * we definitely know the value of *this*
- * reference:
- */
- last->flag |= REF_KNOWS_PEELED;
- }
- }
-
- strbuf_release(&line);
-}
-
-static const char *files_packed_refs_path(struct files_ref_store *refs)
-{
- return refs->packed_refs_path;
-}
-
static void files_reflog_path(struct files_ref_store *refs,
struct strbuf *sb,
const char *refname)
{
- if (!refname) {
- /*
- * FIXME: of course this is wrong in multi worktree
- * setting. To be fixed real soon.
- */
- strbuf_addf(sb, "%s/logs", refs->gitcommondir);
- return;
- }
-
switch (ref_type(refname)) {
case REF_TYPE_PER_WORKTREE:
case REF_TYPE_PSEUDOREF:
@@ -359,62 +139,6 @@ static void files_ref_path(struct files_ref_store *refs,
}
/*
- * Get the packed_ref_cache for the specified files_ref_store,
- * creating it if necessary.
- */
-static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *refs)
-{
- const char *packed_refs_file = files_packed_refs_path(refs);
-
- if (refs->packed &&
- !stat_validity_check(&refs->packed->validity, packed_refs_file))
- clear_packed_ref_cache(refs);
-
- if (!refs->packed) {
- FILE *f;
-
- refs->packed = xcalloc(1, sizeof(*refs->packed));
- acquire_packed_ref_cache(refs->packed);
- refs->packed->cache = create_ref_cache(&refs->base, NULL);
- refs->packed->cache->root->flag &= ~REF_INCOMPLETE;
- f = fopen(packed_refs_file, "r");
- if (f) {
- stat_validity_update(&refs->packed->validity, fileno(f));
- read_packed_refs(f, get_ref_dir(refs->packed->cache->root));
- fclose(f);
- }
- }
- return refs->packed;
-}
-
-static struct ref_dir *get_packed_ref_dir(struct packed_ref_cache *packed_ref_cache)
-{
- return get_ref_dir(packed_ref_cache->cache->root);
-}
-
-static struct ref_dir *get_packed_refs(struct files_ref_store *refs)
-{
- return get_packed_ref_dir(get_packed_ref_cache(refs));
-}
-
-/*
- * Add a reference to the in-memory packed reference cache. This may
- * only be called while the packed-refs file is locked (see
- * lock_packed_refs()). To actually write the packed-refs file, call
- * commit_packed_refs().
- */
-static void add_packed_ref(struct files_ref_store *refs,
- const char *refname, const unsigned char *sha1)
-{
- struct packed_ref_cache *packed_ref_cache = get_packed_ref_cache(refs);
-
- if (!packed_ref_cache->lock)
- die("internal error: packed refs not locked");
- add_ref_entry(get_packed_ref_dir(packed_ref_cache),
- create_ref_entry(refname, sha1, REF_ISPACKED, 1));
-}
-
-/*
* Read the loose references from the namespace dirname into dir
* (without recursing). dirname must end with '/'. dir must be the
* directory entry corresponding to dirname.
@@ -444,7 +168,7 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
strbuf_add(&refname, dirname, dirnamelen);
while ((de = readdir(d)) != NULL) {
- unsigned char sha1[20];
+ struct object_id oid;
struct stat st;
int flag;
@@ -465,10 +189,10 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
if (!refs_resolve_ref_unsafe(&refs->base,
refname.buf,
RESOLVE_REF_READING,
- sha1, &flag)) {
- hashclr(sha1);
+ oid.hash, &flag)) {
+ oidclr(&oid);
flag |= REF_ISBROKEN;
- } else if (is_null_sha1(sha1)) {
+ } else if (is_null_oid(&oid)) {
/*
* It is so astronomically unlikely
* that NULL_SHA1 is the SHA-1 of an
@@ -484,11 +208,11 @@ static void loose_fill_ref_dir(struct ref_store *ref_store,
REFNAME_ALLOW_ONELEVEL)) {
if (!refname_is_safe(refname.buf))
die("loose refname is dangerous: %s", refname.buf);
- hashclr(sha1);
+ oidclr(&oid);
flag |= REF_BAD_NAME | REF_ISBROKEN;
}
add_entry_to_dir(dir,
- create_ref_entry(refname.buf, sha1, flag, 0));
+ create_ref_entry(refname.buf, &oid, flag));
}
strbuf_setlen(&refname, dirnamelen);
strbuf_setlen(&path, path_baselen);
@@ -536,39 +260,6 @@ static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
return refs->loose;
}
-/*
- * Return the ref_entry for the given refname from the packed
- * references. If it does not exist, return NULL.
- */
-static struct ref_entry *get_packed_ref(struct files_ref_store *refs,
- const char *refname)
-{
- return find_ref_entry(get_packed_refs(refs), refname);
-}
-
-/*
- * A loose ref file doesn't exist; check for a packed ref.
- */
-static int resolve_packed_ref(struct files_ref_store *refs,
- const char *refname,
- unsigned char *sha1, unsigned int *flags)
-{
- struct ref_entry *entry;
-
- /*
- * The loose reference file does not exist; check for a packed
- * reference.
- */
- entry = get_packed_ref(refs, refname);
- if (entry) {
- hashcpy(sha1, entry->u.value.oid.hash);
- *flags |= REF_ISPACKED;
- return 0;
- }
- /* refname is not a packed reference. */
- return -1;
-}
-
static int files_read_raw_ref(struct ref_store *ref_store,
const char *refname, unsigned char *sha1,
struct strbuf *referent, unsigned int *type)
@@ -612,7 +303,8 @@ stat_ref:
if (lstat(path, &st) < 0) {
if (errno != ENOENT)
goto out;
- if (resolve_packed_ref(refs, refname, sha1, type)) {
+ if (refs_read_raw_ref(refs->packed_ref_store, refname,
+ sha1, referent, type)) {
errno = ENOENT;
goto out;
}
@@ -651,7 +343,8 @@ stat_ref:
* ref is supposed to be, there could still be a
* packed ref:
*/
- if (resolve_packed_ref(refs, refname, sha1, type)) {
+ if (refs_read_raw_ref(refs->packed_ref_store, refname,
+ sha1, referent, type)) {
errno = EISDIR;
goto out;
}
@@ -716,9 +409,7 @@ out:
static void unlock_ref(struct ref_lock *lock)
{
- /* Do not free lock->lk -- atexit() still looks at them */
- if (lock->lk)
- rollback_lock_file(lock->lk);
+ rollback_lock_file(&lock->lk);
free(lock->ref_name);
free(lock);
}
@@ -832,10 +523,9 @@ retry:
goto error_return;
}
- if (!lock->lk)
- lock->lk = xcalloc(1, sizeof(struct lock_file));
-
- if (hold_lock_file_for_update(lock->lk, ref_file.buf, LOCK_NO_DEREF) < 0) {
+ if (hold_lock_file_for_update_timeout(
+ &lock->lk, ref_file.buf, LOCK_NO_DEREF,
+ get_files_ref_lock_timeout_ms()) < 0) {
if (errno == ENOENT && --attempts_remaining > 0) {
/*
* Maybe somebody just deleted one of the
@@ -930,11 +620,11 @@ retry:
/*
* If the ref did not exist and we are creating it,
- * make sure there is no existing ref that conflicts
- * with refname:
+ * make sure there is no existing packed ref that
+ * conflicts with refname:
*/
if (refs_verify_refname_available(
- &refs->base, refname,
+ refs->packed_ref_store, refname,
extras, skip, err))
goto error_return;
}
@@ -951,53 +641,9 @@ out:
return ret;
}
-static int files_peel_ref(struct ref_store *ref_store,
- const char *refname, unsigned char *sha1)
-{
- struct files_ref_store *refs =
- files_downcast(ref_store, REF_STORE_READ | REF_STORE_ODB,
- "peel_ref");
- int flag;
- unsigned char base[20];
-
- if (current_ref_iter && current_ref_iter->refname == refname) {
- struct object_id peeled;
-
- if (ref_iterator_peel(current_ref_iter, &peeled))
- return -1;
- hashcpy(sha1, peeled.hash);
- return 0;
- }
-
- if (refs_read_ref_full(ref_store, refname,
- RESOLVE_REF_READING, base, &flag))
- return -1;
-
- /*
- * If the reference is packed, read its ref_entry from the
- * cache in the hope that we already know its peeled value.
- * We only try this optimization on packed references because
- * (a) forcing the filling of the loose reference cache could
- * be expensive and (b) loose references anyway usually do not
- * have REF_KNOWS_PEELED.
- */
- if (flag & REF_ISPACKED) {
- struct ref_entry *r = get_packed_ref(refs, refname);
- if (r) {
- if (peel_entry(r, 0))
- return -1;
- hashcpy(sha1, r->u.value.peeled.hash);
- return 0;
- }
- }
-
- return peel_object(base, sha1);
-}
-
struct files_ref_iterator {
struct ref_iterator base;
- struct packed_ref_cache *packed_ref_cache;
struct ref_iterator *iter0;
unsigned int flags;
};
@@ -1050,7 +696,6 @@ static int files_ref_iterator_abort(struct ref_iterator *ref_iterator)
if (iter->iter0)
ok = ref_iterator_abort(iter->iter0);
- release_packed_ref_cache(iter->packed_ref_cache);
base_ref_iterator_free(ref_iterator);
return ok;
}
@@ -1066,22 +711,15 @@ static struct ref_iterator *files_ref_iterator_begin(
const char *prefix, unsigned int flags)
{
struct files_ref_store *refs;
- struct ref_iterator *loose_iter, *packed_iter;
+ struct ref_iterator *loose_iter, *packed_iter, *overlay_iter;
struct files_ref_iterator *iter;
struct ref_iterator *ref_iterator;
+ unsigned int required_flags = REF_STORE_READ;
- if (ref_paranoia < 0)
- ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0);
- if (ref_paranoia)
- flags |= DO_FOR_EACH_INCLUDE_BROKEN;
+ if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN))
+ required_flags |= REF_STORE_ODB;
- refs = files_downcast(ref_store,
- REF_STORE_READ | (ref_paranoia ? 0 : REF_STORE_ODB),
- "ref_iterator_begin");
-
- iter = xcalloc(1, sizeof(*iter));
- ref_iterator = &iter->base;
- base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable);
+ refs = files_downcast(ref_store, required_flags, "ref_iterator_begin");
/*
* We must make sure that all loose refs are read before
@@ -1095,20 +733,36 @@ static struct ref_iterator *files_ref_iterator_begin(
* (If they've already been read, that's OK; we only need to
* guarantee that they're read before the packed refs, not
* *how much* before.) After that, we call
- * get_packed_ref_cache(), which internally checks whether the
- * packed-ref cache is up to date with what is on disk, and
- * re-reads it if not.
+ * packed_ref_iterator_begin(), which internally checks
+ * whether the packed-ref cache is up to date with what is on
+ * disk, and re-reads it if not.
*/
loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs),
prefix, 1);
- iter->packed_ref_cache = get_packed_ref_cache(refs);
- acquire_packed_ref_cache(iter->packed_ref_cache);
- packed_iter = cache_ref_iterator_begin(iter->packed_ref_cache->cache,
- prefix, 0);
+ /*
+ * The packed-refs file might contain broken references, for
+ * example an old version of a reference that points at an
+ * object that has since been garbage-collected. This is OK as
+ * long as there is a corresponding loose reference that
+ * overrides it, and we don't want to emit an error message in
+ * this case. So ask the packed_ref_store for all of its
+ * references, and (if needed) do our own check for broken
+ * ones in files_ref_iterator_advance(), after we have merged
+ * the packed and loose references.
+ */
+ packed_iter = refs_ref_iterator_begin(
+ refs->packed_ref_store, prefix, 0,
+ DO_FOR_EACH_INCLUDE_BROKEN);
+
+ overlay_iter = overlay_ref_iterator_begin(loose_iter, packed_iter);
- iter->iter0 = overlay_ref_iterator_begin(loose_iter, packed_iter);
+ iter = xcalloc(1, sizeof(*iter));
+ ref_iterator = &iter->base;
+ base_ref_iterator_init(ref_iterator, &files_ref_iterator_vtable,
+ overlay_iter->ordered);
+ iter->iter0 = overlay_iter;
iter->flags = flags;
return ref_iterator;
@@ -1164,7 +818,9 @@ static int create_reflock(const char *path, void *cb)
{
struct lock_file *lk = cb;
- return hold_lock_file_for_update(lk, path, LOCK_NO_DEREF) < 0 ? -1 : 0;
+ return hold_lock_file_for_update_timeout(
+ lk, path, LOCK_NO_DEREF,
+ get_files_ref_lock_timeout_ms()) < 0 ? -1 : 0;
}
/*
@@ -1238,17 +894,15 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
* our refname.
*/
if (is_null_oid(&lock->old_oid) &&
- refs_verify_refname_available(&refs->base, refname,
+ refs_verify_refname_available(refs->packed_ref_store, refname,
extras, skip, err)) {
last_errno = ENOTDIR;
goto error_return;
}
- lock->lk = xcalloc(1, sizeof(struct lock_file));
-
lock->ref_name = xstrdup(refname);
- if (raceproof_create_file(ref_file.buf, create_reflock, lock->lk)) {
+ if (raceproof_create_file(ref_file.buf, create_reflock, &lock->lk)) {
last_errno = errno;
unable_to_lock_message(ref_file.buf, errno, err);
goto error_return;
@@ -1270,122 +924,6 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
return lock;
}
-/*
- * Write an entry to the packed-refs file for the specified refname.
- * If peeled is non-NULL, write it as the entry's peeled value.
- */
-static void write_packed_entry(FILE *fh, const char *refname,
- const unsigned char *sha1,
- const unsigned char *peeled)
-{
- fprintf_or_die(fh, "%s %s\n", sha1_to_hex(sha1), refname);
- if (peeled)
- fprintf_or_die(fh, "^%s\n", sha1_to_hex(peeled));
-}
-
-/*
- * Lock the packed-refs file for writing. Flags is passed to
- * hold_lock_file_for_update(). Return 0 on success. On errors, set
- * errno appropriately and return a nonzero value.
- */
-static int lock_packed_refs(struct files_ref_store *refs, int flags)
-{
- static int timeout_configured = 0;
- static int timeout_value = 1000;
- struct packed_ref_cache *packed_ref_cache;
-
- files_assert_main_repository(refs, "lock_packed_refs");
-
- if (!timeout_configured) {
- git_config_get_int("core.packedrefstimeout", &timeout_value);
- timeout_configured = 1;
- }
-
- if (hold_lock_file_for_update_timeout(
- &packlock, files_packed_refs_path(refs),
- flags, timeout_value) < 0)
- return -1;
- /*
- * Get the current packed-refs while holding the lock. If the
- * packed-refs file has been modified since we last read it,
- * this will automatically invalidate the cache and re-read
- * the packed-refs file.
- */
- packed_ref_cache = get_packed_ref_cache(refs);
- packed_ref_cache->lock = &packlock;
- /* Increment the reference count to prevent it from being freed: */
- acquire_packed_ref_cache(packed_ref_cache);
- return 0;
-}
-
-/*
- * Write the current version of the packed refs cache from memory to
- * disk. The packed-refs file must already be locked for writing (see
- * lock_packed_refs()). Return zero on success. On errors, set errno
- * and return a nonzero value
- */
-static int commit_packed_refs(struct files_ref_store *refs)
-{
- struct packed_ref_cache *packed_ref_cache =
- get_packed_ref_cache(refs);
- int ok, error = 0;
- int save_errno = 0;
- FILE *out;
- struct ref_iterator *iter;
-
- files_assert_main_repository(refs, "commit_packed_refs");
-
- if (!packed_ref_cache->lock)
- die("internal error: packed-refs not locked");
-
- out = fdopen_lock_file(packed_ref_cache->lock, "w");
- if (!out)
- die_errno("unable to fdopen packed-refs descriptor");
-
- fprintf_or_die(out, "%s", PACKED_REFS_HEADER);
-
- iter = cache_ref_iterator_begin(packed_ref_cache->cache, NULL, 0);
- while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
- struct object_id peeled;
- int peel_error = ref_iterator_peel(iter, &peeled);
-
- write_packed_entry(out, iter->refname, iter->oid->hash,
- peel_error ? NULL : peeled.hash);
- }
-
- if (ok != ITER_DONE)
- die("error while iterating over references");
-
- if (commit_lock_file(packed_ref_cache->lock)) {
- save_errno = errno;
- error = -1;
- }
- packed_ref_cache->lock = NULL;
- release_packed_ref_cache(packed_ref_cache);
- errno = save_errno;
- return error;
-}
-
-/*
- * Rollback the lockfile for the packed-refs file, and discard the
- * in-memory packed reference cache. (The packed-refs file will be
- * read anew if it is needed again after this function is called.)
- */
-static void rollback_packed_refs(struct files_ref_store *refs)
-{
- struct packed_ref_cache *packed_ref_cache =
- get_packed_ref_cache(refs);
-
- files_assert_main_repository(refs, "rollback_packed_refs");
-
- if (!packed_ref_cache->lock)
- die("internal error: packed-refs not locked");
- rollback_lock_file(packed_ref_cache->lock);
- packed_ref_cache->lock = NULL;
- release_packed_ref_cache(packed_ref_cache);
- clear_packed_ref_cache(refs);
-}
-
struct ref_to_prune {
struct ref_to_prune *next;
unsigned char sha1[20];
@@ -1468,26 +1006,62 @@ static void prune_ref(struct files_ref_store *refs, struct ref_to_prune *r)
strbuf_release(&err);
}
-static void prune_refs(struct files_ref_store *refs, struct ref_to_prune *r)
+/*
+ * Prune the loose versions of the references in the linked list
+ * `*refs_to_prune`, freeing the entries in the list as we go.
+ */
+static void prune_refs(struct files_ref_store *refs, struct ref_to_prune **refs_to_prune)
{
- while (r) {
+ while (*refs_to_prune) {
+ struct ref_to_prune *r = *refs_to_prune;
+ *refs_to_prune = r->next;
prune_ref(refs, r);
- r = r->next;
+ free(r);
}
}
+/*
+ * Return true if the specified reference should be packed.
+ */
+static int should_pack_ref(const char *refname,
+ const struct object_id *oid, unsigned int ref_flags,
+ unsigned int pack_flags)
+{
+ /* Do not pack per-worktree refs: */
+ if (ref_type(refname) != REF_TYPE_NORMAL)
+ return 0;
+
+ /* Do not pack non-tags unless PACK_REFS_ALL is set: */
+ if (!(pack_flags & PACK_REFS_ALL) && !starts_with(refname, "refs/tags/"))
+ return 0;
+
+ /* Do not pack symbolic refs: */
+ if (ref_flags & REF_ISSYMREF)
+ return 0;
+
+ /* Do not pack broken refs: */
+ if (!ref_resolves_to_object(refname, oid, ref_flags))
+ return 0;
+
+ return 1;
+}
+
static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
{
struct files_ref_store *refs =
files_downcast(ref_store, REF_STORE_WRITE | REF_STORE_ODB,
"pack_refs");
struct ref_iterator *iter;
- struct ref_dir *packed_refs;
int ok;
struct ref_to_prune *refs_to_prune = NULL;
+ struct strbuf err = STRBUF_INIT;
+ struct ref_transaction *transaction;
- lock_packed_refs(refs, LOCK_DIE_ON_ERROR);
- packed_refs = get_packed_refs(refs);
+ transaction = ref_store_transaction_begin(refs->packed_ref_store, &err);
+ if (!transaction)
+ return -1;
+
+ packed_refs_lock(refs->packed_ref_store, LOCK_DIE_ON_ERROR, &err);
iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), NULL, 0);
while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
@@ -1496,41 +1070,19 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
* in the packed ref cache. If the reference should be
* pruned, also add it to refs_to_prune.
*/
- struct ref_entry *packed_entry;
- int is_tag_ref = starts_with(iter->refname, "refs/tags/");
-
- /* Do not pack per-worktree refs: */
- if (ref_type(iter->refname) != REF_TYPE_NORMAL)
- continue;
-
- /* ALWAYS pack tags */
- if (!(flags & PACK_REFS_ALL) && !is_tag_ref)
- continue;
-
- /* Do not pack symbolic or broken refs: */
- if (iter->flags & REF_ISSYMREF)
- continue;
-
- if (!ref_resolves_to_object(iter->refname, iter->oid, iter->flags))
+ if (!should_pack_ref(iter->refname, iter->oid, iter->flags,
+ flags))
continue;
/*
- * Create an entry in the packed-refs cache equivalent
- * to the one from the loose ref cache, except that
- * we don't copy the peeled status, because we want it
- * to be re-peeled.
+ * Add a reference creation for this reference to the
+ * packed-refs transaction:
*/
- packed_entry = find_ref_entry(packed_refs, iter->refname);
- if (packed_entry) {
- /* Overwrite existing packed entry with info from loose entry */
- packed_entry->flag = REF_ISPACKED;
- oidcpy(&packed_entry->u.value.oid, iter->oid);
- } else {
- packed_entry = create_ref_entry(iter->refname, iter->oid->hash,
- REF_ISPACKED, 0);
- add_ref_entry(packed_refs, packed_entry);
- }
- oidclr(&packed_entry->u.value.peeled);
+ if (ref_transaction_update(transaction, iter->refname,
+ iter->oid->hash, NULL,
+ REF_NODEREF, NULL, &err))
+ die("failure preparing to create packed reference %s: %s",
+ iter->refname, err.buf);
/* Schedule the loose reference for pruning if requested. */
if ((flags & PACK_REFS_PRUNE)) {
@@ -1544,70 +1096,19 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
if (ok != ITER_DONE)
die("error while iterating over references");
- if (commit_packed_refs(refs))
- die_errno("unable to overwrite old ref-pack file");
-
- prune_refs(refs, refs_to_prune);
- return 0;
-}
-
-/*
- * Rewrite the packed-refs file, omitting any refs listed in
- * 'refnames'. On error, leave packed-refs unchanged, write an error
- * message to 'err', and return a nonzero value.
- *
- * The refs in 'refnames' needn't be sorted. `err` must not be NULL.
- */
-static int repack_without_refs(struct files_ref_store *refs,
- struct string_list *refnames, struct strbuf *err)
-{
- struct ref_dir *packed;
- struct string_list_item *refname;
- int ret, needs_repacking = 0, removed = 0;
-
- files_assert_main_repository(refs, "repack_without_refs");
- assert(err);
-
- /* Look for a packed ref */
- for_each_string_list_item(refname, refnames) {
- if (get_packed_ref(refs, refname->string)) {
- needs_repacking = 1;
- break;
- }
- }
-
- /* Avoid locking if we have nothing to do */
- if (!needs_repacking)
- return 0; /* no refname exists in packed refs */
+ if (ref_transaction_commit(transaction, &err))
+ die("unable to write new packed-refs: %s", err.buf);
- if (lock_packed_refs(refs, 0)) {
- unable_to_lock_message(files_packed_refs_path(refs), errno, err);
- return -1;
- }
- packed = get_packed_refs(refs);
+ ref_transaction_free(transaction);
- /* Remove refnames from the cache */
- for_each_string_list_item(refname, refnames)
- if (remove_entry_from_dir(packed, refname->string) != -1)
- removed = 1;
- if (!removed) {
- /*
- * All packed entries disappeared while we were
- * acquiring the lock.
- */
- rollback_packed_refs(refs);
- return 0;
- }
+ packed_refs_unlock(refs->packed_ref_store);
- /* Write what remains */
- ret = commit_packed_refs(refs);
- if (ret)
- strbuf_addf(err, "unable to overwrite old ref-pack file: %s",
- strerror(errno));
- return ret;
+ prune_refs(refs, &refs_to_prune);
+ strbuf_release(&err);
+ return 0;
}
-static int files_delete_refs(struct ref_store *ref_store,
+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 =
@@ -1618,34 +1119,41 @@ static int files_delete_refs(struct ref_store *ref_store,
if (!refnames->nr)
return 0;
- result = repack_without_refs(refs, refnames, &err);
- if (result) {
- /*
- * 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);
+ if (packed_refs_lock(refs->packed_ref_store, 0, &err))
+ goto error;
- goto out;
+ 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, NULL, refname, NULL, flags))
+ if (refs_delete_ref(&refs->base, msg, refname, NULL, flags))
result |= error(_("could not remove reference %s"), refname);
}
-out:
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;
}
/*
@@ -1709,19 +1217,19 @@ static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)
}
static int write_ref_to_lockfile(struct ref_lock *lock,
- const unsigned char *sha1, struct strbuf *err);
+ const struct object_id *oid, struct strbuf *err);
static int commit_ref_update(struct files_ref_store *refs,
struct ref_lock *lock,
- const unsigned char *sha1, const char *logmsg,
+ const struct object_id *oid, const char *logmsg,
struct strbuf *err);
-static int files_rename_ref(struct ref_store *ref_store,
+static int files_copy_or_rename_ref(struct ref_store *ref_store,
const char *oldrefname, const char *newrefname,
- const char *logmsg)
+ const char *logmsg, int copy)
{
struct files_ref_store *refs =
files_downcast(ref_store, REF_STORE_WRITE, "rename_ref");
- unsigned char sha1[20], orig_sha1[20];
+ struct object_id oid, orig_oid;
int flag = 0, logmoved = 0;
struct ref_lock *lock;
struct stat loginfo;
@@ -1743,14 +1251,18 @@ static int files_rename_ref(struct ref_store *ref_store,
if (!refs_resolve_ref_unsafe(&refs->base, oldrefname,
RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
- orig_sha1, &flag)) {
+ orig_oid.hash, &flag)) {
ret = error("refname %s not found", oldrefname);
goto out;
}
if (flag & REF_ISSYMREF) {
- ret = error("refname %s is a symbolic ref, renaming it is not supported",
- oldrefname);
+ if (copy)
+ ret = error("refname %s is a symbolic ref, copying it is not supported",
+ oldrefname);
+ else
+ ret = error("refname %s is a symbolic ref, renaming it is not supported",
+ oldrefname);
goto out;
}
if (!refs_rename_ref_available(&refs->base, oldrefname, newrefname)) {
@@ -1758,28 +1270,34 @@ static int files_rename_ref(struct ref_store *ref_store,
goto out;
}
- if (log && rename(sb_oldref.buf, tmp_renamed_log.buf)) {
+ if (!copy && log && rename(sb_oldref.buf, tmp_renamed_log.buf)) {
ret = error("unable to move logfile logs/%s to logs/"TMP_RENAMED_LOG": %s",
oldrefname, strerror(errno));
goto out;
}
- if (refs_delete_ref(&refs->base, logmsg, oldrefname,
- orig_sha1, REF_NODEREF)) {
+ if (copy && log && copy_file(tmp_renamed_log.buf, sb_oldref.buf, 0644)) {
+ ret = error("unable to copy logfile logs/%s to logs/"TMP_RENAMED_LOG": %s",
+ oldrefname, strerror(errno));
+ goto out;
+ }
+
+ if (!copy && refs_delete_ref(&refs->base, logmsg, oldrefname,
+ orig_oid.hash, REF_NODEREF)) {
error("unable to delete old %s", oldrefname);
goto rollback;
}
/*
- * Since we are doing a shallow lookup, sha1 is not the
- * correct value to pass to delete_ref as old_sha1. But that
- * doesn't matter, because an old_sha1 check wouldn't add to
+ * Since we are doing a shallow lookup, oid is not the
+ * correct value to pass to delete_ref as old_oid. But that
+ * doesn't matter, because an old_oid check wouldn't add to
* the safety anyway; we want to delete the reference whatever
* its current value.
*/
- if (!refs_read_ref_full(&refs->base, newrefname,
+ if (!copy && !refs_read_ref_full(&refs->base, newrefname,
RESOLVE_REF_READING | RESOLVE_REF_NO_RECURSE,
- sha1, NULL) &&
+ oid.hash, NULL) &&
refs_delete_ref(&refs->base, NULL, newrefname,
NULL, REF_NODEREF)) {
if (errno == EISDIR) {
@@ -1808,14 +1326,17 @@ static int files_rename_ref(struct ref_store *ref_store,
lock = lock_ref_sha1_basic(refs, newrefname, NULL, NULL, NULL,
REF_NODEREF, NULL, &err);
if (!lock) {
- error("unable to rename '%s' to '%s': %s", oldrefname, newrefname, err.buf);
+ if (copy)
+ error("unable to copy '%s' to '%s': %s", oldrefname, newrefname, err.buf);
+ else
+ error("unable to rename '%s' to '%s': %s", oldrefname, newrefname, err.buf);
strbuf_release(&err);
goto rollback;
}
- hashcpy(lock->old_oid.hash, orig_sha1);
+ oidcpy(&lock->old_oid, &orig_oid);
- if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
- commit_ref_update(refs, lock, orig_sha1, logmsg, &err)) {
+ if (write_ref_to_lockfile(lock, &orig_oid, &err) ||
+ commit_ref_update(refs, lock, &orig_oid, logmsg, &err)) {
error("unable to write current sha1 into %s: %s", newrefname, err.buf);
strbuf_release(&err);
goto rollback;
@@ -1835,8 +1356,8 @@ static int files_rename_ref(struct ref_store *ref_store,
flag = log_all_ref_updates;
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)) {
+ if (write_ref_to_lockfile(lock, &orig_oid, &err) ||
+ commit_ref_update(refs, lock, &orig_oid, NULL, &err)) {
error("unable to write current sha1 into %s: %s", oldrefname, err.buf);
strbuf_release(&err);
}
@@ -1859,16 +1380,32 @@ static int files_rename_ref(struct ref_store *ref_store,
return ret;
}
-static int close_ref(struct ref_lock *lock)
+static int files_rename_ref(struct ref_store *ref_store,
+ const char *oldrefname, const char *newrefname,
+ const char *logmsg)
+{
+ return files_copy_or_rename_ref(ref_store, oldrefname,
+ newrefname, logmsg, 0);
+}
+
+static int files_copy_ref(struct ref_store *ref_store,
+ const char *oldrefname, const char *newrefname,
+ const char *logmsg)
+{
+ return files_copy_or_rename_ref(ref_store, oldrefname,
+ newrefname, logmsg, 1);
+}
+
+static int close_ref_gently(struct ref_lock *lock)
{
- if (close_lock_file(lock->lk))
+ if (close_lock_file_gently(&lock->lk))
return -1;
return 0;
}
static int commit_ref(struct ref_lock *lock)
{
- char *path = get_locked_file_path(lock->lk);
+ char *path = get_locked_file_path(&lock->lk);
struct stat st;
if (!lstat(path, &st) && S_ISDIR(st.st_mode)) {
@@ -1892,7 +1429,7 @@ static int commit_ref(struct ref_lock *lock)
free(path);
}
- if (commit_lock_file(lock->lk))
+ if (commit_lock_file(&lock->lk))
return -1;
return 0;
}
@@ -1986,8 +1523,8 @@ static int files_create_reflog(struct ref_store *ref_store,
return 0;
}
-static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
- const unsigned char *new_sha1,
+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;
@@ -1998,23 +1535,23 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
maxlen = strlen(committer) + msglen + 100;
logrec = xmalloc(maxlen);
len = xsnprintf(logrec, maxlen, "%s %s %s\n",
- sha1_to_hex(old_sha1),
- sha1_to_hex(new_sha1),
+ 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 != len)
+ if (written < 0)
return -1;
return 0;
}
static int files_log_ref_write(struct files_ref_store *refs,
- const char *refname, const unsigned char *old_sha1,
- const unsigned char *new_sha1, const char *msg,
+ const char *refname, const struct object_id *old_oid,
+ const struct object_id *new_oid, const char *msg,
int flags, struct strbuf *err)
{
int logfd, result;
@@ -2031,7 +1568,7 @@ static int files_log_ref_write(struct files_ref_store *refs,
if (logfd < 0)
return 0;
- result = log_ref_write_fd(logfd, old_sha1, new_sha1,
+ result = log_ref_write_fd(logfd, old_oid, new_oid,
git_committer_info(0), msg);
if (result) {
struct strbuf sb = STRBUF_INIT;
@@ -2063,33 +1600,33 @@ static int files_log_ref_write(struct files_ref_store *refs,
* return -1.
*/
static int write_ref_to_lockfile(struct ref_lock *lock,
- const unsigned char *sha1, struct strbuf *err)
+ const struct object_id *oid, struct strbuf *err)
{
static char term = '\n';
struct object *o;
int fd;
- o = parse_object(sha1);
+ o = parse_object(oid);
if (!o) {
strbuf_addf(err,
"trying to write ref '%s' with nonexistent object %s",
- lock->ref_name, sha1_to_hex(sha1));
+ lock->ref_name, oid_to_hex(oid));
unlock_ref(lock);
return -1;
}
if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
strbuf_addf(err,
"trying to write non-commit object %s to branch '%s'",
- sha1_to_hex(sha1), lock->ref_name);
+ oid_to_hex(oid), lock->ref_name);
unlock_ref(lock);
return -1;
}
- fd = get_lock_file_fd(lock->lk);
- if (write_in_full(fd, sha1_to_hex(sha1), 40) != 40 ||
- write_in_full(fd, &term, 1) != 1 ||
- close_ref(lock) < 0) {
+ fd = get_lock_file_fd(&lock->lk);
+ if (write_in_full(fd, oid_to_hex(oid), GIT_SHA1_HEXSZ) < 0 ||
+ write_in_full(fd, &term, 1) < 0 ||
+ close_ref_gently(lock) < 0) {
strbuf_addf(err,
- "couldn't write '%s'", get_lock_file_path(lock->lk));
+ "couldn't write '%s'", get_lock_file_path(&lock->lk));
unlock_ref(lock);
return -1;
}
@@ -2103,14 +1640,14 @@ static int write_ref_to_lockfile(struct ref_lock *lock,
*/
static int commit_ref_update(struct files_ref_store *refs,
struct ref_lock *lock,
- const unsigned char *sha1, const char *logmsg,
+ const struct object_id *oid, const char *logmsg,
struct strbuf *err)
{
files_assert_main_repository(refs, "commit_ref_update");
clear_loose_ref_cache(refs);
if (files_log_ref_write(refs, lock->ref_name,
- lock->old_oid.hash, sha1,
+ &lock->old_oid, oid,
logmsg, 0, err)) {
char *old_msg = strbuf_detach(err, NULL);
strbuf_addf(err, "cannot update the ref '%s': %s",
@@ -2133,18 +1670,17 @@ static int commit_ref_update(struct files_ref_store *refs,
* check with HEAD only which should cover 99% of all usage
* scenarios (even 100% of the default ones).
*/
- unsigned char head_sha1[20];
int head_flag;
const char *head_ref;
head_ref = refs_resolve_ref_unsafe(&refs->base, "HEAD",
RESOLVE_REF_READING,
- head_sha1, &head_flag);
+ NULL, &head_flag);
if (head_ref && (head_flag & REF_ISSYMREF) &&
!strcmp(head_ref, lock->ref_name)) {
struct strbuf log_err = STRBUF_INIT;
if (files_log_ref_write(refs, "HEAD",
- lock->old_oid.hash, sha1,
+ &lock->old_oid, oid,
logmsg, 0, &log_err)) {
error("%s", log_err.buf);
strbuf_release(&log_err);
@@ -2166,7 +1702,7 @@ static int create_ref_symlink(struct ref_lock *lock, const char *target)
{
int ret = -1;
#ifndef NO_SYMLINK_HEAD
- char *ref_path = get_locked_file_path(lock->lk);
+ char *ref_path = get_locked_file_path(&lock->lk);
unlink(ref_path);
ret = symlink(target, ref_path);
free(ref_path);
@@ -2182,12 +1718,12 @@ static void update_symref_reflog(struct files_ref_store *refs,
const char *target, const char *logmsg)
{
struct strbuf err = STRBUF_INIT;
- unsigned char new_sha1[20];
+ struct object_id new_oid;
if (logmsg &&
!refs_read_ref_full(&refs->base, target,
- RESOLVE_REF_READING, new_sha1, NULL) &&
- files_log_ref_write(refs, refname, lock->old_oid.hash,
- new_sha1, logmsg, 0, &err)) {
+ RESOLVE_REF_READING, new_oid.hash, NULL) &&
+ files_log_ref_write(refs, refname, &lock->old_oid,
+ &new_oid, logmsg, 0, &err)) {
error("%s", err.buf);
strbuf_release(&err);
}
@@ -2202,14 +1738,14 @@ static int create_symref_locked(struct files_ref_store *refs,
return 0;
}
- if (!fdopen_lock_file(lock->lk, "w"))
+ if (!fdopen_lock_file(&lock->lk, "w"))
return error("unable to fdopen %s: %s",
- lock->lk->tempfile.filename.buf, strerror(errno));
+ lock->lk.tempfile->filename.buf, 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(lock->lk.tempfile->fp, "ref: %s\n", target);
if (commit_ref(lock) < 0)
return error("unable to write symref for %s: %s", refname,
strerror(errno));
@@ -2240,50 +1776,6 @@ static int files_create_symref(struct ref_store *ref_store,
return ret;
}
-int set_worktree_head_symref(const char *gitdir, const char *target, const char *logmsg)
-{
- /*
- * FIXME: this obviously will not work well for future refs
- * backends. This function needs to die.
- */
- struct files_ref_store *refs =
- files_downcast(get_main_ref_store(),
- REF_STORE_WRITE,
- "set_head_symref");
-
- static struct lock_file head_lock;
- struct ref_lock *lock;
- struct strbuf head_path = STRBUF_INIT;
- const char *head_rel;
- int ret;
-
- strbuf_addf(&head_path, "%s/HEAD", absolute_path(gitdir));
- if (hold_lock_file_for_update(&head_lock, head_path.buf,
- LOCK_NO_DEREF) < 0) {
- struct strbuf err = STRBUF_INIT;
- unable_to_lock_message(head_path.buf, errno, &err);
- error("%s", err.buf);
- strbuf_release(&err);
- strbuf_release(&head_path);
- return -1;
- }
-
- /* head_rel will be "HEAD" for the main tree, "worktrees/wt/HEAD" for
- linked trees */
- head_rel = remove_leading_path(head_path.buf,
- absolute_path(get_git_common_dir()));
- /* to make use of create_symref_locked(), initialize ref_lock */
- lock = xcalloc(1, sizeof(struct ref_lock));
- lock->lk = &head_lock;
- lock->ref_name = xstrdup(head_rel);
-
- ret = create_symref_locked(refs, lock, head_rel, target, logmsg);
-
- unlock_ref(lock); /* will free lock */
- strbuf_release(&head_path);
- return ret;
-}
-
static int files_reflog_exists(struct ref_store *ref_store,
const char *refname)
{
@@ -2317,7 +1809,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c
{
struct object_id ooid, noid;
char *email_end, *message;
- unsigned long timestamp;
+ timestamp_t timestamp;
int tz;
const char *p = sb->buf;
@@ -2327,7 +1819,7 @@ static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *c
parse_oid_hex(p, &noid, &p) || *p++ != ' ' ||
!(email_end = strchr(p, '>')) ||
email_end[1] != ' ' ||
- !(timestamp = strtoul(email_end + 2, &message, 10)) ||
+ !(timestamp = parse_timestamp(email_end + 2, &message, 10)) ||
!message || message[0] != ' ' ||
(message[1] != '+' && message[1] != '-') ||
!isdigit(message[2]) || !isdigit(message[3]) ||
@@ -2560,38 +2052,62 @@ static struct ref_iterator_vtable files_reflog_iterator_vtable = {
files_reflog_iterator_abort
};
-static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)
+static struct ref_iterator *reflog_iterator_begin(struct ref_store *ref_store,
+ const char *gitdir)
{
- struct files_ref_store *refs =
- files_downcast(ref_store, REF_STORE_READ,
- "reflog_iterator_begin");
struct files_reflog_iterator *iter = xcalloc(1, sizeof(*iter));
struct ref_iterator *ref_iterator = &iter->base;
struct strbuf sb = STRBUF_INIT;
- base_ref_iterator_init(ref_iterator, &files_reflog_iterator_vtable);
- files_reflog_path(refs, &sb, NULL);
+ 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);
iter->ref_store = ref_store;
strbuf_release(&sb);
+
return ref_iterator;
}
-static int ref_update_reject_duplicates(struct string_list *refnames,
- struct strbuf *err)
+static enum iterator_selection reflog_iterator_select(
+ struct ref_iterator *iter_worktree,
+ struct ref_iterator *iter_common,
+ void *cb_data)
{
- int i, n = refnames->nr;
+ if (iter_worktree) {
+ /*
+ * We're a bit loose here. We probably should ignore
+ * common refs if they are accidentally added as
+ * per-worktree refs.
+ */
+ return ITER_SELECT_0;
+ } else if (iter_common) {
+ if (ref_type(iter_common->refname) == REF_TYPE_NORMAL)
+ return ITER_SELECT_1;
- assert(err);
+ /*
+ * The main ref store may contain main worktree's
+ * per-worktree refs, which should be ignored
+ */
+ return ITER_SKIP_1;
+ } else
+ return ITER_DONE;
+}
- for (i = 1; i < n; i++)
- if (!strcmp(refnames->items[i - 1].string, refnames->items[i].string)) {
- strbuf_addf(err,
- "multiple updates for ref '%s' not allowed.",
- refnames->items[i].string);
- return 1;
- }
- return 0;
+static struct ref_iterator *files_reflog_iterator_begin(struct ref_store *ref_store)
+{
+ struct files_ref_store *refs =
+ files_downcast(ref_store, REF_STORE_READ,
+ "reflog_iterator_begin");
+
+ if (!strcmp(refs->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),
+ reflog_iterator_begin(ref_store, refs->gitcommondir),
+ reflog_iterator_select, refs);
+ }
}
/*
@@ -2617,11 +2133,10 @@ static int split_head_update(struct ref_update *update,
/*
* First make sure that HEAD is not already in the
- * transaction. This insertion is O(N) in the transaction
+ * transaction. This check is O(lg N) in the transaction
* size, but it happens at most once per transaction.
*/
- item = string_list_insert(affected_refnames, "HEAD");
- if (item->util) {
+ if (string_list_has_string(affected_refnames, "HEAD")) {
/* An entry already existed */
strbuf_addf(err,
"multiple updates for 'HEAD' (including one "
@@ -2633,9 +2148,17 @@ static int split_head_update(struct ref_update *update,
new_update = ref_transaction_add_update(
transaction, "HEAD",
update->flags | REF_LOG_ONLY | REF_NODEREF,
- update->new_sha1, update->old_sha1,
+ update->new_oid.hash, update->old_oid.hash,
update->msg);
+ /*
+ * Add "HEAD". This insertion is O(N) in the transaction
+ * size, but it happens at most once per transaction.
+ * Add new_update->refname instead of a literal "HEAD".
+ */
+ if (strcmp(new_update->refname, "HEAD"))
+ BUG("%s unexpectedly not 'HEAD'", new_update->refname);
+ item = string_list_insert(affected_refnames, new_update->refname);
item->util = new_update;
return 0;
@@ -2662,13 +2185,12 @@ static int split_symref_update(struct files_ref_store *refs,
/*
* First make sure that referent is not already in the
- * transaction. This insertion is O(N) in the transaction
+ * transaction. This check is O(lg N) in the transaction
* size, but it happens at most once per symref in a
* transaction.
*/
- item = string_list_insert(affected_refnames, referent);
- if (item->util) {
- /* An entry already existed */
+ if (string_list_has_string(affected_refnames, referent)) {
+ /* An entry already exists */
strbuf_addf(err,
"multiple updates for '%s' (including one "
"via symref '%s') are not allowed",
@@ -2690,7 +2212,7 @@ static int split_symref_update(struct files_ref_store *refs,
new_update = ref_transaction_add_update(
transaction, referent, new_flags,
- update->new_sha1, update->old_sha1,
+ update->new_oid.hash, update->old_oid.hash,
update->msg);
new_update->parent_update = update;
@@ -2703,6 +2225,17 @@ static int split_symref_update(struct files_ref_store *refs,
update->flags |= REF_LOG_ONLY | REF_NODEREF;
update->flags &= ~REF_HAVE_OLD;
+ /*
+ * Add the referent. This insertion is O(N) in the transaction
+ * size, but it happens at most once per symref in a
+ * transaction. Make sure to add new_update->refname, which will
+ * be valid as long as affected_refnames is in use, and NOT
+ * referent, which might soon be freed by our caller.
+ */
+ item = string_list_insert(affected_refnames, new_update->refname);
+ if (item->util)
+ BUG("%s unexpectedly found in affected_refnames",
+ new_update->refname);
item->util = new_update;
return 0;
@@ -2729,10 +2262,10 @@ 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))
+ !oidcmp(oid, &update->old_oid))
return 0;
- if (is_null_sha1(update->old_sha1))
+ if (is_null_oid(&update->old_oid))
strbuf_addf(err, "cannot lock ref '%s': "
"reference already exists",
original_update_refname(update));
@@ -2740,13 +2273,13 @@ static int check_old_oid(struct ref_update *update, struct object_id *oid,
strbuf_addf(err, "cannot lock ref '%s': "
"reference is missing but expected %s",
original_update_refname(update),
- sha1_to_hex(update->old_sha1));
+ oid_to_hex(&update->old_oid));
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));
+ oid_to_hex(&update->old_oid));
return -1;
}
@@ -2773,20 +2306,20 @@ static int lock_ref_for_update(struct files_ref_store *refs,
{
struct strbuf referent = STRBUF_INIT;
int mustexist = (update->flags & REF_HAVE_OLD) &&
- !is_null_sha1(update->old_sha1);
- int ret;
+ !is_null_oid(&update->old_oid);
+ int ret = 0;
struct ref_lock *lock;
files_assert_main_repository(refs, "lock_ref_for_update");
- if ((update->flags & REF_HAVE_NEW) && is_null_sha1(update->new_sha1))
+ if ((update->flags & REF_HAVE_NEW) && is_null_oid(&update->new_oid))
update->flags |= REF_DELETING;
if (head_ref) {
ret = split_head_update(update, transaction, head_ref,
affected_refnames, err);
if (ret)
- return ret;
+ goto out;
}
ret = lock_raw_ref(refs, update->refname, mustexist,
@@ -2800,7 +2333,7 @@ static int lock_ref_for_update(struct files_ref_store *refs,
strbuf_addf(err, "cannot lock ref '%s': %s",
original_update_refname(update), reason);
free(reason);
- return ret;
+ goto out;
}
update->backend_data = lock;
@@ -2819,10 +2352,12 @@ static int lock_ref_for_update(struct files_ref_store *refs,
strbuf_addf(err, "cannot lock ref '%s': "
"error reading reference",
original_update_refname(update));
- return -1;
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto out;
}
} else if (check_old_oid(update, &lock->old_oid, err)) {
- return TRANSACTION_GENERIC_ERROR;
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto out;
}
} else {
/*
@@ -2836,13 +2371,15 @@ static int lock_ref_for_update(struct files_ref_store *refs,
referent.buf, transaction,
affected_refnames, err);
if (ret)
- return ret;
+ goto out;
}
} else {
struct ref_update *parent_update;
- if (check_old_oid(update, &lock->old_oid, err))
- return TRANSACTION_GENERIC_ERROR;
+ if (check_old_oid(update, &lock->old_oid, err)) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto out;
+ }
/*
* If this update is happening indirectly because of a
@@ -2861,12 +2398,12 @@ 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) &&
- !hashcmp(lock->old_oid.hash, update->new_sha1)) {
+ !oidcmp(&lock->old_oid, &update->new_oid)) {
/*
* The reference already has the desired
* value, so we don't need to write it.
*/
- } else if (write_ref_to_lockfile(lock, update->new_sha1,
+ } else if (write_ref_to_lockfile(lock, &update->new_oid,
err)) {
char *write_err = strbuf_detach(err, NULL);
@@ -2879,7 +2416,8 @@ static int lock_ref_for_update(struct files_ref_store *refs,
"cannot update ref '%s': %s",
update->refname, write_err);
free(write_err);
- return TRANSACTION_GENERIC_ERROR;
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto out;
} else {
update->flags |= REF_NEEDS_COMMIT;
}
@@ -2890,40 +2428,82 @@ static int lock_ref_for_update(struct files_ref_store *refs,
* the lockfile is still open. Close it to
* free up the file descriptor:
*/
- if (close_ref(lock)) {
+ if (close_ref_gently(lock)) {
strbuf_addf(err, "couldn't close '%s.lock'",
update->refname);
- return TRANSACTION_GENERIC_ERROR;
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto out;
}
}
- return 0;
+
+out:
+ strbuf_release(&referent);
+ return ret;
}
-static int files_transaction_commit(struct ref_store *ref_store,
- struct ref_transaction *transaction,
- struct strbuf *err)
+struct files_transaction_backend_data {
+ struct ref_transaction *packed_transaction;
+ int packed_refs_locked;
+};
+
+/*
+ * Unlock any references in `transaction` that are still locked, and
+ * mark the transaction closed.
+ */
+static void files_transaction_cleanup(struct files_ref_store *refs,
+ struct ref_transaction *transaction)
+{
+ size_t i;
+ struct files_transaction_backend_data *backend_data =
+ transaction->backend_data;
+ struct strbuf err = STRBUF_INIT;
+
+ 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);
+ update->backend_data = NULL;
+ }
+ }
+
+ 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);
+
+ free(backend_data);
+
+ transaction->state = REF_TRANSACTION_CLOSED;
+}
+
+static int files_transaction_prepare(struct ref_store *ref_store,
+ struct ref_transaction *transaction,
+ struct strbuf *err)
{
struct files_ref_store *refs =
files_downcast(ref_store, REF_STORE_WRITE,
- "ref_transaction_commit");
- int ret = 0, i;
- struct string_list refs_to_delete = STRING_LIST_INIT_NODUP;
- struct string_list_item *ref_to_delete;
+ "ref_transaction_prepare");
+ size_t i;
+ int ret = 0;
struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
char *head_ref = NULL;
int head_type;
- struct object_id head_oid;
- struct strbuf sb = STRBUF_INIT;
+ struct files_transaction_backend_data *backend_data;
+ struct ref_transaction *packed_transaction = NULL;
assert(err);
- if (transaction->state != REF_TRANSACTION_OPEN)
- die("BUG: commit called for transaction that is not open");
+ if (!transaction->nr)
+ goto cleanup;
- if (!transaction->nr) {
- transaction->state = REF_TRANSACTION_CLOSED;
- return 0;
- }
+ backend_data = xcalloc(1, sizeof(*backend_data));
+ transaction->backend_data = backend_data;
/*
* Fail if a refname appears more than once in the
@@ -2970,11 +2550,10 @@ static int files_transaction_commit(struct ref_store *ref_store,
*/
head_ref = refs_resolve_refdup(ref_store, "HEAD",
RESOLVE_REF_NO_RECURSE,
- head_oid.hash, &head_type);
+ NULL, &head_type);
if (head_ref && !(head_type & REF_ISSYMREF)) {
- free(head_ref);
- head_ref = NULL;
+ FREE_AND_NULL(head_ref);
}
/*
@@ -2982,6 +2561,8 @@ static int files_transaction_commit(struct ref_store *ref_store,
* that new values are valid, and write new values to the
* lockfiles, ready to be activated. Only keep one lockfile
* open at a time to avoid running out of file descriptors.
+ * Note that lock_ref_for_update() might append more updates
+ * to the transaction.
*/
for (i = 0; i < transaction->nr; i++) {
struct ref_update *update = transaction->updates[i];
@@ -2990,8 +2571,78 @@ static int files_transaction_commit(struct ref_store *ref_store,
head_ref, &affected_refnames, err);
if (ret)
goto cleanup;
+
+ if (update->flags & REF_DELETING &&
+ !(update->flags & REF_LOG_ONLY) &&
+ !(update->flags & REF_ISPRUNING)) {
+ /*
+ * This reference has to be deleted from
+ * packed-refs if it exists there.
+ */
+ if (!packed_transaction) {
+ packed_transaction = ref_store_transaction_begin(
+ refs->packed_ref_store, err);
+ if (!packed_transaction) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ }
+
+ backend_data->packed_transaction =
+ packed_transaction;
+ }
+
+ ref_transaction_add_update(
+ packed_transaction, update->refname,
+ update->flags & ~REF_HAVE_OLD,
+ update->new_oid.hash, update->old_oid.hash,
+ NULL);
+ }
+ }
+
+ if (packed_transaction) {
+ if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ }
+ backend_data->packed_refs_locked = 1;
+ ret = ref_transaction_prepare(packed_transaction, err);
+ }
+
+cleanup:
+ free(head_ref);
+ string_list_clear(&affected_refnames, 0);
+
+ if (ret)
+ files_transaction_cleanup(refs, transaction);
+ else
+ transaction->state = REF_TRANSACTION_PREPARED;
+
+ return ret;
+}
+
+static int files_transaction_finish(struct ref_store *ref_store,
+ struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ struct files_ref_store *refs =
+ files_downcast(ref_store, 0, "ref_transaction_finish");
+ size_t i;
+ int ret = 0;
+ struct strbuf sb = STRBUF_INIT;
+ struct files_transaction_backend_data *backend_data;
+ struct ref_transaction *packed_transaction;
+
+
+ assert(err);
+
+ if (!transaction->nr) {
+ transaction->state = REF_TRANSACTION_CLOSED;
+ return 0;
}
+ backend_data = transaction->backend_data;
+ packed_transaction = backend_data->packed_transaction;
+
/* Perform updates first so live commits remain referenced */
for (i = 0; i < transaction->nr; i++) {
struct ref_update *update = transaction->updates[i];
@@ -3001,8 +2652,8 @@ static int files_transaction_commit(struct ref_store *ref_store,
update->flags & REF_LOG_ONLY) {
if (files_log_ref_write(refs,
lock->ref_name,
- lock->old_oid.hash,
- update->new_sha1,
+ &lock->old_oid,
+ &update->new_oid,
update->msg, update->flags,
err)) {
char *old_msg = strbuf_detach(err, NULL);
@@ -3027,7 +2678,44 @@ static int files_transaction_commit(struct ref_store *ref_store,
}
}
}
- /* Perform deletes now that updates are safely completed */
+
+ /*
+ * Now that updates are safely completed, we can perform
+ * deletes. First delete the reflogs of any references that
+ * will be deleted, since (in the unexpected event of an
+ * error) leaving a reference without a reflog is less bad
+ * than leaving a reflog without a reference (the latter is a
+ * mildly invalid repository state):
+ */
+ for (i = 0; i < transaction->nr; i++) {
+ struct ref_update *update = transaction->updates[i];
+ if (update->flags & REF_DELETING &&
+ !(update->flags & REF_LOG_ONLY) &&
+ !(update->flags & REF_ISPRUNING)) {
+ strbuf_reset(&sb);
+ files_reflog_path(refs, &sb, update->refname);
+ if (!unlink_or_warn(sb.buf))
+ try_remove_empty_parents(refs, update->refname,
+ REMOVE_EMPTY_PARENTS_REFLOG);
+ }
+ }
+
+ /*
+ * Perform deletes now that updates are safely completed.
+ *
+ * First delete any packed versions of the references, while
+ * retaining the packed-refs lock:
+ */
+ if (packed_transaction) {
+ ret = ref_transaction_commit(packed_transaction, err);
+ ref_transaction_free(packed_transaction);
+ packed_transaction = NULL;
+ backend_data->packed_transaction = NULL;
+ if (ret)
+ goto cleanup;
+ }
+
+ /* Now delete the loose versions of the references: */
for (i = 0; i < transaction->nr; i++) {
struct ref_update *update = transaction->updates[i];
struct ref_lock *lock = update->backend_data;
@@ -3045,39 +2733,16 @@ static int files_transaction_commit(struct ref_store *ref_store,
}
update->flags |= REF_DELETED_LOOSE;
}
-
- if (!(update->flags & REF_ISPRUNING))
- string_list_append(&refs_to_delete,
- lock->ref_name);
}
}
- if (repack_without_refs(refs, &refs_to_delete, err)) {
- ret = TRANSACTION_GENERIC_ERROR;
- goto cleanup;
- }
-
- /* Delete the reflogs of any references that were deleted: */
- for_each_string_list_item(ref_to_delete, &refs_to_delete) {
- strbuf_reset(&sb);
- files_reflog_path(refs, &sb, ref_to_delete->string);
- if (!unlink_or_warn(sb.buf))
- try_remove_empty_parents(refs, ref_to_delete->string,
- REMOVE_EMPTY_PARENTS_REFLOG);
- }
-
clear_loose_ref_cache(refs);
cleanup:
- strbuf_release(&sb);
- transaction->state = REF_TRANSACTION_CLOSED;
+ files_transaction_cleanup(refs, transaction);
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) {
/*
@@ -3091,13 +2756,21 @@ cleanup:
}
}
- string_list_clear(&refs_to_delete, 0);
- free(head_ref);
- string_list_clear(&affected_refnames, 0);
-
+ strbuf_release(&sb);
return ret;
}
+static int files_transaction_abort(struct ref_store *ref_store,
+ struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ struct files_ref_store *refs =
+ files_downcast(ref_store, 0, "ref_transaction_abort");
+
+ files_transaction_cleanup(refs, transaction);
+ return 0;
+}
+
static int ref_present(const char *refname,
const struct object_id *oid, int flags, void *cb_data)
{
@@ -3113,8 +2786,10 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
struct files_ref_store *refs =
files_downcast(ref_store, REF_STORE_WRITE,
"initial_ref_transaction_commit");
- int ret = 0, i;
+ size_t i;
+ int ret = 0;
struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
+ struct ref_transaction *packed_transaction = NULL;
assert(err);
@@ -3147,11 +2822,17 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
&affected_refnames))
die("BUG: initial ref transaction called with existing refs");
+ packed_transaction = ref_store_transaction_begin(refs->packed_ref_store, err);
+ if (!packed_transaction) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ }
+
for (i = 0; i < transaction->nr; i++) {
struct ref_update *update = transaction->updates[i];
if ((update->flags & REF_HAVE_OLD) &&
- !is_null_sha1(update->old_sha1))
+ !is_null_oid(&update->old_oid))
die("BUG: initial ref transaction with old_sha1 set");
if (refs_verify_refname_available(&refs->base, update->refname,
&affected_refnames, NULL,
@@ -3159,31 +2840,31 @@ static int files_initial_transaction_commit(struct ref_store *ref_store,
ret = TRANSACTION_NAME_CONFLICT;
goto cleanup;
}
+
+ /*
+ * Add a reference creation for this reference to the
+ * packed-refs transaction:
+ */
+ ref_transaction_add_update(packed_transaction, update->refname,
+ update->flags & ~REF_HAVE_OLD,
+ update->new_oid.hash, update->old_oid.hash,
+ NULL);
}
- if (lock_packed_refs(refs, 0)) {
- strbuf_addf(err, "unable to lock packed-refs file: %s",
- strerror(errno));
+ if (packed_refs_lock(refs->packed_ref_store, 0, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
- for (i = 0; i < transaction->nr; i++) {
- struct ref_update *update = transaction->updates[i];
-
- if ((update->flags & REF_HAVE_NEW) &&
- !is_null_sha1(update->new_sha1))
- add_packed_ref(refs, update->refname, update->new_sha1);
- }
-
- if (commit_packed_refs(refs)) {
- strbuf_addf(err, "unable to commit packed-refs file: %s",
- strerror(errno));
+ if (initial_ref_transaction_commit(packed_transaction, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
cleanup:
+ if (packed_transaction)
+ ref_transaction_free(packed_transaction);
+ packed_refs_unlock(refs->packed_ref_store);
transaction->state = REF_TRANSACTION_CLOSED;
string_list_clear(&affected_refnames, 0);
return ret;
@@ -3198,7 +2879,7 @@ struct expire_reflog_cb {
};
static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
- const char *email, unsigned long timestamp, int tz,
+ const char *email, timestamp_t timestamp, int tz,
const char *message, void *cb_data)
{
struct expire_reflog_cb *cb = cb_data;
@@ -3207,7 +2888,7 @@ static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
if (cb->flags & EXPIRE_REFLOGS_REWRITE)
ooid = &cb->last_kept_oid;
- if ((*cb->should_prune_fn)(ooid->hash, noid->hash, email, timestamp, tz,
+ if ((*cb->should_prune_fn)(ooid, noid, email, timestamp, tz,
message, policy_cb)) {
if (!cb->newlog)
printf("would prune %s", message);
@@ -3215,7 +2896,7 @@ static int expire_reflog_ent(struct object_id *ooid, struct object_id *noid,
printf("prune %s", message);
} else {
if (cb->newlog) {
- fprintf(cb->newlog, "%s %s %s %lu %+05d\t%s",
+ fprintf(cb->newlog, "%s %s %s %"PRItime" %+05d\t%s",
oid_to_hex(ooid), oid_to_hex(noid),
email, timestamp, tz, message);
oidcpy(&cb->last_kept_oid, noid);
@@ -3244,6 +2925,7 @@ static int files_reflog_expire(struct ref_store *ref_store,
int status = 0;
int type;
struct strbuf err = STRBUF_INIT;
+ struct object_id oid;
memset(&cb, 0, sizeof(cb));
cb.flags = flags;
@@ -3293,7 +2975,9 @@ static int files_reflog_expire(struct ref_store *ref_store,
}
}
- (*prepare_fn)(refname, sha1, cb.policy_cb);
+ hashcpy(oid.hash, sha1);
+
+ (*prepare_fn)(refname, &oid, cb.policy_cb);
refs_for_each_reflog_ent(ref_store, refname, expire_reflog_ent, &cb);
(*cleanup_fn)(cb.policy_cb);
@@ -3309,16 +2993,17 @@ static int files_reflog_expire(struct ref_store *ref_store,
!(type & REF_ISSYMREF) &&
!is_null_oid(&cb.last_kept_oid);
- if (close_lock_file(&reflog_lock)) {
+ if (close_lock_file_gently(&reflog_lock)) {
status |= error("couldn't write %s: %s", log_file,
strerror(errno));
+ 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) != GIT_SHA1_HEXSZ ||
- write_str_in_full(get_lock_file_fd(lock->lk), "\n") != 1 ||
- close_ref(lock) < 0)) {
+ (write_in_full(get_lock_file_fd(&lock->lk),
+ oid_to_hex(&cb.last_kept_oid), GIT_SHA1_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",
- get_lock_file_path(lock->lk));
+ get_lock_file_path(&lock->lk));
rollback_lock_file(&reflog_lock);
} else if (commit_lock_file(&reflog_lock)) {
status |= error("unable to write reflog '%s' (%s)",
@@ -3363,14 +3048,16 @@ struct ref_storage_be refs_be_files = {
"files",
files_ref_store_create,
files_init_db,
- files_transaction_commit,
+ files_transaction_prepare,
+ files_transaction_finish,
+ files_transaction_abort,
files_initial_transaction_commit,
files_pack_refs,
- files_peel_ref,
files_create_symref,
files_delete_refs,
files_rename_ref,
+ files_copy_ref,
files_ref_iterator_begin,
files_read_raw_ref,