summaryrefslogtreecommitdiff
path: root/refs.c
diff options
context:
space:
mode:
Diffstat (limited to 'refs.c')
-rw-r--r--refs.c402
1 files changed, 307 insertions, 95 deletions
diff --git a/refs.c b/refs.c
index 68f2cb03c4..5f9831e66b 100644
--- a/refs.c
+++ b/refs.c
@@ -19,12 +19,14 @@ struct ref_lock {
* 1: End-of-component
* 2: ., look for a preceding . to reject .. in refs
* 3: {, look for a preceding @ to reject @{ in refs
- * 4: A bad character: ASCII control characters, "~", "^", ":" or SP
+ * 4: A bad character: ASCII control characters, and
+ * ":", "?", "[", "\", "^", "~", SP, or TAB
+ * 5: *, reject unless REFNAME_REFSPEC_PATTERN is set
*/
static unsigned char refname_disposition[256] = {
1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4,
- 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 2, 1,
+ 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 5, 0, 0, 0, 2, 1,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 4,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4, 0, 4, 0,
@@ -63,6 +65,11 @@ static unsigned char refname_disposition[256] = {
#define REF_NEEDS_COMMIT 0x20
/*
+ * 0x40 is REF_FORCE_CREATE_REFLOG, so skip it if you're adding a
+ * value to ref_update::flags
+ */
+
+/*
* Try to read one refname component from the front of refname.
* Return the length of the component found, or -1 if the component is
* not legal. It is legal if it is something reasonable to have under
@@ -70,12 +77,14 @@ static unsigned char refname_disposition[256] = {
*
* - any path component of it begins with ".", or
* - it has double dots "..", or
- * - it has ASCII control character, "~", "^", ":" or SP, anywhere, or
- * - it ends with a "/".
- * - it ends with ".lock"
- * - it contains a "\" (backslash)
+ * - it has ASCII control characters, or
+ * - it has ":", "?", "[", "\", "^", "~", SP, or TAB anywhere, or
+ * - it has "*" anywhere unless REFNAME_REFSPEC_PATTERN is set, or
+ * - it ends with a "/", or
+ * - it ends with ".lock", or
+ * - it contains a "@{" portion
*/
-static int check_refname_component(const char *refname, int flags)
+static int check_refname_component(const char *refname, int *flags)
{
const char *cp;
char last = '\0';
@@ -96,6 +105,16 @@ static int check_refname_component(const char *refname, int flags)
break;
case 4:
return -1;
+ case 5:
+ if (!(*flags & REFNAME_REFSPEC_PATTERN))
+ return -1; /* refspec can't be a pattern */
+
+ /*
+ * Unset the pattern flag so that we only accept
+ * a single asterisk for one side of refspec.
+ */
+ *flags &= ~ REFNAME_REFSPEC_PATTERN;
+ break;
}
last = ch;
}
@@ -120,18 +139,10 @@ int check_refname_format(const char *refname, int flags)
while (1) {
/* We are at the start of a path component. */
- component_len = check_refname_component(refname, flags);
- if (component_len <= 0) {
- if ((flags & REFNAME_REFSPEC_PATTERN) &&
- refname[0] == '*' &&
- (refname[1] == '\0' || refname[1] == '/')) {
- /* Accept one wildcard as a full refname component. */
- flags &= ~REFNAME_REFSPEC_PATTERN;
- component_len = 1;
- } else {
- return -1;
- }
- }
+ component_len = check_refname_component(refname, &flags);
+ if (component_len <= 0)
+ return -1;
+
component_count++;
if (refname[component_len] == '\0')
break;
@@ -1314,7 +1325,13 @@ static struct ref_dir *get_packed_refs(struct ref_cache *refs)
return get_packed_ref_dir(get_packed_ref_cache(refs));
}
-void add_packed_ref(const char *refname, const unsigned char *sha1)
+/*
+ * Add a reference to the in-memory packed reference cache. This may
+ * only be called while the packed-refs file is locked (see
+ * lock_packed_refs()). To actually write the packed-refs file, call
+ * commit_packed_refs().
+ */
+static void add_packed_ref(const char *refname, const unsigned char *sha1)
{
struct packed_ref_cache *packed_ref_cache =
get_packed_ref_cache(&ref_cache);
@@ -1741,9 +1758,11 @@ const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
return ret;
}
-char *resolve_refdup(const char *ref, int resolve_flags, unsigned char *sha1, int *flags)
+char *resolve_refdup(const char *refname, int resolve_flags,
+ unsigned char *sha1, int *flags)
{
- return xstrdup_or_null(resolve_ref_unsafe(ref, resolve_flags, sha1, flags));
+ return xstrdup_or_null(resolve_ref_unsafe(refname, resolve_flags,
+ sha1, flags));
}
/* The argument to filter_refs */
@@ -2122,7 +2141,8 @@ int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *c
int for_each_replace_ref(each_ref_fn fn, void *cb_data)
{
- return do_for_each_ref(&ref_cache, "refs/replace/", fn, 13, 0, cb_data);
+ return do_for_each_ref(&ref_cache, git_replace_ref_base, fn,
+ strlen(git_replace_ref_base), 0, cb_data);
}
int head_ref_namespaced(each_ref_fn fn, void *cb_data)
@@ -2530,8 +2550,12 @@ 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)
+/*
+ * Lock the packed-refs file for writing. Flags is passed to
+ * hold_lock_file_for_update(). Return 0 on success. On errors, set
+ * errno appropriately and return a nonzero value.
+ */
+static int lock_packed_refs(int flags)
{
static int timeout_configured = 0;
static int timeout_value = 1000;
@@ -2561,10 +2585,12 @@ int lock_packed_refs(int flags)
}
/*
- * Commit the packed refs changes.
- * On error we must make sure that errno contains a meaningful value.
+ * 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. On errors, set errno
+ * and return a nonzero value
*/
-int commit_packed_refs(void)
+static int commit_packed_refs(void)
{
struct packed_ref_cache *packed_ref_cache =
get_packed_ref_cache(&ref_cache);
@@ -2593,7 +2619,12 @@ int commit_packed_refs(void)
return error;
}
-void rollback_packed_refs(void)
+/*
+ * Rollback the lockfile for the packed-refs file, and discard the
+ * in-memory packed reference cache. (The packed-refs file will be
+ * read anew if it is needed again after this function is called.)
+ */
+static void rollback_packed_refs(void)
{
struct packed_ref_cache *packed_ref_cache =
get_packed_ref_cache(&ref_cache);
@@ -2751,7 +2782,14 @@ int pack_refs(unsigned int flags)
return 0;
}
-int repack_without_refs(struct string_list *refnames, struct strbuf *err)
+/*
+ * Rewrite the packed-refs file, omitting any refs listed in
+ * 'refnames'. On error, leave packed-refs unchanged, write an error
+ * message to 'err', and return a nonzero value.
+ *
+ * The refs in 'refnames' needn't be sorted. `err` must not be NULL.
+ */
+static int repack_without_refs(struct string_list *refnames, struct strbuf *err)
{
struct ref_dir *packed;
struct string_list_item *refname;
@@ -2816,15 +2854,15 @@ static int delete_ref_loose(struct ref_lock *lock, int flag, struct strbuf *err)
return 0;
}
-int delete_ref(const char *refname, const unsigned char *sha1, unsigned int flags)
+int delete_ref(const char *refname, const unsigned char *old_sha1,
+ unsigned int flags)
{
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
transaction = ref_transaction_begin(&err);
if (!transaction ||
- ref_transaction_delete(transaction, refname,
- (sha1 && !is_null_sha1(sha1)) ? sha1 : NULL,
+ ref_transaction_delete(transaction, refname, old_sha1,
flags, NULL, &err) ||
ref_transaction_commit(transaction, &err)) {
error("%s", err.buf);
@@ -2837,6 +2875,44 @@ int delete_ref(const char *refname, const unsigned char *sha1, unsigned int flag
return 0;
}
+int delete_refs(struct string_list *refnames)
+{
+ struct strbuf err = STRBUF_INIT;
+ int i, result = 0;
+
+ if (!refnames->nr)
+ return 0;
+
+ result = repack_without_refs(refnames, &err);
+ if (result) {
+ /*
+ * If we failed to rewrite the packed-refs file, then
+ * it is unsafe to try to remove loose refs, because
+ * doing so might expose an obsolete packed value for
+ * a reference that might even point at an object that
+ * has been garbage collected.
+ */
+ if (refnames->nr == 1)
+ error(_("could not delete reference %s: %s"),
+ refnames->items[0].string, err.buf);
+ else
+ error(_("could not delete references: %s"), err.buf);
+
+ goto out;
+ }
+
+ for (i = 0; i < refnames->nr; i++) {
+ const char *refname = refnames->items[i].string;
+
+ if (delete_ref(refname, NULL, 0))
+ result |= error(_("could not remove reference %s"), refname);
+ }
+
+out:
+ strbuf_release(&err);
+ return result;
+}
+
/*
* People using contrib's git-new-workdir have .git/logs/refs ->
* /some/other/path/.git/logs/refs, and that may live on another device.
@@ -2910,9 +2986,11 @@ static int rename_ref_available(const char *oldname, const char *newname)
return ret;
}
-static int write_ref_to_lockfile(struct ref_lock *lock, const unsigned char *sha1);
+static int write_ref_to_lockfile(struct ref_lock *lock,
+ const unsigned char *sha1, struct strbuf *err);
static int commit_ref_update(struct ref_lock *lock,
- const unsigned char *sha1, const char *logmsg);
+ const unsigned char *sha1, const char *logmsg,
+ int flags, struct strbuf *err);
int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
{
@@ -2973,9 +3051,10 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
}
hashcpy(lock->old_oid.hash, orig_sha1);
- if (write_ref_to_lockfile(lock, orig_sha1) ||
- commit_ref_update(lock, orig_sha1, logmsg)) {
- error("unable to write current sha1 into %s", newrefname);
+ if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
+ commit_ref_update(lock, orig_sha1, logmsg, 0, &err)) {
+ error("unable to write current sha1 into %s: %s", newrefname, err.buf);
+ strbuf_release(&err);
goto rollback;
}
@@ -2991,9 +3070,11 @@ int rename_ref(const char *oldrefname, const char *newrefname, const char *logms
flag = log_all_ref_updates;
log_all_ref_updates = 0;
- if (write_ref_to_lockfile(lock, orig_sha1) ||
- commit_ref_update(lock, orig_sha1, NULL))
- error("unable to write current sha1 into %s", oldrefname);
+ if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
+ commit_ref_update(lock, orig_sha1, NULL, 0, &err)) {
+ error("unable to write current sha1 into %s: %s", oldrefname, err.buf);
+ strbuf_release(&err);
+ }
log_all_ref_updates = flag;
rollbacklog:
@@ -3048,8 +3129,23 @@ 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, struct strbuf *sb_logfile)
+static int should_autocreate_reflog(const char *refname)
+{
+ if (!log_all_ref_updates)
+ return 0;
+ return starts_with(refname, "refs/heads/") ||
+ starts_with(refname, "refs/remotes/") ||
+ starts_with(refname, "refs/notes/") ||
+ !strcmp(refname, "HEAD");
+}
+
+/*
+ * Create a reflog for a ref. If force_create = 0, the reflog will
+ * only be created for certain refs (those for which
+ * should_autocreate_reflog returns non-zero. Otherwise, create it
+ * regardless of the ref name. Fill in *err and return -1 on failure.
+ */
+static int log_ref_setup(const char *refname, struct strbuf *sb_logfile, struct strbuf *err, int force_create)
{
int logfd, oflags = O_APPEND | O_WRONLY;
char *logfile;
@@ -3058,15 +3154,10 @@ int log_ref_setup(const char *refname, struct strbuf *sb_logfile)
logfile = sb_logfile->buf;
/* make sure the rest of the function can't change "logfile" */
sb_logfile = NULL;
- if (log_all_ref_updates &&
- (starts_with(refname, "refs/heads/") ||
- starts_with(refname, "refs/remotes/") ||
- starts_with(refname, "refs/notes/") ||
- !strcmp(refname, "HEAD"))) {
+ if (force_create || should_autocreate_reflog(refname)) {
if (safe_create_leading_directories(logfile) < 0) {
- int save_errno = errno;
- error("unable to create directory for %s", logfile);
- errno = save_errno;
+ strbuf_addf(err, "unable to create directory for %s: "
+ "%s", logfile, strerror(errno));
return -1;
}
oflags |= O_CREAT;
@@ -3079,20 +3170,16 @@ int log_ref_setup(const char *refname, struct strbuf *sb_logfile)
if (errno == EISDIR) {
if (remove_empty_directories(logfile)) {
- int save_errno = errno;
- error("There are still logs under '%s'",
- logfile);
- errno = save_errno;
+ strbuf_addf(err, "There are still logs under "
+ "'%s'", logfile);
return -1;
}
logfd = open(logfile, oflags, 0666);
}
if (logfd < 0) {
- int save_errno = errno;
- error("Unable to append to %s: %s", logfile,
- strerror(errno));
- errno = save_errno;
+ strbuf_addf(err, "unable to append to %s: %s",
+ logfile, strerror(errno));
return -1;
}
}
@@ -3102,6 +3189,17 @@ int log_ref_setup(const char *refname, struct strbuf *sb_logfile)
return 0;
}
+
+int safe_create_reflog(const char *refname, int force_create, struct strbuf *err)
+{
+ int ret;
+ struct strbuf sb = STRBUF_INIT;
+
+ ret = log_ref_setup(refname, &sb, err, force_create);
+ strbuf_release(&sb);
+ return ret;
+}
+
static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
const unsigned char *new_sha1,
const char *committer, const char *msg)
@@ -3130,7 +3228,8 @@ static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,
const unsigned char *new_sha1, const char *msg,
- struct strbuf *sb_log_file)
+ struct strbuf *sb_log_file, int flags,
+ struct strbuf *err)
{
int logfd, result, oflags = O_APPEND | O_WRONLY;
char *log_file;
@@ -3138,7 +3237,8 @@ static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,
if (log_all_ref_updates < 0)
log_all_ref_updates = !is_bare_repository();
- result = log_ref_setup(refname, sb_log_file);
+ result = log_ref_setup(refname, sb_log_file, err, flags & REF_FORCE_CREATE_REFLOG);
+
if (result)
return result;
log_file = sb_log_file->buf;
@@ -3151,26 +3251,26 @@ static int log_ref_write_1(const char *refname, const unsigned char *old_sha1,
result = log_ref_write_fd(logfd, old_sha1, new_sha1,
git_committer_info(0), msg);
if (result) {
- int save_errno = errno;
+ strbuf_addf(err, "unable to append to %s: %s", log_file,
+ strerror(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;
+ strbuf_addf(err, "unable to append to %s: %s", log_file,
+ strerror(errno));
return -1;
}
return 0;
}
static int log_ref_write(const char *refname, const unsigned char *old_sha1,
- const unsigned char *new_sha1, const char *msg)
+ const unsigned char *new_sha1, const char *msg,
+ int flags, struct strbuf *err)
{
struct strbuf sb = STRBUF_INIT;
- int ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb);
+ int ret = log_ref_write_1(refname, old_sha1, new_sha1, msg, &sb, flags,
+ err);
strbuf_release(&sb);
return ret;
}
@@ -3182,36 +3282,36 @@ int is_branch(const char *refname)
/*
* Write sha1 into the open lockfile, then close the lockfile. On
- * errors, rollback the lockfile and set errno to reflect the problem.
+ * errors, rollback the lockfile, fill in *err and
+ * return -1.
*/
static int write_ref_to_lockfile(struct ref_lock *lock,
- const unsigned char *sha1)
+ const unsigned char *sha1, struct strbuf *err)
{
static char term = '\n';
struct object *o;
o = parse_object(sha1);
if (!o) {
- error("Trying to write ref %s with nonexistent object %s",
- lock->ref_name, sha1_to_hex(sha1));
+ strbuf_addf(err,
+ "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);
+ strbuf_addf(err,
+ "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->lk->fd, sha1_to_hex(sha1), 40) != 40 ||
write_in_full(lock->lk->fd, &term, 1) != 1 ||
close_ref(lock) < 0) {
- int save_errno = errno;
- error("Couldn't write %s", lock->lk->filename.buf);
+ strbuf_addf(err,
+ "Couldn't write %s", lock->lk->filename.buf);
unlock_ref(lock);
- errno = save_errno;
return -1;
}
return 0;
@@ -3223,12 +3323,17 @@ static int write_ref_to_lockfile(struct ref_lock *lock,
* necessary, using the specified lockmsg (which can be NULL).
*/
static int commit_ref_update(struct ref_lock *lock,
- const unsigned char *sha1, const char *logmsg)
+ const unsigned char *sha1, const char *logmsg,
+ int flags, struct strbuf *err)
{
clear_loose_ref_cache(&ref_cache);
- if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg) < 0 ||
+ if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, flags, err) < 0 ||
(strcmp(lock->ref_name, lock->orig_ref_name) &&
- log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, logmsg) < 0)) {
+ log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, logmsg, flags, err) < 0)) {
+ char *old_msg = strbuf_detach(err, NULL);
+ strbuf_addf(err, "Cannot update the ref '%s': %s",
+ lock->ref_name, old_msg);
+ free(old_msg);
unlock_ref(lock);
return -1;
}
@@ -3251,14 +3356,21 @@ static int commit_ref_update(struct ref_lock *lock,
head_ref = resolve_ref_unsafe("HEAD", RESOLVE_REF_READING,
head_sha1, &head_flag);
if (head_ref && (head_flag & REF_ISSYMREF) &&
- !strcmp(head_ref, lock->ref_name))
- log_ref_write("HEAD", lock->old_oid.hash, sha1, logmsg);
+ !strcmp(head_ref, lock->ref_name)) {
+ struct strbuf log_err = STRBUF_INIT;
+ if (log_ref_write("HEAD", lock->old_oid.hash, sha1,
+ logmsg, 0, &log_err)) {
+ error("%s", log_err.buf);
+ strbuf_release(&log_err);
+ }
+ }
}
if (commit_ref(lock)) {
error("Couldn't set %s", lock->ref_name);
unlock_ref(lock);
return -1;
}
+
unlock_ref(lock);
return 0;
}
@@ -3271,6 +3383,7 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
int fd, len, written;
char *git_HEAD = git_pathdup("%s", ref_target);
unsigned char old_sha1[20], new_sha1[20];
+ struct strbuf err = STRBUF_INIT;
if (logmsg && read_ref(ref_target, old_sha1))
hashclr(old_sha1);
@@ -3319,8 +3432,11 @@ int create_symref(const char *ref_target, const char *refs_heads_master,
#ifndef NO_SYMLINK_HEAD
done:
#endif
- if (logmsg && !read_ref(refs_heads_master, new_sha1))
- log_ref_write(ref_target, old_sha1, new_sha1, logmsg);
+ if (logmsg && !read_ref(refs_heads_master, new_sha1) &&
+ log_ref_write(ref_target, old_sha1, new_sha1, logmsg, 0, &err)) {
+ error("%s", err.buf);
+ strbuf_release(&err);
+ }
free(git_HEAD);
return 0;
@@ -3371,14 +3487,14 @@ static int read_ref_at_ent(unsigned char *osha1, unsigned char *nsha1,
hashcpy(cb->sha1, nsha1);
if (hashcmp(cb->osha1, nsha1))
warning("Log for ref %s has gap after %s.",
- cb->refname, show_date(cb->date, cb->tz, DATE_RFC2822));
+ cb->refname, show_date(cb->date, cb->tz, DATE_MODE(RFC2822)));
}
else if (cb->date == cb->at_time)
hashcpy(cb->sha1, nsha1);
else if (hashcmp(nsha1, cb->sha1))
warning("Log for ref %s unexpectedly ended on %s.",
cb->refname, show_date(cb->date, cb->tz,
- DATE_RFC2822));
+ DATE_MODE(RFC2822)));
hashcpy(cb->osha1, osha1);
hashcpy(cb->nsha1, nsha1);
cb->found_it = 1;
@@ -3956,14 +4072,19 @@ int ref_transaction_commit(struct ref_transaction *transaction,
* value, so we don't need to write it.
*/
} else if (write_ref_to_lockfile(update->lock,
- update->new_sha1)) {
+ update->new_sha1,
+ err)) {
+ char *write_err = strbuf_detach(err, NULL);
+
/*
* The lock was freed upon failure of
* write_ref_to_lockfile():
*/
update->lock = NULL;
- strbuf_addf(err, "cannot update the ref '%s'.",
- update->refname);
+ strbuf_addf(err,
+ "cannot update the ref '%s': %s",
+ update->refname, write_err);
+ free(write_err);
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
} else {
@@ -3989,11 +4110,10 @@ int ref_transaction_commit(struct ref_transaction *transaction,
if (update->flags & REF_NEEDS_COMMIT) {
if (commit_ref_update(update->lock,
- update->new_sha1, update->msg)) {
+ update->new_sha1, update->msg,
+ update->flags, err)) {
/* freed by commit_ref_update(): */
update->lock = NULL;
- strbuf_addf(err, "Cannot update the ref '%s'.",
- update->refname);
ret = TRANSACTION_GENERIC_ERROR;
goto cleanup;
} else {
@@ -4038,6 +4158,98 @@ cleanup:
return ret;
}
+static int ref_present(const char *refname,
+ const struct object_id *oid, int flags, void *cb_data)
+{
+ struct string_list *affected_refnames = cb_data;
+
+ return string_list_has_string(affected_refnames, refname);
+}
+
+int initial_ref_transaction_commit(struct ref_transaction *transaction,
+ struct strbuf *err)
+{
+ struct ref_dir *loose_refs = get_loose_refs(&ref_cache);
+ struct ref_dir *packed_refs = get_packed_refs(&ref_cache);
+ int ret = 0, i;
+ int n = transaction->nr;
+ struct ref_update **updates = transaction->updates;
+ struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
+
+ assert(err);
+
+ if (transaction->state != REF_TRANSACTION_OPEN)
+ die("BUG: commit called for transaction that is not open");
+
+ /* Fail if a refname appears more than once in the transaction: */
+ for (i = 0; i < n; i++)
+ string_list_append(&affected_refnames, updates[i]->refname);
+ string_list_sort(&affected_refnames);
+ if (ref_update_reject_duplicates(&affected_refnames, err)) {
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ }
+
+ /*
+ * It's really undefined to call this function in an active
+ * repository or when there are existing references: we are
+ * only locking and changing packed-refs, so (1) any
+ * simultaneous processes might try to change a reference at
+ * the same time we do, and (2) any existing loose versions of
+ * the references that we are setting would have precedence
+ * over our values. But some remote helpers create the remote
+ * "HEAD" and "master" branches before calling this function,
+ * so here we really only check that none of the references
+ * that we are creating already exists.
+ */
+ if (for_each_rawref(ref_present, &affected_refnames))
+ die("BUG: initial ref transaction called with existing refs");
+
+ for (i = 0; i < n; i++) {
+ struct ref_update *update = updates[i];
+
+ if ((update->flags & REF_HAVE_OLD) &&
+ !is_null_sha1(update->old_sha1))
+ die("BUG: initial ref transaction with old_sha1 set");
+ if (verify_refname_available(update->refname,
+ &affected_refnames, NULL,
+ loose_refs, err) ||
+ verify_refname_available(update->refname,
+ &affected_refnames, NULL,
+ packed_refs, err)) {
+ ret = TRANSACTION_NAME_CONFLICT;
+ goto cleanup;
+ }
+ }
+
+ if (lock_packed_refs(0)) {
+ strbuf_addf(err, "unable to lock packed-refs file: %s",
+ strerror(errno));
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ }
+
+ for (i = 0; i < n; i++) {
+ struct ref_update *update = updates[i];
+
+ if ((update->flags & REF_HAVE_NEW) &&
+ !is_null_sha1(update->new_sha1))
+ add_packed_ref(update->refname, update->new_sha1);
+ }
+
+ if (commit_packed_refs()) {
+ strbuf_addf(err, "unable to commit packed-refs file: %s",
+ strerror(errno));
+ ret = TRANSACTION_GENERIC_ERROR;
+ goto cleanup;
+ }
+
+cleanup:
+ transaction->state = REF_TRANSACTION_CLOSED;
+ string_list_clear(&affected_refnames, 0);
+ return ret;
+}
+
char *shorten_unambiguous_ref(const char *refname, int strict)
{
int i;