зеркало из https://github.com/microsoft/git.git
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:
Коммит
19a249ba83
|
@ -754,7 +754,7 @@ static int remove_branches(struct string_list *branches)
|
|||
branch_names = xmalloc(branches->nr * sizeof(*branch_names));
|
||||
for (i = 0; i < branches->nr; i++)
|
||||
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);
|
||||
|
||||
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++)
|
||||
delete_refs[i] = states.stale.items[i].util;
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ static struct ref_transaction *transaction;
|
|||
|
||||
static char line_termination = '\n';
|
||||
static int update_flags;
|
||||
static struct strbuf err = STRBUF_INIT;
|
||||
|
||||
/*
|
||||
* 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)
|
||||
die("update %s: extra input: %s", refname, next);
|
||||
|
||||
ref_transaction_update(transaction, refname, new_sha1, old_sha1,
|
||||
update_flags, have_old);
|
||||
if (ref_transaction_update(transaction, refname, new_sha1, old_sha1,
|
||||
update_flags, have_old, &err))
|
||||
die("%s", err.buf);
|
||||
|
||||
update_flags = 0;
|
||||
free(refname);
|
||||
|
@ -286,8 +288,9 @@ static const char *parse_cmd_verify(struct strbuf *input, const char *next)
|
|||
if (*next != line_termination)
|
||||
die("verify %s: extra input: %s", refname, next);
|
||||
|
||||
ref_transaction_update(transaction, refname, new_sha1, old_sha1,
|
||||
update_flags, have_old);
|
||||
if (ref_transaction_update(transaction, refname, new_sha1, old_sha1,
|
||||
update_flags, have_old, &err))
|
||||
die("%s", err.buf);
|
||||
|
||||
update_flags = 0;
|
||||
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.");
|
||||
|
||||
if (read_stdin) {
|
||||
int ret;
|
||||
transaction = ref_transaction_begin();
|
||||
|
||||
if (delete || no_deref || argc > 0)
|
||||
usage_with_options(git_update_ref_usage, options);
|
||||
if (end_null)
|
||||
line_termination = '\0';
|
||||
update_refs_stdin();
|
||||
ret = ref_transaction_commit(transaction, msg,
|
||||
UPDATE_REFS_DIE_ON_ERR);
|
||||
return ret;
|
||||
if (ref_transaction_commit(transaction, msg, &err))
|
||||
die("%s", err.buf);
|
||||
ref_transaction_free(transaction);
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (end_null)
|
||||
|
|
4
cache.h
4
cache.h
|
@ -578,6 +578,8 @@ struct lock_file {
|
|||
#define LOCK_DIE_ON_ERROR 1
|
||||
#define LOCK_NODEREF 2
|
||||
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 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);
|
||||
|
@ -995,7 +997,7 @@ extern int read_ref(const char *refname, unsigned char *sha1);
|
|||
* NULL. If more than MAXDEPTH recursive symbolic lookups are needed,
|
||||
* 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 char *resolve_refdup(const char *ref, unsigned char *sha1, int reading, int *flag);
|
||||
|
|
39
lockfile.c
39
lockfile.c
|
@ -120,7 +120,7 @@ static char *resolve_symlink(char *p, size_t s)
|
|||
return p;
|
||||
}
|
||||
|
||||
|
||||
/* Make sure errno contains a meaningful value on error */
|
||||
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;
|
||||
|
||||
if (strlen(path) >= max_path_len)
|
||||
if (strlen(path) >= max_path_len) {
|
||||
errno = ENAMETOOLONG;
|
||||
return -1;
|
||||
}
|
||||
strcpy(lk->filename, path);
|
||||
if (!(flags & LOCK_NODEREF))
|
||||
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;
|
||||
lk->on_list = 1;
|
||||
}
|
||||
if (adjust_shared_perm(lk->filename))
|
||||
return error("cannot fix permission bits on %s",
|
||||
lk->filename);
|
||||
if (adjust_shared_perm(lk->filename)) {
|
||||
int save_errno = errno;
|
||||
error("cannot fix permission bits on %s",
|
||||
lk->filename);
|
||||
errno = save_errno;
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
else
|
||||
lk->filename[0] = 0;
|
||||
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) {
|
||||
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"
|
||||
"git process crashed in this repository earlier. Make sure no other git\n"
|
||||
"process is running and remove the file manually to continue.",
|
||||
absolute_path(path), strerror(err));
|
||||
} else
|
||||
strbuf_addf(&buf, "Unable to create '%s.lock': %s",
|
||||
strbuf_addf(buf, "Unable to create '%s.lock': %s",
|
||||
absolute_path(path), strerror(err));
|
||||
return strbuf_detach(&buf, NULL);
|
||||
}
|
||||
|
||||
int unable_to_lock_error(const char *path, int err)
|
||||
{
|
||||
char *msg = unable_to_lock_message(path, err);
|
||||
error("%s", msg);
|
||||
free(msg);
|
||||
struct strbuf buf = STRBUF_INIT;
|
||||
|
||||
unable_to_lock_message(path, err, &buf);
|
||||
error("%s", buf.buf);
|
||||
strbuf_release(&buf);
|
||||
return -1;
|
||||
}
|
||||
|
||||
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 fd = lock_file(lk, path, flags);
|
||||
|
|
178
refs.c
178
refs.c
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
53
refs.h
53
refs.h
|
@ -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
|
||||
* hold_lock_file_for_update(). Return 0 on success.
|
||||
* Errno is set to something meaningful on error.
|
||||
*/
|
||||
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
|
||||
* disk. The packed-refs file must already be locked for writing (see
|
||||
* lock_packed_refs()). Return zero on success.
|
||||
* Sets errno to something meaningful on error.
|
||||
*/
|
||||
extern int commit_packed_refs(void);
|
||||
|
||||
|
@ -121,7 +123,8 @@ extern void rollback_packed_refs(void);
|
|||
*/
|
||||
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 *);
|
||||
|
||||
|
@ -135,11 +138,15 @@ extern int ref_exists(const char *);
|
|||
*/
|
||||
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);
|
||||
|
||||
/** Locks any ref (for 'HEAD' type refs). */
|
||||
#define REF_NODEREF 0x01
|
||||
/* errno is set to something meaningful on failure */
|
||||
extern struct ref_lock *lock_any_ref_for_update(const char *refname,
|
||||
const unsigned char *old_sha1,
|
||||
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. **/
|
||||
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);
|
||||
|
||||
/** 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
|
||||
* eventually be commited using ref_transaction_commit() or rolled
|
||||
* back using ref_transaction_rollback().
|
||||
* be freed by calling ref_transaction_free().
|
||||
*/
|
||||
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
|
||||
* 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().
|
||||
*/
|
||||
|
||||
|
||||
/*
|
||||
* Add a reference update to transaction. new_sha1 is the value that
|
||||
* 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
|
||||
* that the reference should have had before the update, or zeros if
|
||||
* 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,
|
||||
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);
|
||||
|
||||
/*
|
||||
* 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,
|
||||
const char *refname,
|
||||
unsigned char *new_sha1,
|
||||
const unsigned char *new_sha1,
|
||||
int flags);
|
||||
|
||||
/*
|
||||
|
@ -269,16 +275,23 @@ 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);
|
||||
|
||||
/*
|
||||
* Commit all of the changes that have been queued in transaction, as
|
||||
* 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,
|
||||
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 */
|
||||
int update_ref(const char *action, const char *refname,
|
||||
|
|
Загрузка…
Ссылка в новой задаче