Merge branch 'rs/ref-transaction-0'

Early part of the "ref transaction" topic.

* rs/ref-transaction-0:
  refs.c: change ref_transaction_update() to do error checking and return status
  refs.c: remove the onerr argument to ref_transaction_commit
  update-ref: use err argument to get error from ref_transaction_commit
  refs.c: make update_ref_write update a strbuf on failure
  refs.c: make ref_update_reject_duplicates take a strbuf argument for errors
  refs.c: log_ref_write should try to return meaningful errno
  refs.c: make resolve_ref_unsafe set errno to something meaningful on error
  refs.c: commit_packed_refs to return a meaningful errno on failure
  refs.c: make remove_empty_directories always set errno to something sane
  refs.c: verify_lock should set errno to something meaningful
  refs.c: make sure log_ref_setup returns a meaningful errno
  refs.c: add an err argument to repack_without_refs
  lockfile.c: make lock_file return a meaningful errno on failurei
  lockfile.c: add a new public function unable_to_lock_message
  refs.c: add a strbuf argument to ref_transaction_commit for error logging
  refs.c: allow passing NULL to ref_transaction_free
  refs.c: constify the sha arguments for ref_transaction_create|delete|update
  refs.c: ref_transaction_commit should not free the transaction
  refs.c: remove ref_transaction_rollback
This commit is contained in:
Junio C Hamano
2014-07-21 11:18:37 -07:00
6 changed files with 199 additions and 100 deletions

178
refs.c
View File

@ -1533,6 +1533,7 @@ static const char *handle_missing_loose_ref(const char *refname,
}
}
/* This function needs to return a meaningful errno on failure */
const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int reading, int *flag)
{
int depth = MAXDEPTH;
@ -1543,8 +1544,10 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
if (flag)
*flag = 0;
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
errno = EINVAL;
return NULL;
}
for (;;) {
char path[PATH_MAX];
@ -1552,8 +1555,10 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
char *buf;
int fd;
if (--depth < 0)
if (--depth < 0) {
errno = ELOOP;
return NULL;
}
git_snpath(path, sizeof(path), "%s", refname);
@ -1615,9 +1620,13 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
return NULL;
}
len = read_in_full(fd, buffer, sizeof(buffer)-1);
close(fd);
if (len < 0)
if (len < 0) {
int save_errno = errno;
close(fd);
errno = save_errno;
return NULL;
}
close(fd);
while (len && isspace(buffer[len-1]))
len--;
buffer[len] = '\0';
@ -1634,6 +1643,7 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
(buffer[40] != '\0' && !isspace(buffer[40]))) {
if (flag)
*flag |= REF_ISBROKEN;
errno = EINVAL;
return NULL;
}
return refname;
@ -1646,6 +1656,7 @@ const char *resolve_ref_unsafe(const char *refname, unsigned char *sha1, int rea
if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
if (flag)
*flag |= REF_ISBROKEN;
errno = EINVAL;
return NULL;
}
refname = strcpy(refname_buffer, buf);
@ -2131,18 +2142,22 @@ int refname_match(const char *abbrev_name, const char *full_name)
return 0;
}
/* 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)
{
if (read_ref_full(lock->ref_name, lock->old_sha1, mustexist, NULL)) {
int save_errno = errno;
error("Can't verify ref %s", lock->ref_name);
unlock_ref(lock);
errno = save_errno;
return NULL;
}
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);
errno = EBUSY;
return NULL;
}
return lock;
@ -2155,14 +2170,16 @@ static int remove_empty_directories(const char *file)
* only empty directories), remove them.
*/
struct strbuf path;
int result;
int result, save_errno;
strbuf_init(&path, 20);
strbuf_addstr(&path, file);
result = remove_dir_recursively(&path, REMOVE_DIR_EMPTY_ONLY);
save_errno = errno;
strbuf_release(&path);
errno = save_errno;
return result;
}
@ -2251,6 +2268,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
return logs_found;
}
/* This function should make sure errno is meaningful on error */
static struct ref_lock *lock_ref_sha1_basic(const char *refname,
const unsigned char *old_sha1,
int flags, int *type_p)
@ -2411,6 +2429,7 @@ static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data)
return 0;
}
/* This should return a meaningful errno on failure */
int lock_packed_refs(int flags)
{
struct packed_ref_cache *packed_ref_cache;
@ -2430,11 +2449,16 @@ int lock_packed_refs(int flags)
return 0;
}
/*
* Commit the packed refs changes.
* On error we must make sure that errno contains a meaningful value.
*/
int commit_packed_refs(void)
{
struct packed_ref_cache *packed_ref_cache =
get_packed_ref_cache(&ref_cache);
int error = 0;
int save_errno = 0;
if (!packed_ref_cache->lock)
die("internal error: packed-refs not locked");
@ -2444,10 +2468,13 @@ int commit_packed_refs(void)
do_for_each_entry_in_dir(get_packed_ref_dir(packed_ref_cache),
0, write_packed_entry_fn,
&packed_ref_cache->lock->fd);
if (commit_lock_file(packed_ref_cache->lock))
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;
}
@ -2654,12 +2681,12 @@ static int curate_packed_ref_fn(struct ref_entry *entry, void *cb_data)
return 0;
}
int repack_without_refs(const char **refnames, int n)
int repack_without_refs(const char **refnames, int n, struct strbuf *err)
{
struct ref_dir *packed;
struct string_list refs_to_delete = STRING_LIST_INIT_DUP;
struct string_list_item *ref_to_delete;
int i, removed = 0;
int i, ret, removed = 0;
/* Look for a packed ref */
for (i = 0; i < n; i++)
@ -2671,6 +2698,11 @@ int repack_without_refs(const char **refnames, int n)
return 0; /* no refname exists in packed refs */
if (lock_packed_refs(0)) {
if (err) {
unable_to_lock_message(git_path("packed-refs"), errno,
err);
return -1;
}
unable_to_lock_error(git_path("packed-refs"), errno);
return error("cannot delete '%s' from packed refs", refnames[i]);
}
@ -2697,12 +2729,16 @@ int repack_without_refs(const char **refnames, int n)
}
/* Write what remains */
return commit_packed_refs();
ret = commit_packed_refs();
if (ret && err)
strbuf_addf(err, "unable to overwrite old ref-pack file: %s",
strerror(errno));
return ret;
}
static int repack_without_ref(const char *refname)
{
return repack_without_refs(&refname, 1);
return repack_without_refs(&refname, 1, NULL);
}
static int delete_ref_loose(struct ref_lock *lock, int flag)
@ -2940,6 +2976,7 @@ static int copy_msg(char *buf, const char *msg)
return cp - buf;
}
/* This function must set a meaningful errno on failure */
int log_ref_setup(const char *refname, char *logfile, int bufsize)
{
int logfd, oflags = O_APPEND | O_WRONLY;
@ -2950,9 +2987,12 @@ int log_ref_setup(const char *refname, char *logfile, int bufsize)
starts_with(refname, "refs/remotes/") ||
starts_with(refname, "refs/notes/") ||
!strcmp(refname, "HEAD"))) {
if (safe_create_leading_directories(logfile) < 0)
return error("unable to create directory for %s",
logfile);
if (safe_create_leading_directories(logfile) < 0) {
int save_errno = errno;
error("unable to create directory for %s", logfile);
errno = save_errno;
return -1;
}
oflags |= O_CREAT;
}
@ -2963,15 +3003,22 @@ int log_ref_setup(const char *refname, char *logfile, int bufsize)
if ((oflags & O_CREAT) && errno == EISDIR) {
if (remove_empty_directories(logfile)) {
return error("There are still logs under '%s'",
logfile);
int save_errno = errno;
error("There are still logs under '%s'",
logfile);
errno = save_errno;
return -1;
}
logfd = open(logfile, oflags, 0666);
}
if (logfd < 0)
return error("Unable to append to %s: %s",
logfile, strerror(errno));
if (logfd < 0) {
int save_errno = errno;
error("Unable to append to %s: %s", logfile,
strerror(errno));
errno = save_errno;
return -1;
}
}
adjust_shared_perm(logfile);
@ -3011,8 +3058,19 @@ static int log_ref_write(const char *refname, const unsigned char *old_sha1,
len += copy_msg(logrec + len - 1, msg) - 1;
written = len <= maxlen ? write_in_full(logfd, logrec, len) : -1;
free(logrec);
if (close(logfd) != 0 || written != len)
return error("Unable to append to %s", log_file);
if (written != len) {
int save_errno = errno;
close(logfd);
error("Unable to append to %s", log_file);
errno = save_errno;
return -1;
}
if (close(logfd)) {
int save_errno = errno;
error("Unable to append to %s", log_file);
errno = save_errno;
return -1;
}
return 0;
}
@ -3021,14 +3079,17 @@ static int is_branch(const char *refname)
return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
}
/* This function must return a meaningful errno */
int write_ref_sha1(struct ref_lock *lock,
const unsigned char *sha1, const char *logmsg)
{
static char term = '\n';
struct object *o;
if (!lock)
if (!lock) {
errno = EINVAL;
return -1;
}
if (!lock->force_write && !hashcmp(lock->old_sha1, sha1)) {
unlock_ref(lock);
return 0;
@ -3038,19 +3099,23 @@ int write_ref_sha1(struct ref_lock *lock,
error("Trying to write ref %s with nonexistent object %s",
lock->ref_name, sha1_to_hex(sha1));
unlock_ref(lock);
errno = EINVAL;
return -1;
}
if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
error("Trying to write non-commit object %s to branch %s",
sha1_to_hex(sha1), lock->ref_name);
unlock_ref(lock);
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
|| close_ref(lock) < 0) {
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);
unlock_ref(lock);
errno = save_errno;
return -1;
}
clear_loose_ref_cache(&ref_cache);
@ -3487,10 +3552,13 @@ static struct ref_lock *update_ref_lock(const char *refname,
static int update_ref_write(const char *action, const char *refname,
const unsigned char *sha1, struct ref_lock *lock,
enum action_on_err onerr)
struct strbuf *err, enum action_on_err onerr)
{
if (write_ref_sha1(lock, sha1, action) < 0) {
const char *str = "Cannot update the ref '%s'.";
if (err)
strbuf_addf(err, str, refname);
switch (onerr) {
case UPDATE_REFS_MSG_ON_ERR: error(str, refname); break;
case UPDATE_REFS_DIE_ON_ERR: die(str, refname); break;
@ -3533,10 +3601,13 @@ struct ref_transaction *ref_transaction_begin(void)
return xcalloc(1, sizeof(struct ref_transaction));
}
static void ref_transaction_free(struct ref_transaction *transaction)
void ref_transaction_free(struct ref_transaction *transaction)
{
int i;
if (!transaction)
return;
for (i = 0; i < transaction->nr; i++)
free(transaction->updates[i]);
@ -3544,11 +3615,6 @@ static void ref_transaction_free(struct ref_transaction *transaction)
free(transaction);
}
void ref_transaction_rollback(struct ref_transaction *transaction)
{
ref_transaction_free(transaction);
}
static struct ref_update *add_update(struct ref_transaction *transaction,
const char *refname)
{
@ -3561,23 +3627,30 @@ static struct ref_update *add_update(struct ref_transaction *transaction,
return update;
}
void ref_transaction_update(struct ref_transaction *transaction,
const char *refname,
unsigned char *new_sha1, unsigned char *old_sha1,
int flags, int have_old)
int ref_transaction_update(struct ref_transaction *transaction,
const char *refname,
const unsigned char *new_sha1,
const unsigned char *old_sha1,
int flags, int have_old,
struct strbuf *err)
{
struct ref_update *update = add_update(transaction, refname);
struct ref_update *update;
if (have_old && !old_sha1)
die("BUG: have_old is true but old_sha1 is NULL");
update = add_update(transaction, refname);
hashcpy(update->new_sha1, new_sha1);
update->flags = flags;
update->have_old = have_old;
if (have_old)
hashcpy(update->old_sha1, old_sha1);
return 0;
}
void ref_transaction_create(struct ref_transaction *transaction,
const char *refname,
unsigned char *new_sha1,
const unsigned char *new_sha1,
int flags)
{
struct ref_update *update = add_update(transaction, refname);
@ -3591,7 +3664,7 @@ void ref_transaction_create(struct ref_transaction *transaction,
void ref_transaction_delete(struct ref_transaction *transaction,
const char *refname,
unsigned char *old_sha1,
const unsigned char *old_sha1,
int flags, int have_old)
{
struct ref_update *update = add_update(transaction, refname);
@ -3612,7 +3685,7 @@ int update_ref(const char *action, const char *refname,
lock = update_ref_lock(refname, oldval, flags, NULL, onerr);
if (!lock)
return 1;
return update_ref_write(action, refname, sha1, lock, onerr);
return update_ref_write(action, refname, sha1, lock, NULL, onerr);
}
static int ref_update_compare(const void *r1, const void *r2)
@ -3623,28 +3696,23 @@ static int ref_update_compare(const void *r1, const void *r2)
}
static int ref_update_reject_duplicates(struct ref_update **updates, int n,
enum action_on_err onerr)
struct strbuf *err)
{
int i;
for (i = 1; i < n; i++)
if (!strcmp(updates[i - 1]->refname, updates[i]->refname)) {
const char *str =
"Multiple updates for ref '%s' not allowed.";
switch (onerr) {
case UPDATE_REFS_MSG_ON_ERR:
error(str, updates[i]->refname); break;
case UPDATE_REFS_DIE_ON_ERR:
die(str, updates[i]->refname); break;
case UPDATE_REFS_QUIET_ON_ERR:
break;
}
if (err)
strbuf_addf(err, str, updates[i]->refname);
return 1;
}
return 0;
}
int ref_transaction_commit(struct ref_transaction *transaction,
const char *msg, enum action_on_err onerr)
const char *msg, struct strbuf *err)
{
int ret = 0, delnum = 0, i;
const char **delnames;
@ -3659,7 +3727,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
/* Copy, sort, and reject duplicate refs */
qsort(updates, n, sizeof(*updates), ref_update_compare);
ret = ref_update_reject_duplicates(updates, n, onerr);
ret = ref_update_reject_duplicates(updates, n, err);
if (ret)
goto cleanup;
@ -3671,8 +3739,12 @@ int ref_transaction_commit(struct ref_transaction *transaction,
(update->have_old ?
update->old_sha1 : NULL),
update->flags,
&update->type, onerr);
&update->type,
UPDATE_REFS_QUIET_ON_ERR);
if (!update->lock) {
if (err)
strbuf_addf(err, "Cannot lock the ref '%s'.",
update->refname);
ret = 1;
goto cleanup;
}
@ -3686,7 +3758,8 @@ int ref_transaction_commit(struct ref_transaction *transaction,
ret = update_ref_write(msg,
update->refname,
update->new_sha1,
update->lock, onerr);
update->lock, err,
UPDATE_REFS_QUIET_ON_ERR);
update->lock = NULL; /* freed by update_ref_write */
if (ret)
goto cleanup;
@ -3703,7 +3776,7 @@ int ref_transaction_commit(struct ref_transaction *transaction,
}
}
ret |= repack_without_refs(delnames, delnum);
ret |= repack_without_refs(delnames, delnum, err);
for (i = 0; i < delnum; i++)
unlink_or_warn(git_path("logs/%s", delnames[i]));
clear_loose_ref_cache(&ref_cache);
@ -3713,7 +3786,6 @@ cleanup:
if (updates[i]->lock)
unlock_ref(updates[i]->lock);
free(delnames);
ref_transaction_free(transaction);
return ret;
}