char *ref_name;
char *orig_ref_name;
struct lock_file *lk;
- unsigned char old_sha1[20];
- int lock_fd;
+ struct object_id old_oid;
};
/*
*/
#define REF_HAVE_OLD 0x10
+/*
+ * Used as a flag in ref_update::flags when the lockfile needs to be
+ * committed.
+ */
+#define REF_NEEDS_COMMIT 0x20
+
/*
* Try to read one refname component from the front of refname.
* Return the length of the component found, or -1 if the component is
* null. If REF_ISSYMREF, then this is the name of the object
* referred to by the last reference in the symlink chain.
*/
- unsigned char sha1[20];
+ struct object_id oid;
/*
* If REF_KNOWS_PEELED, then this field holds the peeled value
* be peelable. See the documentation for peel_ref() for an
* exact definition of "peelable".
*/
- unsigned char peeled[20];
+ struct object_id peeled;
};
struct ref_cache;
if (check_name &&
check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
die("Reference has invalid format: '%s'", refname);
- if (!check_name && !refname_is_safe(refname))
- die("Reference has invalid name: '%s'", refname);
len = strlen(refname) + 1;
ref = xmalloc(sizeof(struct ref_entry) + len);
- hashcpy(ref->u.value.sha1, sha1);
- hashclr(ref->u.value.peeled);
+ hashcpy(ref->u.value.oid.hash, sha1);
+ oidclr(&ref->u.value.peeled);
memcpy(ref->name, refname, len);
ref->flag = flag;
return ref;
/* This is impossible by construction */
die("Reference directory conflict: %s", ref1->name);
- if (hashcmp(ref1->u.value.sha1, ref2->u.value.sha1))
+ if (oidcmp(&ref1->u.value.oid, &ref2->u.value.oid))
die("Duplicated ref, and SHA1s don't match: %s", ref1->name);
warning("Duplicated ref: %s", ref1->name);
{
if (entry->flag & REF_ISBROKEN)
return 0;
- if (!has_sha1_file(entry->u.value.sha1)) {
+ if (!has_sha1_file(entry->u.value.oid.hash)) {
error("%s does not point to a valid object!", entry->name);
return 0;
}
/* Store the old value, in case this is a recursive call: */
old_current_ref = current_ref;
current_ref = entry;
- retval = data->fn(entry->name + data->trim, entry->u.value.sha1,
+ retval = data->fn(entry->name + data->trim, &entry->u.value.oid,
entry->flag, data->cb_data);
current_ref = old_current_ref;
return retval;
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;
}
line.len == PEELED_LINE_LENGTH &&
line.buf[PEELED_LINE_LENGTH - 1] == '\n' &&
!get_sha1_hex(line.buf + 1, sha1)) {
- hashcpy(last->u.value.peeled, sha1);
+ hashcpy(last->u.value.peeled.hash, sha1);
/*
* Regardless of what the file header said,
* we definitely know the value of *this*
create_dir_entry(refs, refname.buf,
refname.len, 1));
} else {
+ int read_ok;
+
if (*refs->name) {
hashclr(sha1);
flag = 0;
- if (resolve_gitlink_ref(refs->name, refname.buf, sha1) < 0) {
- hashclr(sha1);
- flag |= REF_ISBROKEN;
- }
- } else if (read_ref_full(refname.buf,
- RESOLVE_REF_READING,
- sha1, &flag)) {
+ read_ok = !resolve_gitlink_ref(refs->name,
+ refname.buf, sha1);
+ } else {
+ read_ok = !read_ref_full(refname.buf,
+ RESOLVE_REF_READING,
+ sha1, &flag);
+ }
+
+ if (!read_ok) {
hashclr(sha1);
flag |= REF_ISBROKEN;
+ } else if (is_null_sha1(sha1)) {
+ /*
+ * It is so astronomically unlikely
+ * that NULL_SHA1 is the SHA-1 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);
hashclr(sha1);
flag |= REF_BAD_NAME | REF_ISBROKEN;
}
if (ref == NULL)
return -1;
- hashcpy(sha1, ref->u.value.sha1);
+ hashcpy(sha1, ref->u.value.oid.hash);
return 0;
}
{
int fd, len;
char buffer[128], *p;
- char *path;
+ const char *path;
if (recursion > MAXDEPTH || strlen(refname) > MAXREFLEN)
return -1;
*/
entry = get_packed_ref(refname);
if (entry) {
- hashcpy(sha1, entry->u.value.sha1);
+ hashcpy(sha1, entry->u.value.oid.hash);
if (flags)
*flags |= REF_ISPACKED;
return 0;
}
/* This function needs to return a meaningful errno on failure */
-const char *resolve_ref_unsafe(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)
+static const char *resolve_ref_unsafe_1(const char *refname,
+ int resolve_flags,
+ unsigned char *sha1,
+ int *flags,
+ struct strbuf *sb_path)
{
int depth = MAXDEPTH;
ssize_t len;
bad_name = 1;
}
for (;;) {
- char path[PATH_MAX];
+ const char *path;
struct stat st;
char *buf;
int fd;
return NULL;
}
- git_snpath(path, sizeof(path), "%s", refname);
+ strbuf_reset(sb_path);
+ strbuf_git_path(sb_path, "%s", refname);
+ path = sb_path->buf;
/*
* We might have to loop back here to avoid a race
}
}
+const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
+ unsigned char *sha1, int *flags)
+{
+ struct strbuf sb_path = STRBUF_INIT;
+ const char *ret = resolve_ref_unsafe_1(refname, resolve_flags,
+ sha1, flags, &sb_path);
+ strbuf_release(&sb_path);
+ return ret;
+}
+
char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags)
{
return xstrdup_or_null(resolve_ref_unsafe(ref, resolve_flags, sha1, flags));
return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, sha1, NULL);
}
-static int filter_refs(const char *refname, const unsigned char *sha1, int flags,
- void *data)
+static int filter_refs(const char *refname, const struct object_id *oid,
+ int flags, void *data)
{
struct ref_filter *filter = (struct ref_filter *)data;
+
if (wildmatch(filter->pattern, refname, 0, NULL))
return 0;
- return filter->fn(refname, sha1, flags, filter->cb_data);
+ return filter->fn(refname, oid, flags, filter->cb_data);
}
enum peel_status {
if (entry->flag & REF_KNOWS_PEELED) {
if (repeel) {
entry->flag &= ~REF_KNOWS_PEELED;
- hashclr(entry->u.value.peeled);
+ oidclr(&entry->u.value.peeled);
} else {
- return is_null_sha1(entry->u.value.peeled) ?
+ return is_null_oid(&entry->u.value.peeled) ?
PEEL_NON_TAG : PEEL_PEELED;
}
}
if (entry->flag & REF_ISSYMREF)
return PEEL_IS_SYMREF;
- status = peel_object(entry->u.value.sha1, entry->u.value.peeled);
+ status = peel_object(entry->u.value.oid.hash, entry->u.value.peeled.hash);
if (status == PEEL_PEELED || status == PEEL_NON_TAG)
entry->flag |= REF_KNOWS_PEELED;
return status;
|| !strcmp(current_ref->name, refname))) {
if (peel_entry(current_ref, 0))
return -1;
- hashcpy(sha1, current_ref->u.value.peeled);
+ hashcpy(sha1, current_ref->u.value.peeled.hash);
return 0;
}
if (r) {
if (peel_entry(r, 0))
return -1;
- hashcpy(sha1, r->u.value.peeled);
+ hashcpy(sha1, r->u.value.peeled.hash);
return 0;
}
}
const char *msg_fmt;
};
-static int warn_if_dangling_symref(const char *refname, const unsigned char *sha1,
+static int warn_if_dangling_symref(const char *refname, const struct object_id *oid,
int flags, void *cb_data)
{
struct warn_if_dangling_data *d = cb_data;
const char *resolves_to;
- unsigned char junk[20];
+ struct object_id junk;
if (!(flags & REF_ISSYMREF))
return 0;
- resolves_to = resolve_ref_unsafe(refname, 0, junk, NULL);
+ resolves_to = resolve_ref_unsafe(refname, 0, junk.hash, NULL);
if (!resolves_to
|| (d->refname
? strcmp(resolves_to, d->refname)
static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data)
{
- unsigned char sha1[20];
+ struct object_id oid;
int flag;
if (submodule) {
- if (resolve_gitlink_ref(submodule, "HEAD", sha1) == 0)
- return fn("HEAD", sha1, 0, cb_data);
+ if (resolve_gitlink_ref(submodule, "HEAD", oid.hash) == 0)
+ return fn("HEAD", &oid, 0, cb_data);
return 0;
}
- if (!read_ref_full("HEAD", RESOLVE_REF_READING, sha1, &flag))
- return fn("HEAD", sha1, flag, cb_data);
+ if (!read_ref_full("HEAD", RESOLVE_REF_READING, oid.hash, &flag))
+ return fn("HEAD", &oid, flag, cb_data);
return 0;
}
{
struct strbuf buf = STRBUF_INIT;
int ret = 0;
- unsigned char sha1[20];
+ struct object_id oid;
int flag;
strbuf_addf(&buf, "%sHEAD", get_git_namespace());
- if (!read_ref_full(buf.buf, RESOLVE_REF_READING, sha1, &flag))
- ret = fn(buf.buf, sha1, flag, cb_data);
+ if (!read_ref_full(buf.buf, RESOLVE_REF_READING, oid.hash, &flag))
+ ret = fn(buf.buf, &oid, flag, cb_data);
strbuf_release(&buf);
return ret;
free(lock);
}
-/* This function should make sure errno is meaningful on error */
-static struct ref_lock *verify_lock(struct ref_lock *lock,
- const unsigned char *old_sha1, int mustexist)
+/*
+ * Verify that the reference locked by lock has the value old_sha1.
+ * Fail if the reference doesn't exist and mustexist is set. Return 0
+ * on success. On error, write an error message to err, set errno, and
+ * return a negative value.
+ */
+static int verify_lock(struct ref_lock *lock,
+ const unsigned char *old_sha1, int mustexist,
+ struct strbuf *err)
{
+ assert(err);
+
if (read_ref_full(lock->ref_name,
mustexist ? RESOLVE_REF_READING : 0,
- lock->old_sha1, NULL)) {
+ lock->old_oid.hash, NULL)) {
int save_errno = errno;
- error("Can't verify ref %s", lock->ref_name);
- unlock_ref(lock);
+ strbuf_addf(err, "can't verify ref %s", lock->ref_name);
errno = save_errno;
- return NULL;
+ return -1;
}
- if (hashcmp(lock->old_sha1, old_sha1)) {
- error("Ref %s is at %s but expected %s", lock->ref_name,
- sha1_to_hex(lock->old_sha1), sha1_to_hex(old_sha1));
- unlock_ref(lock);
+ if (hashcmp(lock->old_oid.hash, old_sha1)) {
+ strbuf_addf(err, "ref %s is at %s but expected %s",
+ lock->ref_name,
+ sha1_to_hex(lock->old_oid.hash),
+ sha1_to_hex(old_sha1));
errno = EBUSY;
- return NULL;
+ return -1;
}
- return lock;
+ return 0;
}
static int remove_empty_directories(const char *file)
unsigned int flags, int *type_p,
struct strbuf *err)
{
- char *ref_file;
+ const char *ref_file;
const char *orig_refname = refname;
struct ref_lock *lock;
int last_errno = 0;
assert(err);
lock = xcalloc(1, sizeof(struct ref_lock));
- lock->lock_fd = -1;
if (mustexist)
resolve_flags |= RESOLVE_REF_READING;
}
refname = resolve_ref_unsafe(refname, resolve_flags,
- lock->old_sha1, &type);
+ lock->old_oid.hash, &type);
if (!refname && errno == EISDIR) {
/* we are trying to lock foo but we used to
* have foo/bar which now does not exist;
goto error_return;
}
refname = resolve_ref_unsafe(orig_refname, resolve_flags,
- lock->old_sha1, &type);
+ lock->old_oid.hash, &type);
}
if (type_p)
*type_p = type;
* refname, nor a packed ref whose name is a proper prefix of
* our refname.
*/
- if (is_null_sha1(lock->old_sha1) &&
+ if (is_null_oid(&lock->old_oid) &&
verify_refname_available(refname, extras, skip,
get_packed_refs(&ref_cache), err)) {
last_errno = ENOTDIR;
ref_file = git_path("%s", refname);
retry:
- switch (safe_create_leading_directories(ref_file)) {
+ switch (safe_create_leading_directories_const(ref_file)) {
case SCLD_OK:
break; /* success */
case SCLD_VANISHED:
goto error_return;
}
- lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, lflags);
- if (lock->lock_fd < 0) {
+ if (hold_lock_file_for_update(lock->lk, ref_file, lflags) < 0) {
last_errno = errno;
if (errno == ENOENT && --attempts_remaining > 0)
/*
goto error_return;
}
}
- return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
+ if (old_sha1 && verify_lock(lock, old_sha1, mustexist, err)) {
+ last_errno = errno;
+ goto error_return;
+ }
+ return lock;
error_return:
unlock_ref(lock);
if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
error("internal error: %s is not a valid packed reference!",
entry->name);
- write_packed_entry(cb_data, entry->name, entry->u.value.sha1,
+ write_packed_entry(cb_data, entry->name, entry->u.value.oid.hash,
peel_status == PEEL_PEELED ?
- entry->u.value.peeled : NULL);
+ entry->u.value.peeled.hash : NULL);
return 0;
}
/* This should return a meaningful errno on failure */
int lock_packed_refs(int flags)
{
+ static int timeout_configured = 0;
+ static int timeout_value = 1000;
+
struct packed_ref_cache *packed_ref_cache;
- if (hold_lock_file_for_update(&packlock, git_path("packed-refs"), flags) < 0)
+ if (!timeout_configured) {
+ git_config_get_int("core.packedrefstimeout", &timeout_value);
+ timeout_configured = 1;
+ }
+
+ if (hold_lock_file_for_update_timeout(
+ &packlock, git_path("packed-refs"),
+ flags, timeout_value) < 0)
return -1;
/*
* Get the current packed-refs while holding the lock. If the
peel_status = peel_entry(entry, 1);
if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
die("internal error peeling reference %s (%s)",
- entry->name, sha1_to_hex(entry->u.value.sha1));
+ entry->name, oid_to_hex(&entry->u.value.oid));
packed_entry = find_ref(cb->packed_refs, entry->name);
if (packed_entry) {
/* Overwrite existing packed entry with info from loose entry */
packed_entry->flag = REF_ISPACKED | REF_KNOWS_PEELED;
- hashcpy(packed_entry->u.value.sha1, entry->u.value.sha1);
+ oidcpy(&packed_entry->u.value.oid, &entry->u.value.oid);
} else {
- packed_entry = create_ref_entry(entry->name, entry->u.value.sha1,
+ packed_entry = create_ref_entry(entry->name, entry->u.value.oid.hash,
REF_ISPACKED | REF_KNOWS_PEELED, 0);
add_ref(cb->packed_refs, packed_entry);
}
- hashcpy(packed_entry->u.value.peeled, entry->u.value.peeled);
+ oidcpy(&packed_entry->u.value.peeled, &entry->u.value.peeled);
/* Schedule the loose reference for pruning if requested. */
if ((cb->flags & PACK_REFS_PRUNE)) {
int namelen = strlen(entry->name) + 1;
struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
- hashcpy(n->sha1, entry->u.value.sha1);
+ hashcpy(n->sha1, entry->u.value.oid.hash);
strcpy(n->name, entry->name);
n->next = cb->ref_to_prune;
cb->ref_to_prune = n;
int attempts_remaining = 4;
retry:
- switch (safe_create_leading_directories(git_path("logs/%s", newrefname))) {
+ switch (safe_create_leading_directories_const(git_path("logs/%s", newrefname))) {
case SCLD_OK:
break; /* success */
case SCLD_VANISHED:
return ret;
}
-static int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1,
- const char *logmsg);
+static int write_ref_to_lockfile(struct ref_lock *lock, const unsigned char *sha1);
+static int commit_ref_update(struct ref_lock *lock,
+ const unsigned char *sha1, const char *logmsg);
int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
{
strbuf_release(&err);
goto rollback;
}
- hashcpy(lock->old_sha1, orig_sha1);
- if (write_ref_sha1(lock, orig_sha1, logmsg)) {
+ hashcpy(lock->old_oid.hash, orig_sha1);
+
+ if (write_ref_to_lockfile(lock, orig_sha1) ||
+ commit_ref_update(lock, orig_sha1, logmsg)) {
error("unable to write current sha1 into %s", newrefname);
goto rollback;
}
flag = log_all_ref_updates;
log_all_ref_updates = 0;
- if (write_ref_sha1(lock, orig_sha1, NULL))
+ if (write_ref_to_lockfile(lock, orig_sha1) ||
+ commit_ref_update(lock, orig_sha1, NULL))
error("unable to write current sha1 into %s", oldrefname);
log_all_ref_updates = flag;
{
if (close_lock_file(lock->lk))
return -1;
- lock->lock_fd = -1;
return 0;
}
{
if (commit_lock_file(lock->lk))
return -1;
- lock->lock_fd = -1;
return 0;
}
}
/* This function must set a meaningful errno on failure */
-int log_ref_setup(const char *refname, char *logfile, int bufsize)
+int log_ref_setup(const char *refname, struct strbuf *sb_logfile)
{
int logfd, oflags = O_APPEND | O_WRONLY;
+ char *logfile;
- git_snpath(logfile, bufsize, "logs/%s", refname);
+ strbuf_git_path(sb_logfile, "logs/%s", refname);
+ logfile = sb_logfile->buf;
+ /* make sure the rest of the function can't change "logfile" */
+ sb_logfile = NULL;
if (log_all_ref_updates &&
(starts_with(refname, "refs/heads/") ||
starts_with(refname, "refs/remotes/") ||
return 0;
}
-static int log_ref_write(const char *refname, const unsigned char *old_sha1,
- const unsigned char *new_sha1, const char *msg)
+static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,
+ const unsigned char *new_sha1, const char *msg,
+ struct strbuf *sb_log_file)
{
int logfd, result, oflags = O_APPEND | O_WRONLY;
- char log_file[PATH_MAX];
+ char *log_file;
if (log_all_ref_updates < 0)
log_all_ref_updates = !is_bare_repository();
- result = log_ref_setup(refname, log_file, sizeof(log_file));
+ result = log_ref_setup(refname, sb_log_file);
if (result)
return result;
+ log_file = sb_log_file->buf;
+ /* make sure the rest of the function can't change "log_file" */
+ sb_log_file = NULL;
logfd = open(log_file, oflags);
if (logfd < 0)
return 0;
}
+static int log_ref_write(const char *refname, const unsigned char *old_sha1,
+ const unsigned char *new_sha1, const char *msg)
+{
+ struct strbuf sb = STRBUF_INIT;
+ int ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb);
+ strbuf_release(&sb);
+ return ret;
+}
+
int is_branch(const char *refname)
{
return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
}
/*
- * Write sha1 into the ref specified by the lock. Make sure that errno
- * is sane on error.
+ * Write sha1 into the open lockfile, then close the lockfile. On
+ * errors, rollback the lockfile and set errno to reflect the problem.
*/
-static int write_ref_sha1(struct ref_lock *lock,
- const unsigned char *sha1, const char *logmsg)
+static int write_ref_to_lockfile(struct ref_lock *lock,
+ const unsigned char *sha1)
{
static char term = '\n';
struct object *o;
errno = EINVAL;
return -1;
}
- if (write_in_full(lock->lock_fd, sha1_to_hex(sha1), 40) != 40 ||
- write_in_full(lock->lock_fd, &term, 1) != 1 ||
+ if (write_in_full(lock->lk->fd, sha1_to_hex(sha1), 40) != 40 ||
+ write_in_full(lock->lk->fd, &term, 1) != 1 ||
close_ref(lock) < 0) {
int save_errno = errno;
error("Couldn't write %s", lock->lk->filename.buf);
errno = save_errno;
return -1;
}
+ return 0;
+}
+
+/*
+ * Commit a change to a loose reference that has already been written
+ * to the loose reference lockfile. Also update the reflogs if
+ * necessary, using the specified lockmsg (which can be NULL).
+ */
+static int commit_ref_update(struct ref_lock *lock,
+ const unsigned char *sha1, const char *logmsg)
+{
clear_loose_ref_cache(&ref_cache);
- if (log_ref_write(lock->ref_name, lock->old_sha1, sha1, logmsg) < 0 ||
+ if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg) < 0 ||
(strcmp(lock->ref_name, lock->orig_ref_name) &&
- log_ref_write(lock->orig_ref_name, lock->old_sha1, sha1, logmsg) < 0)) {
+ log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, logmsg) < 0)) {
unlock_ref(lock);
return -1;
}
head_sha1, &head_flag);
if (head_ref && (head_flag & REF_ISSYMREF) &&
!strcmp(head_ref, lock->ref_name))
- log_ref_write("HEAD", lock->old_sha1, sha1, logmsg);
+ log_ref_write("HEAD", lock->old_oid.hash, sha1, logmsg);
}
if (commit_ref(lock)) {
error("Couldn't set %s", lock->ref_name);
strbuf_addch(name, '/');
retval = do_for_each_reflog(name, fn, cb_data);
} else {
- unsigned char sha1[20];
- if (read_ref_full(name->buf, 0, sha1, NULL))
+ struct object_id oid;
+
+ if (read_ref_full(name->buf, 0, oid.hash, NULL))
retval = error("bad ref for %s", name->buf);
else
- retval = fn(name->buf, sha1, 0, cb_data);
+ retval = fn(name->buf, &oid, 0, cb_data);
}
if (retval)
break;
goto cleanup;
}
- /* Acquire all locks while verifying old values */
+ /*
+ * Acquire all locks, verify old values if provided, check
+ * 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.
+ */
for (i = 0; i < n; i++) {
struct ref_update *update = updates[i];
- unsigned int flags = update->flags;
- if ((flags & REF_HAVE_NEW) && is_null_sha1(update->new_sha1))
- flags |= REF_DELETING;
+ if ((update->flags & REF_HAVE_NEW) &&
+ is_null_sha1(update->new_sha1))
+ update->flags |= REF_DELETING;
update->lock = lock_ref_sha1_basic(
update->refname,
((update->flags & REF_HAVE_OLD) ?
update->old_sha1 : NULL),
&affected_refnames, NULL,
- flags,
+ update->flags,
&update->type,
err);
if (!update->lock) {
? TRANSACTION_NAME_CONFLICT
: TRANSACTION_GENERIC_ERROR;
reason = strbuf_detach(err, NULL);
- strbuf_addf(err, "Cannot lock ref '%s': %s",
+ strbuf_addf(err, "cannot lock ref '%s': %s",
update->refname, reason);
free(reason);
goto cleanup;
}
- }
-
- /* Perform updates first so live commits remain referenced */
- for (i = 0; i < n; i++) {
- struct ref_update *update = updates[i];
- int flags = update->flags;
-
- if ((flags & REF_HAVE_NEW) && !is_null_sha1(update->new_sha1)) {
+ if ((update->flags & REF_HAVE_NEW) &&
+ !(update->flags & REF_DELETING)) {
int overwriting_symref = ((update->type & REF_ISSYMREF) &&
(update->flags & REF_NODEREF));
- if (!overwriting_symref
- && !hashcmp(update->lock->old_sha1, update->new_sha1)) {
+ if (!overwriting_symref &&
+ !hashcmp(update->lock->old_oid.hash, update->new_sha1)) {
/*
* The reference already has the desired
* value, so we don't need to write it.
*/
- unlock_ref(update->lock);
+ } else if (write_ref_to_lockfile(update->lock,
+ update->new_sha1)) {
+ /*
+ * The lock was freed upon failure of
+ * write_ref_to_lockfile():
+ */
+ update->lock = NULL;
+ strbuf_addf(err, "cannot update the ref '%s'.",
+ update->refname);
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ } else {
+ update->flags |= REF_NEEDS_COMMIT;
+ }
+ }
+ if (!(update->flags & REF_NEEDS_COMMIT)) {
+ /*
+ * We didn't have to write anything to the lockfile.
+ * Close it to free up the file descriptor:
+ */
+ if (close_ref(update->lock)) {
+ strbuf_addf(err, "Couldn't close %s.lock",
+ update->refname);
+ goto cleanup;
+ }
+ }
+ }
+
+ /* Perform updates first so live commits remain referenced */
+ for (i = 0; i < n; i++) {
+ struct ref_update *update = updates[i];
+
+ if (update->flags & REF_NEEDS_COMMIT) {
+ if (commit_ref_update(update->lock,
+ update->new_sha1, update->msg)) {
+ /* freed by commit_ref_update(): */
update->lock = NULL;
- } else if (write_ref_sha1(update->lock, update->new_sha1,
- update->msg)) {
- update->lock = NULL; /* freed by write_ref_sha1 */
strbuf_addf(err, "Cannot update the ref '%s'.",
update->refname);
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
} else {
- /* freed by write_ref_sha1(): */
+ /* freed by commit_ref_update(): */
update->lock = NULL;
}
}
/* Perform deletes now that updates are safely completed */
for (i = 0; i < n; i++) {
struct ref_update *update = updates[i];
- int flags = update->flags;
- if ((flags & REF_HAVE_NEW) && is_null_sha1(update->new_sha1)) {
+ if (update->flags & REF_DELETING) {
if (delete_ref_loose(update->lock, update->type, err)) {
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
- if (!(flags & REF_ISPRUNING))
+ if (!(update->flags & REF_ISPRUNING))
string_list_append(&refs_to_delete,
update->lock->ref_name);
}
status |= error("couldn't write %s: %s", log_file,
strerror(errno));
} else if (update &&
- (write_in_full(lock->lock_fd,
+ (write_in_full(lock->lk->fd,
sha1_to_hex(cb.last_kept_sha1), 40) != 40 ||
- write_str_in_full(lock->lock_fd, "\n") != 1 ||
+ write_str_in_full(lock->lk->fd, "\n") != 1 ||
close_ref(lock) < 0)) {
status |= error("couldn't write %s",
lock->lk->filename.buf);