OSDN Git Service

pseudorefs: create and use pseudoref update and delete functions
authorDavid Turner <dturner@twopensource.com>
Fri, 31 Jul 2015 06:06:19 +0000 (02:06 -0400)
committerJunio C Hamano <gitster@pobox.com>
Fri, 31 Jul 2015 17:39:38 +0000 (10:39 -0700)
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 <dturner@twopensource.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
refs.c

diff --git a/refs.c b/refs.c
index 58be259..522b19b 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -2847,11 +2847,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);
+                       rollback_lock_file(&lock);
+                       return -1;
+               }
+
+               unlink(filename);
+               rollback_lock_file(&lock);
+       } else {
+               unlink(filename);
+       }
+
+       return 0;
+}
+
 int delete_ref(const char *refname, const unsigned char *sha1, unsigned int flags)
 {
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
 
+       if (ref_type(refname) == REF_TYPE_PSEUDOREF)
+               return delete_pseudoref(refname, sha1);
+
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
            ref_transaction_delete(transaction, refname,
@@ -3908,17 +3984,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);
@@ -3933,7 +4017,8 @@ int update_ref(const char *msg, const char *refname,
                return 1;
        }
        strbuf_release(&err);
-       ref_transaction_free(t);
+       if (t)
+               ref_transaction_free(t);
        return 0;
 }