Pseudorefs should not be updated through the ref transaction
API, because alternate ref backends still need to store pseudorefs
in GIT_DIR (instead of wherever they store refs). Instead,
change update_ref and delete_ref to call pseudoref-specific
functions.
Signed-off-by: David Turner dtur...@twopensource.com
---
refs.c | 100 +++--
1 file changed, 92 insertions(+), 8 deletions(-)
diff --git a/refs.c b/refs.c
index 553ae8b..2bd6aa6 100644
--- a/refs.c
+++ b/refs.c
@@ -2877,12 +2877,87 @@ enum ref_type ref_type(const char *refname)
return REF_TYPE_NORMAL;
}
+static int write_pseudoref(const char *pseudoref, const unsigned char *sha1,
+ const unsigned char *old_sha1, struct strbuf *err)
+{
+ const char *filename;
+ int fd;
+ static struct lock_file lock;
+ struct strbuf buf = STRBUF_INIT;
+ int ret = -1;
+
+ strbuf_addf(buf, %s\n, sha1_to_hex(sha1));
+
+ filename = git_path(%s, pseudoref);
+ fd = hold_lock_file_for_update(lock, filename, LOCK_DIE_ON_ERROR);
+ if (fd 0) {
+ strbuf_addf(err, Could not open '%s' for writing: %s,
+ filename, strerror(errno));
+ return -1;
+ }
+
+ if (old_sha1) {
+ unsigned char actual_old_sha1[20];
+ read_ref(pseudoref, actual_old_sha1);
+ if (hashcmp(actual_old_sha1, old_sha1)) {
+ strbuf_addf(err, Unexpected sha1 when writing %s,
pseudoref);
+ rollback_lock_file(lock);
+ goto done;
+ }
+ }
+
+ if (write_in_full(fd, buf.buf, buf.len) != buf.len) {
+ strbuf_addf(err, Could not write to '%s', filename);
+ rollback_lock_file(lock);
+ goto done;
+ }
+
+ commit_lock_file(lock);
+ ret = 0;
+done:
+ strbuf_release(buf);
+ return ret;
+}
+
+static int delete_pseudoref(const char *pseudoref, const unsigned char
*old_sha1)
+{
+ static struct lock_file lock;
+ const char *filename;
+
+ filename = git_path(%s, pseudoref);
+
+ if (old_sha1 !is_null_sha1(old_sha1)) {
+ int fd;
+ unsigned char actual_old_sha1[20];
+
+ fd = hold_lock_file_for_update(lock, filename,
+ LOCK_DIE_ON_ERROR);
+ if (fd 0)
+ die_errno(_(Could not open '%s' for writing),
filename);
+ read_ref(pseudoref, actual_old_sha1);
+ if (hashcmp(actual_old_sha1, old_sha1)) {
+ warning(Unexpected sha1 when deleting %s, pseudoref);
+ return -1;
+ }
+
+ unlink(filename);
+ rollback_lock_file(lock);
+ } else {
+ unlink(filename);
+ }
+
+ return 0;
+}
+
int delete_ref(const char *refname, const unsigned char *old_sha1,
unsigned int flags)
{
struct ref_transaction *transaction;
struct strbuf err = STRBUF_INIT;
+ if (ref_type(refname) == REF_TYPE_PSEUDOREF)
+ return delete_pseudoref(refname, old_sha1);
+
transaction = ref_transaction_begin(err);
if (!transaction ||
ref_transaction_delete(transaction, refname, old_sha1,
@@ -3976,17 +4051,25 @@ int update_ref(const char *msg, const char *refname,
const unsigned char *new_sha1, const unsigned char *old_sha1,
unsigned int flags, enum action_on_err onerr)
{
- struct ref_transaction *t;
+ struct ref_transaction *t = NULL;
struct strbuf err = STRBUF_INIT;
+ int ret = 0;
- t = ref_transaction_begin(err);
- if (!t ||
- ref_transaction_update(t, refname, new_sha1, old_sha1,
- flags, msg, err) ||
- ref_transaction_commit(t, err)) {
+ if (ref_type(refname) == REF_TYPE_PSEUDOREF) {
+ ret = write_pseudoref(refname, new_sha1, old_sha1, err);
+ } else {
+ t = ref_transaction_begin(err);
+ if (!t ||
+ ref_transaction_update(t, refname, new_sha1, old_sha1,
+ flags, msg, err) ||
+ ref_transaction_commit(t, err)) {
+ ret = 1;
+ ref_transaction_free(t);
+ }
+ }
+ if (ret) {
const char *str = update_ref failed for ref '%s': %s;
- ref_transaction_free(t);
switch (onerr) {
case UPDATE_REFS_MSG_ON_ERR:
error(str, refname, err.buf);
@@ -4001,7 +4084,8 @@ int update_ref(const char *msg, const char *refname,
return 1;
}
strbuf_release(err);
- ref_transaction_free(t);
+ if