#include "cache.h"
+#include "lockfile.h"
#include "refs.h"
#include "object.h"
#include "tag.h"
if (refname[1] == '\0')
return -1; /* Component equals ".". */
}
- if (cp - refname >= 5 && !memcmp(cp - 5, ".lock", 5))
+ if (cp - refname >= LOCK_SUFFIX_LEN &&
+ !memcmp(cp - LOCK_SUFFIX_LEN, LOCK_SUFFIX, LOCK_SUFFIX_LEN))
return -1; /* Refname ends with ".lock". */
return cp - refname;
}
}
}
-static int entry_matches(struct ref_entry *entry, const char *refname)
+static int entry_matches(struct ref_entry *entry, const struct string_list *list)
{
- return refname && !strcmp(entry->name, refname);
+ return list && string_list_has_string(list, entry->name);
}
struct nonmatching_ref_data {
- const char *skip;
+ const struct string_list *skip;
struct ref_entry *found;
};
/*
* Return true iff a reference named refname could be created without
* conflicting with the name of an existing reference in dir. If
- * oldrefname is non-NULL, ignore potential conflicts with oldrefname
- * (e.g., because oldrefname is scheduled for deletion in the same
+ * skip is non-NULL, ignore potential conflicts with refs in skip
+ * (e.g., because they are scheduled for deletion in the same
* operation).
*
* Two reference names conflict if one of them exactly matches the
* leading components of the other; e.g., "foo/bar" conflicts with
* both "foo" and with "foo/bar/baz" but not with "foo/bar" or
* "foo/barbados".
+ *
+ * skip must be sorted.
*/
-static int is_refname_available(const char *refname, const char *oldrefname,
+static int is_refname_available(const char *refname,
+ const struct string_list *skip,
struct ref_dir *dir)
{
const char *slash;
* looking for a conflict with a leaf entry.
*
* If we find one, we still must make sure it is
- * not "oldrefname".
+ * not in "skip".
*/
pos = search_ref_dir(dir, refname, slash - refname);
if (pos >= 0) {
struct ref_entry *entry = dir->entries[pos];
- if (entry_matches(entry, oldrefname))
+ if (entry_matches(entry, skip))
return 1;
report_refname_conflict(entry, refname);
return 0;
/*
* We found a directory named "refname". It is a
* problem iff it contains any ref that is not
- * "oldrefname".
+ * in "skip".
*/
struct ref_entry *entry = dir->entries[pos];
struct ref_dir *dir = get_ref_dir(entry);
struct nonmatching_ref_data data;
- data.skip = oldrefname;
+ data.skip = skip;
sort_ref_dir(dir);
if (!do_for_each_entry_in_dir(dir, 0, nonmatching_ref_fn, &data))
return 1;
}
/*
- * Locks a "refs/" ref returning the lock on success and NULL on failure.
+ * Locks a ref returning the lock on success and NULL on failure.
* On failure errno is set to something meaningful.
*/
static struct ref_lock *lock_ref_sha1_basic(const char *refname,
const unsigned char *old_sha1,
+ const struct string_list *skip,
int flags, int *type_p)
{
char *ref_file;
int missing = 0;
int attempts_remaining = 3;
+ if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+ errno = EINVAL;
+ return NULL;
+ }
+
lock = xcalloc(1, sizeof(struct ref_lock));
lock->lock_fd = -1;
* name is a proper prefix of our refname.
*/
if (missing &&
- !is_refname_available(refname, NULL, get_packed_refs(&ref_cache))) {
+ !is_refname_available(refname, skip, get_packed_refs(&ref_cache))) {
last_errno = ENOTDIR;
goto error_return;
}
lflags = 0;
if (flags & REF_NODEREF) {
refname = orig_refname;
- lflags |= LOCK_NODEREF;
+ lflags |= LOCK_NO_DEREF;
}
lock->ref_name = xstrdup(refname);
lock->orig_ref_name = xstrdup(orig_refname);
*/
goto retry;
else
- unable_to_lock_index_die(ref_file, errno);
+ unable_to_lock_die(ref_file, errno);
}
return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;
const unsigned char *old_sha1,
int flags, int *type_p)
{
- if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
- return NULL;
- return lock_ref_sha1_basic(refname, old_sha1, flags, type_p);
+ return lock_ref_sha1_basic(refname, old_sha1, NULL, flags, type_p);
}
/*
if (!packed_ref_cache->lock)
die("internal error: packed-refs not locked");
- out = fdopen(packed_ref_cache->lock->fd, "w");
+ 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);
do_for_each_entry_in_dir(get_packed_ref_dir(packed_ref_cache),
0, write_packed_entry_fn, out);
- if (fclose(out))
- die_errno("write error");
- packed_ref_cache->lock->fd = -1;
if (commit_lock_file(packed_ref_cache->lock)) {
save_errno = errno;
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_delete(transaction, r->name, r->sha1,
- REF_ISPRUNING, 1, &err) ||
- ref_transaction_commit(transaction, NULL, &err)) {
+ REF_ISPRUNING, 1, NULL, &err) ||
+ ref_transaction_commit(transaction, &err)) {
ref_transaction_free(transaction);
error("%s", err.buf);
strbuf_release(&err);
return ret;
}
-static int delete_ref_loose(struct ref_lock *lock, int flag)
+static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
{
if (!(flag & REF_ISPACKED) || flag & REF_ISSYMREF) {
- /* loose */
- int err, i = strlen(lock->lk->filename) - 5; /* .lock */
-
- lock->lk->filename[i] = 0;
- err = unlink_or_warn(lock->lk->filename);
- lock->lk->filename[i] = '.';
- if (err && errno != ENOENT)
+ /*
+ * loose. The loose file name is the same as the
+ * lockfile name, minus ".lock":
+ */
+ char *loose_filename = get_locked_file_path(lock->lk);
+ int res = unlink_or_msg(loose_filename, err);
+ free(loose_filename);
+ if (res)
return 1;
}
return 0;
transaction = ref_transaction_begin(&err);
if (!transaction ||
ref_transaction_delete(transaction, refname, sha1, delopt,
- sha1 && !is_null_sha1(sha1), &err) ||
- ref_transaction_commit(transaction, NULL, &err)) {
+ sha1 && !is_null_sha1(sha1), NULL, &err) ||
+ ref_transaction_commit(transaction, &err)) {
error("%s", err.buf);
ref_transaction_free(transaction);
strbuf_release(&err);
return 0;
}
+static int rename_ref_available(const char *oldname, const char *newname)
+{
+ struct string_list skip = STRING_LIST_INIT_NODUP;
+ int ret;
+
+ string_list_insert(&skip, oldname);
+ ret = is_refname_available(newname, &skip, get_packed_refs(&ref_cache))
+ && is_refname_available(newname, &skip, get_loose_refs(&ref_cache));
+ string_list_clear(&skip, 0);
+ return ret;
+}
+
+static int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1,
+ const char *logmsg);
+
int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
{
unsigned char sha1[20], orig_sha1[20];
if (!symref)
return error("refname %s not found", oldrefname);
- if (!is_refname_available(newrefname, oldrefname, get_packed_refs(&ref_cache)))
- return 1;
-
- if (!is_refname_available(newrefname, oldrefname, get_loose_refs(&ref_cache)))
+ if (!rename_ref_available(oldrefname, newrefname))
return 1;
if (log && rename(git_path("logs/%s", oldrefname), git_path(TMP_RENAMED_LOG)))
goto rollback;
}
- if (!read_ref_full(newrefname, sha1, 1, &flag) &&
+ if (!read_ref_full(newrefname, sha1, 1, NULL) &&
delete_ref(newrefname, sha1, REF_NODEREF)) {
if (errno==EISDIR) {
if (remove_empty_directories(git_path("%s", newrefname))) {
logmoved = log;
- lock = lock_ref_sha1_basic(newrefname, NULL, 0, NULL);
+ lock = lock_ref_sha1_basic(newrefname, NULL, NULL, 0, NULL);
if (!lock) {
error("unable to lock %s for update", newrefname);
goto rollback;
return 0;
rollback:
- lock = lock_ref_sha1_basic(oldrefname, NULL, 0, NULL);
+ lock = lock_ref_sha1_basic(oldrefname, NULL, NULL, 0, NULL);
if (!lock) {
error("unable to lock %s for rollback", oldrefname);
goto rollbacklog;
return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
}
-/* This function must return a meaningful errno */
-int write_ref_sha1(struct ref_lock *lock,
+/*
+ * Write sha1 into the ref specified by the lock. Make sure that errno
+ * is sane on error.
+ */
+static int write_ref_sha1(struct ref_lock *lock,
const unsigned char *sha1, const char *logmsg)
{
static char term = '\n';
write_in_full(lock->lock_fd, &term, 1) != 1 ||
close_ref(lock) < 0) {
int save_errno = errno;
- error("Couldn't write %s", lock->lk->filename);
+ error("Couldn't write %s", lock->lk->filename.buf);
unlock_ref(lock);
errno = save_errno;
return -1;
return 1;
}
-int read_ref_at(const char *refname, unsigned long at_time, int cnt,
+int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time, int cnt,
unsigned char *sha1, char **msg,
unsigned long *cutoff_time, int *cutoff_tz, int *cutoff_cnt)
{
for_each_reflog_ent_reverse(refname, read_ref_at_ent, &cb);
- if (!cb.reccnt)
- die("Log for %s is empty.", refname);
+ if (!cb.reccnt) {
+ if (flags & GET_SHA1_QUIETLY)
+ exit(128);
+ else
+ die("Log for %s is empty.", refname);
+ }
if (cb.found_it)
return 0;
int have_old; /* 1 if old_sha1 is valid, 0 otherwise */
struct ref_lock *lock;
int type;
+ char *msg;
const char refname[FLEX_ARRAY];
};
if (!transaction)
return;
- for (i = 0; i < transaction->nr; i++)
+ for (i = 0; i < transaction->nr; i++) {
+ free(transaction->updates[i]->msg);
free(transaction->updates[i]);
-
+ }
free(transaction->updates);
free(transaction);
}
const char *refname,
const unsigned char *new_sha1,
const unsigned char *old_sha1,
- int flags, int have_old,
+ int flags, int have_old, const char *msg,
struct strbuf *err)
{
struct ref_update *update;
update->have_old = have_old;
if (have_old)
hashcpy(update->old_sha1, old_sha1);
+ if (msg)
+ update->msg = xstrdup(msg);
return 0;
}
int ref_transaction_create(struct ref_transaction *transaction,
const char *refname,
const unsigned char *new_sha1,
- int flags,
+ int flags, const char *msg,
struct strbuf *err)
{
struct ref_update *update;
hashclr(update->old_sha1);
update->flags = flags;
update->have_old = 1;
+ if (msg)
+ update->msg = xstrdup(msg);
return 0;
}
int ref_transaction_delete(struct ref_transaction *transaction,
const char *refname,
const unsigned char *old_sha1,
- int flags, int have_old,
+ int flags, int have_old, const char *msg,
struct strbuf *err)
{
struct ref_update *update;
assert(!is_null_sha1(old_sha1));
hashcpy(update->old_sha1, old_sha1);
}
+ if (msg)
+ update->msg = xstrdup(msg);
return 0;
}
t = ref_transaction_begin(&err);
if (!t ||
ref_transaction_update(t, refname, sha1, oldval, flags,
- !!oldval, &err) ||
- ref_transaction_commit(t, action, &err)) {
+ !!oldval, action, &err) ||
+ ref_transaction_commit(t, &err)) {
const char *str = "update_ref failed for ref '%s': %s";
ref_transaction_free(t);
}
int ref_transaction_commit(struct ref_transaction *transaction,
- const char *msg, struct strbuf *err)
+ struct strbuf *err)
{
int ret = 0, delnum = 0, i;
const char **delnames;
/* Copy, sort, and reject duplicate refs */
qsort(updates, n, sizeof(*updates), ref_update_compare);
- ret = ref_update_reject_duplicates(updates, n, err);
- if (ret)
+ if (ref_update_reject_duplicates(updates, n, err)) {
+ ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
+ }
/* Acquire all locks while verifying old values */
for (i = 0; i < n; i++) {
struct ref_update *update = updates[i];
- update->lock = lock_any_ref_for_update(update->refname,
- (update->have_old ?
- update->old_sha1 :
- NULL),
- update->flags,
- &update->type);
+ update->lock = lock_ref_sha1_basic(update->refname,
+ (update->have_old ?
+ update->old_sha1 :
+ NULL),
+ NULL,
+ update->flags,
+ &update->type);
if (!update->lock) {
+ ret = (errno == ENOTDIR)
+ ? TRANSACTION_NAME_CONFLICT
+ : TRANSACTION_GENERIC_ERROR;
if (err)
strbuf_addf(err, "Cannot lock the ref '%s'.",
update->refname);
- ret = 1;
goto cleanup;
}
}
struct ref_update *update = updates[i];
if (!is_null_sha1(update->new_sha1)) {
- ret = write_ref_sha1(update->lock, update->new_sha1,
- msg);
- update->lock = NULL; /* freed by write_ref_sha1 */
- if (ret) {
+ if (write_ref_sha1(update->lock, update->new_sha1,
+ update->msg)) {
+ update->lock = NULL; /* freed by write_ref_sha1 */
if (err)
strbuf_addf(err, "Cannot update the ref '%s'.",
update->refname);
+ ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
}
+ update->lock = NULL; /* freed by write_ref_sha1 */
}
}
struct ref_update *update = updates[i];
if (update->lock) {
- ret |= delete_ref_loose(update->lock, update->type);
+ if (delete_ref_loose(update->lock, update->type, err))
+ ret = TRANSACTION_GENERIC_ERROR;
+
if (!(update->flags & REF_ISPRUNING))
delnames[delnum++] = update->lock->ref_name;
}
}
- ret |= repack_without_refs(delnames, delnum, err);
+ if (repack_without_refs(delnames, delnum, err))
+ ret = TRANSACTION_GENERIC_ERROR;
for (i = 0; i < delnum; i++)
unlink_or_warn(git_path("logs/%s", delnames[i]));
clear_loose_ref_cache(&ref_cache);