Merge branch 'dt/refs-pseudo'
authorJunio C Hamano <gitster@pobox.com>
Tue, 25 Aug 2015 21:57:08 +0000 (14:57 -0700)
committerJunio C Hamano <gitster@pobox.com>
Tue, 25 Aug 2015 21:57:08 +0000 (14:57 -0700)
To prepare for allowing a different "ref" backend to be plugged in
to the system, update_ref()/delete_ref() have been taught about
ref-like things like MERGE_HEAD that are per-worktree (they will
always be written to the filesystem inside $GIT_DIR).

* dt/refs-pseudo:
  pseudoref: check return values from read_ref()
  sequencer: replace write_cherry_pick_head with update_ref
  bisect: use update_ref
  pseudorefs: create and use pseudoref update and delete functions
  refs: add ref_type function
  refs: introduce pseudoref and per-worktree ref concepts

1  2 
bisect.c
refs.c
refs.h
sequencer.c

diff --cc bisect.c
+++ b/bisect.c
@@@ -19,11 -19,7 +19,10 @@@ static struct object_id *current_bad_oi
  
  static const char *argv_checkout[] = {"checkout", "-q", NULL, "--", NULL};
  static const char *argv_show_branch[] = {"show-branch", NULL, NULL};
- static const char *argv_update_ref[] = {"update-ref", "--no-deref", "BISECT_HEAD", NULL, NULL};
  
 +static const char *term_bad;
 +static const char *term_good;
 +
  /* Remember to update object flag allocation in object.h */
  #define COUNTED               (1u<<16)
  
@@@ -951,9 -884,7 +932,8 @@@ int bisect_next_all(const char *prefix
        struct commit_list *tried;
        int reaches = 0, all = 0, nr, steps;
        const unsigned char *bisect_rev;
-       char bisect_rev_hex[GIT_SHA1_HEXSZ + 1];
  
 +      read_bisect_terms(&term_bad, &term_good);
        if (read_bisect_refs())
                die("reading bisect refs failed");
  
  
        if (!hashcmp(bisect_rev, current_bad_oid->hash)) {
                exit_if_skipped_commits(tried, current_bad_oid);
-               printf("%s is the first %s commit\n", bisect_rev_hex,
 -              printf("%s is the first bad commit\n", sha1_to_hex(bisect_rev));
++              printf("%s is the first %s commit\n", sha1_to_hex(bisect_rev),
 +                      term_bad);
                show_diff_tree(prefix, revs.commits->item);
                /* This means the bisection process succeeded. */
                exit(10);
diff --cc refs.c
--- 1/refs.c
--- 2/refs.c
+++ b/refs.c
@@@ -2854,15 -2821,120 +2854,120 @@@ static int delete_ref_loose(struct ref_
        return 0;
  }
  
 -int delete_ref(const char *refname, const unsigned char *sha1, unsigned int flags)
+ static int is_per_worktree_ref(const char *refname)
+ {
+       return !strcmp(refname, "HEAD");
+ }
+ static int is_pseudoref_syntax(const char *refname)
+ {
+       const char *c;
+       for (c = refname; *c; c++) {
+               if (!isupper(*c) && *c != '-' && *c != '_')
+                       return 0;
+       }
+       return 1;
+ }
+ enum ref_type ref_type(const char *refname)
+ {
+       if (is_per_worktree_ref(refname))
+               return REF_TYPE_PER_WORKTREE;
+       if (is_pseudoref_syntax(refname))
+               return REF_TYPE_PSEUDOREF;
+        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];
+               if (read_ref(pseudoref, actual_old_sha1))
+                       die("could not read ref '%s'", pseudoref);
+               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);
+               if (read_ref(pseudoref, actual_old_sha1))
+                       die("could not read ref '%s'", pseudoref);
+               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 *old_sha1,
 +             unsigned int flags)
  {
        struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
  
 -              return delete_pseudoref(refname, sha1);
+       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,
 -                                 (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);
diff --cc refs.h
--- 1/refs.h
--- 2/refs.h
+++ b/refs.h
@@@ -442,9 -378,16 +442,17 @@@ int update_ref(const char *msg, const c
               unsigned int flags, enum action_on_err onerr);
  
  extern int parse_hide_refs_config(const char *var, const char *value, const char *);
 +
  extern int ref_is_hidden(const char *);
  
+ enum ref_type {
+       REF_TYPE_PER_WORKTREE,
+       REF_TYPE_PSEUDOREF,
+       REF_TYPE_NORMAL,
+ };
+ enum ref_type ref_type(const char *refname);
  enum expire_reflog_flags {
        EXPIRE_REFLOGS_DRY_RUN = 1 << 0,
        EXPIRE_REFLOGS_UPDATE_REF = 1 << 1,
diff --cc sequencer.c
Simple merge