1
0
Fork 0
mirror of https://github.com/git/git.git synced 2024-06-10 22:46:21 +02:00

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
commit 19a249ba83
6 changed files with 199 additions and 100 deletions

View File

@ -754,7 +754,7 @@ static int remove_branches(struct string_list *branches)
branch_names = xmalloc(branches->nr * sizeof(*branch_names)); branch_names = xmalloc(branches->nr * sizeof(*branch_names));
for (i = 0; i < branches->nr; i++) for (i = 0; i < branches->nr; i++)
branch_names[i] = branches->items[i].string; branch_names[i] = branches->items[i].string;
result |= repack_without_refs(branch_names, branches->nr); result |= repack_without_refs(branch_names, branches->nr, NULL);
free(branch_names); free(branch_names);
for (i = 0; i < branches->nr; i++) { for (i = 0; i < branches->nr; i++) {
@ -1332,7 +1332,8 @@ static int prune_remote(const char *remote, int dry_run)
for (i = 0; i < states.stale.nr; i++) for (i = 0; i < states.stale.nr; i++)
delete_refs[i] = states.stale.items[i].util; delete_refs[i] = states.stale.items[i].util;
if (!dry_run) if (!dry_run)
result |= repack_without_refs(delete_refs, states.stale.nr); result |= repack_without_refs(delete_refs,
states.stale.nr, NULL);
free(delete_refs); free(delete_refs);
} }

View File

@ -16,6 +16,7 @@ static struct ref_transaction *transaction;
static char line_termination = '\n'; static char line_termination = '\n';
static int update_flags; static int update_flags;
static struct strbuf err = STRBUF_INIT;
/* /*
* Parse one whitespace- or NUL-terminated, possibly C-quoted argument * Parse one whitespace- or NUL-terminated, possibly C-quoted argument
@ -197,8 +198,9 @@ static const char *parse_cmd_update(struct strbuf *input, const char *next)
if (*next != line_termination) if (*next != line_termination)
die("update %s: extra input: %s", refname, next); die("update %s: extra input: %s", refname, next);
ref_transaction_update(transaction, refname, new_sha1, old_sha1, if (ref_transaction_update(transaction, refname, new_sha1, old_sha1,
update_flags, have_old); update_flags, have_old, &err))
die("%s", err.buf);
update_flags = 0; update_flags = 0;
free(refname); free(refname);
@ -286,8 +288,9 @@ static const char *parse_cmd_verify(struct strbuf *input, const char *next)
if (*next != line_termination) if (*next != line_termination)
die("verify %s: extra input: %s", refname, next); die("verify %s: extra input: %s", refname, next);
ref_transaction_update(transaction, refname, new_sha1, old_sha1, if (ref_transaction_update(transaction, refname, new_sha1, old_sha1,
update_flags, have_old); update_flags, have_old, &err))
die("%s", err.buf);
update_flags = 0; update_flags = 0;
free(refname); free(refname);
@ -359,17 +362,16 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix)
die("Refusing to perform update with empty message."); die("Refusing to perform update with empty message.");
if (read_stdin) { if (read_stdin) {
int ret;
transaction = ref_transaction_begin(); transaction = ref_transaction_begin();
if (delete || no_deref || argc > 0) if (delete || no_deref || argc > 0)
usage_with_options(git_update_ref_usage, options); usage_with_options(git_update_ref_usage, options);
if (end_null) if (end_null)
line_termination = '\0'; line_termination = '\0';
update_refs_stdin(); update_refs_stdin();
ret = ref_transaction_commit(transaction, msg, if (ref_transaction_commit(transaction, msg, &err))
UPDATE_REFS_DIE_ON_ERR); die("%s", err.buf);
return ret; ref_transaction_free(transaction);
return 0;
} }
if (end_null) if (end_null)

View File

@ -578,6 +578,8 @@ struct lock_file {
#define LOCK_DIE_ON_ERROR 1 #define LOCK_DIE_ON_ERROR 1
#define LOCK_NODEREF 2 #define LOCK_NODEREF 2
extern int unable_to_lock_error(const char *path, int err); extern int unable_to_lock_error(const char *path, int err);
extern void unable_to_lock_message(const char *path, int err,
struct strbuf *buf);
extern NORETURN void unable_to_lock_index_die(const char *path, int err); extern NORETURN void unable_to_lock_index_die(const char *path, int err);
extern int hold_lock_file_for_update(struct lock_file *, const char *path, int); extern int hold_lock_file_for_update(struct lock_file *, const char *path, int);
extern int hold_lock_file_for_append(struct lock_file *, const char *path, int); extern int hold_lock_file_for_append(struct lock_file *, const char *path, int);
@ -995,7 +997,7 @@ extern int read_ref(const char *refname, unsigned char *sha1);
* NULL. If more than MAXDEPTH recursive symbolic lookups are needed, * NULL. If more than MAXDEPTH recursive symbolic lookups are needed,
* give up and return NULL. * give up and return NULL.
* *
* errno is sometimes set on errors, but not always. * errno is set to something meaningful on error.
*/ */
extern const char *resolve_ref_unsafe(const char *ref, unsigned char *sha1, int reading, int *flag); extern const char *resolve_ref_unsafe(const char *ref, unsigned char *sha1, int reading, int *flag);
extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag); extern char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);

View File

@ -120,7 +120,7 @@ static char *resolve_symlink(char *p, size_t s)
return p; return p;
} }
/* Make sure errno contains a meaningful value on error */
static int lock_file(struct lock_file *lk, const char *path, int flags) static int lock_file(struct lock_file *lk, const char *path, int flags)
{ {
/* /*
@ -129,8 +129,10 @@ static int lock_file(struct lock_file *lk, const char *path, int flags)
*/ */
static const size_t max_path_len = sizeof(lk->filename) - 5; static const size_t max_path_len = sizeof(lk->filename) - 5;
if (strlen(path) >= max_path_len) if (strlen(path) >= max_path_len) {
errno = ENAMETOOLONG;
return -1; return -1;
}
strcpy(lk->filename, path); strcpy(lk->filename, path);
if (!(flags & LOCK_NODEREF)) if (!(flags & LOCK_NODEREF))
resolve_symlink(lk->filename, max_path_len); resolve_symlink(lk->filename, max_path_len);
@ -147,44 +149,51 @@ static int lock_file(struct lock_file *lk, const char *path, int flags)
lock_file_list = lk; lock_file_list = lk;
lk->on_list = 1; lk->on_list = 1;
} }
if (adjust_shared_perm(lk->filename)) if (adjust_shared_perm(lk->filename)) {
return error("cannot fix permission bits on %s", int save_errno = errno;
lk->filename); error("cannot fix permission bits on %s",
lk->filename);
errno = save_errno;
return -1;
}
} }
else else
lk->filename[0] = 0; lk->filename[0] = 0;
return lk->fd; return lk->fd;
} }
static char *unable_to_lock_message(const char *path, int err) void unable_to_lock_message(const char *path, int err, struct strbuf *buf)
{ {
struct strbuf buf = STRBUF_INIT;
if (err == EEXIST) { if (err == EEXIST) {
strbuf_addf(&buf, "Unable to create '%s.lock': %s.\n\n" strbuf_addf(buf, "Unable to create '%s.lock': %s.\n\n"
"If no other git process is currently running, this probably means a\n" "If no other git process is currently running, this probably means a\n"
"git process crashed in this repository earlier. Make sure no other git\n" "git process crashed in this repository earlier. Make sure no other git\n"
"process is running and remove the file manually to continue.", "process is running and remove the file manually to continue.",
absolute_path(path), strerror(err)); absolute_path(path), strerror(err));
} else } else
strbuf_addf(&buf, "Unable to create '%s.lock': %s", strbuf_addf(buf, "Unable to create '%s.lock': %s",
absolute_path(path), strerror(err)); absolute_path(path), strerror(err));
return strbuf_detach(&buf, NULL);
} }
int unable_to_lock_error(const char *path, int err) int unable_to_lock_error(const char *path, int err)
{ {
char *msg = unable_to_lock_message(path, err); struct strbuf buf = STRBUF_INIT;
error("%s", msg);
free(msg); unable_to_lock_message(path, err, &buf);
error("%s", buf.buf);
strbuf_release(&buf);
return -1; return -1;
} }
NORETURN void unable_to_lock_index_die(const char *path, int err) NORETURN void unable_to_lock_index_die(const char *path, int err)
{ {
die("%s", unable_to_lock_message(path, err)); struct strbuf buf = STRBUF_INIT;
unable_to_lock_message(path, err, &buf);
die("%s", buf.buf);
} }
/* This should return a meaningful errno on failure */
int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags) int hold_lock_file_for_update(struct lock_file *lk, const char *path, int flags)
{ {
int fd = lock_file(lk, path, flags); int fd = lock_file(lk, path, flags);

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

53
refs.h
View File

@ -82,6 +82,7 @@ extern void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct st
/* /*
* Lock the packed-refs file for writing. Flags is passed to * Lock the packed-refs file for writing. Flags is passed to
* hold_lock_file_for_update(). Return 0 on success. * hold_lock_file_for_update(). Return 0 on success.
* Errno is set to something meaningful on error.
*/ */
extern int lock_packed_refs(int flags); extern int lock_packed_refs(int flags);
@ -97,6 +98,7 @@ extern void add_packed_ref(const char *refname, const unsigned char *sha1);
* Write the current version of the packed refs cache from memory to * Write the current version of the packed refs cache from memory to
* disk. The packed-refs file must already be locked for writing (see * disk. The packed-refs file must already be locked for writing (see
* lock_packed_refs()). Return zero on success. * lock_packed_refs()). Return zero on success.
* Sets errno to something meaningful on error.
*/ */
extern int commit_packed_refs(void); extern int commit_packed_refs(void);
@ -121,7 +123,8 @@ extern void rollback_packed_refs(void);
*/ */
int pack_refs(unsigned int flags); int pack_refs(unsigned int flags);
extern int repack_without_refs(const char **refnames, int n); extern int repack_without_refs(const char **refnames, int n,
struct strbuf *err);
extern int ref_exists(const char *); extern int ref_exists(const char *);
@ -135,11 +138,15 @@ extern int ref_exists(const char *);
*/ */
extern int peel_ref(const char *refname, unsigned char *sha1); extern int peel_ref(const char *refname, unsigned char *sha1);
/** Locks a "refs/" ref returning the lock on success and NULL on failure. **/ /*
* Locks a "refs/" ref returning the lock on success and NULL on failure.
* On failure errno is set to something meaningful.
*/
extern struct ref_lock *lock_ref_sha1(const char *refname, const unsigned char *old_sha1); extern struct ref_lock *lock_ref_sha1(const char *refname, const unsigned char *old_sha1);
/** Locks any ref (for 'HEAD' type refs). */ /** Locks any ref (for 'HEAD' type refs). */
#define REF_NODEREF 0x01 #define REF_NODEREF 0x01
/* errno is set to something meaningful on failure */
extern struct ref_lock *lock_any_ref_for_update(const char *refname, extern struct ref_lock *lock_any_ref_for_update(const char *refname,
const unsigned char *old_sha1, const unsigned char *old_sha1,
int flags, int *type_p); int flags, int *type_p);
@ -156,7 +163,9 @@ extern void unlock_ref(struct ref_lock *lock);
/** Writes sha1 into the ref specified by the lock. **/ /** Writes sha1 into the ref specified by the lock. **/
extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg); extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg);
/** Setup reflog before using. **/ /*
* Setup reflog before using. Set errno to something meaningful on failure.
*/
int log_ref_setup(const char *refname, char *logfile, int bufsize); int log_ref_setup(const char *refname, char *logfile, int bufsize);
/** Reads log for the value of ref during at_time. **/ /** Reads log for the value of ref during at_time. **/
@ -219,17 +228,10 @@ enum action_on_err {
/* /*
* Begin a reference transaction. The reference transaction must * Begin a reference transaction. The reference transaction must
* eventually be commited using ref_transaction_commit() or rolled * be freed by calling ref_transaction_free().
* back using ref_transaction_rollback().
*/ */
struct ref_transaction *ref_transaction_begin(void); struct ref_transaction *ref_transaction_begin(void);
/*
* Roll back a ref_transaction and free all associated data.
*/
void ref_transaction_rollback(struct ref_transaction *transaction);
/* /*
* The following functions add a reference check or update to a * The following functions add a reference check or update to a
* ref_transaction. In all of them, refname is the name of the * ref_transaction. In all of them, refname is the name of the
@ -238,18 +240,22 @@ void ref_transaction_rollback(struct ref_transaction *transaction);
* can be REF_NODEREF; it is passed to update_ref_lock(). * can be REF_NODEREF; it is passed to update_ref_lock().
*/ */
/* /*
* Add a reference update to transaction. new_sha1 is the value that * Add a reference update to transaction. new_sha1 is the value that
* the reference should have after the update, or zeros if it should * the reference should have after the update, or zeros if it should
* be deleted. If have_old is true, then old_sha1 holds the value * be deleted. If have_old is true, then old_sha1 holds the value
* that the reference should have had before the update, or zeros if * that the reference should have had before the update, or zeros if
* it must not have existed beforehand. * it must not have existed beforehand.
* Function returns 0 on success and non-zero on failure. A failure to update
* means that the transaction as a whole has failed and will need to be
* rolled back. On failure the err buffer will be updated.
*/ */
void ref_transaction_update(struct ref_transaction *transaction, int ref_transaction_update(struct ref_transaction *transaction,
const char *refname, const char *refname,
unsigned char *new_sha1, unsigned char *old_sha1, const unsigned char *new_sha1,
int flags, int have_old); const unsigned char *old_sha1,
int flags, int have_old,
struct strbuf *err);
/* /*
* Add a reference creation to transaction. new_sha1 is the value * Add a reference creation to transaction. new_sha1 is the value
@ -259,7 +265,7 @@ void ref_transaction_update(struct ref_transaction *transaction,
*/ */
void ref_transaction_create(struct ref_transaction *transaction, void ref_transaction_create(struct ref_transaction *transaction,
const char *refname, const char *refname,
unsigned char *new_sha1, const unsigned char *new_sha1,
int flags); int flags);
/* /*
@ -269,16 +275,23 @@ void ref_transaction_create(struct ref_transaction *transaction,
*/ */
void ref_transaction_delete(struct ref_transaction *transaction, void ref_transaction_delete(struct ref_transaction *transaction,
const char *refname, const char *refname,
unsigned char *old_sha1, const unsigned char *old_sha1,
int flags, int have_old); int flags, int have_old);
/* /*
* Commit all of the changes that have been queued in transaction, as * Commit all of the changes that have been queued in transaction, as
* atomically as possible. Return a nonzero value if there is a * atomically as possible. Return a nonzero value if there is a
* problem. The ref_transaction is freed by this function. * problem.
* If err is non-NULL we will add an error string to it to explain why
* the transaction failed. The string does not end in newline.
*/ */
int ref_transaction_commit(struct ref_transaction *transaction, int ref_transaction_commit(struct ref_transaction *transaction,
const char *msg, enum action_on_err onerr); const char *msg, struct strbuf *err);
/*
* Free an existing transaction and all associated data.
*/
void ref_transaction_free(struct ref_transaction *transaction);
/** Lock a ref and then write its file */ /** Lock a ref and then write its file */
int update_ref(const char *action, const char *refname, int update_ref(const char *action, const char *refname,