resolve_ref(): report breakage to the caller without warning
[gitweb.git] / refs.c
diff --git a/refs.c b/refs.c
index 52597244b871887028aafbefece4d22bbe1001ec..448f0271f830ca1b38f4d077b667e7aa122657d3 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -4,9 +4,8 @@
 #include "tag.h"
 #include "dir.h"
 
-/* ISSYMREF=01 and ISPACKED=02 are public interfaces */
-#define REF_KNOWS_PEELED 04
-#define REF_BROKEN 010
+/* ISSYMREF=0x01, ISPACKED=0x02 and ISBROKEN=0x04 are public interfaces */
+#define REF_KNOWS_PEELED 0x10
 
 struct ref_list {
        struct ref_list *next;
@@ -56,6 +55,8 @@ static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
        entry = xmalloc(sizeof(struct ref_list) + len);
        hashcpy(entry->sha1, sha1);
        hashclr(entry->peeled);
+       if (check_refname_format(name, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT))
+               die("Reference has invalid format: '%s'", name);
        memcpy(entry->name, name, len);
        entry->flag = flag;
        entry->next = list;
@@ -307,12 +308,12 @@ static struct ref_list *get_ref_dir(const char *submodule, const char *base,
                                flag = 0;
                                if (resolve_gitlink_ref(submodule, ref, sha1) < 0) {
                                        hashclr(sha1);
-                                       flag |= REF_BROKEN;
+                                       flag |= REF_ISBROKEN;
                                }
                        } else
                                if (!resolve_ref(ref, sha1, 1, &flag)) {
                                        hashclr(sha1);
-                                       flag |= REF_BROKEN;
+                                       flag |= REF_ISBROKEN;
                                }
                        list = add_ref(ref, sha1, flag, list, NULL);
                }
@@ -466,17 +467,22 @@ int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *re
 }
 
 /*
- * If the "reading" argument is set, this function finds out what _object_
- * the ref points at by "reading" the ref.  The ref, if it is not symbolic,
- * has to exist, and if it is symbolic, it has to point at an existing ref,
- * because the "read" goes through the symref to the ref it points at.
- *
- * The access that is not "reading" may often be "writing", but does not
- * have to; it can be merely checking _where it leads to_. If it is a
- * prelude to "writing" to the ref, a write to a symref that points at
- * yet-to-be-born ref will create the real ref pointed by the symref.
- * reading=0 allows the caller to check where such a symref leads to.
+ * Try to read ref from the packed references.  On success, set sha1
+ * and return 0; otherwise, return -1.
  */
+static int get_packed_ref(const char *ref, unsigned char *sha1)
+{
+       struct ref_list *list = get_packed_refs(NULL);
+       while (list) {
+               if (!strcmp(ref, list->name)) {
+                       hashcpy(sha1, list->sha1);
+                       return 0;
+               }
+               list = list->next;
+       }
+       return -1;
+}
+
 const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)
 {
        int depth = MAXDEPTH;
@@ -487,6 +493,9 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
        if (flag)
                *flag = 0;
 
+       if (check_refname_format(ref, REFNAME_ALLOW_ONELEVEL))
+               return NULL;
+
        for (;;) {
                char path[PATH_MAX];
                struct stat st;
@@ -497,29 +506,36 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
                        return NULL;
 
                git_snpath(path, sizeof(path), "%s", ref);
-               /* Special case: non-existing file. */
+
                if (lstat(path, &st) < 0) {
-                       struct ref_list *list = get_packed_refs(NULL);
-                       while (list) {
-                               if (!strcmp(ref, list->name)) {
-                                       hashcpy(sha1, list->sha1);
-                                       if (flag)
-                                               *flag |= REF_ISPACKED;
-                                       return ref;
-                               }
-                               list = list->next;
+                       if (errno != ENOENT)
+                               return NULL;
+                       /*
+                        * The loose reference file does not exist;
+                        * check for a packed reference.
+                        */
+                       if (!get_packed_ref(ref, sha1)) {
+                               if (flag)
+                                       *flag |= REF_ISPACKED;
+                               return ref;
                        }
-                       if (reading || errno != ENOENT)
+                       /* The reference is not a packed reference, either. */
+                       if (reading) {
                                return NULL;
-                       hashclr(sha1);
-                       return ref;
+                       } else {
+                               hashclr(sha1);
+                               return ref;
+                       }
                }
 
                /* Follow "normalized" - ie "refs/.." symlinks by hand */
                if (S_ISLNK(st.st_mode)) {
                        len = readlink(path, buffer, sizeof(buffer)-1);
-                       if (len >= 5 && !memcmp("refs/", buffer, 5)) {
-                               buffer[len] = 0;
+                       if (len < 0)
+                               return NULL;
+                       buffer[len] = 0;
+                       if (!prefixcmp(buffer, "refs/") &&
+                                       !check_refname_format(buffer, 0)) {
                                strcpy(ref_buffer, buffer);
                                ref = ref_buffer;
                                if (flag)
@@ -543,26 +559,35 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *
                        return NULL;
                len = read_in_full(fd, buffer, sizeof(buffer)-1);
                close(fd);
+               if (len < 0)
+                       return NULL;
+               while (len && isspace(buffer[len-1]))
+                       len--;
+               buffer[len] = '\0';
 
                /*
                 * Is it a symbolic ref?
                 */
-               if (len < 4 || memcmp("ref:", buffer, 4))
+               if (prefixcmp(buffer, "ref:"))
                        break;
-               buf = buffer + 4;
-               len -= 4;
-               while (len && isspace(*buf))
-                       buf++, len--;
-               while (len && isspace(buf[len-1]))
-                       len--;
-               buf[len] = 0;
-               memcpy(ref_buffer, buf, len + 1);
-               ref = ref_buffer;
                if (flag)
                        *flag |= REF_ISSYMREF;
+               buf = buffer + 4;
+               while (isspace(*buf))
+                       buf++;
+               if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
+                       if (flag)
+                               *flag |= REF_ISBROKEN;
+                       return NULL;
+               }
+               ref = strcpy(ref_buffer, buf);
        }
-       if (len < 40 || get_sha1_hex(buffer, sha1))
+       /* Please note that FETCH_HEAD has a second line containing other data. */
+       if (get_sha1_hex(buffer, sha1) || (buffer[40] != '\0' && !isspace(buffer[40]))) {
+               if (flag)
+                       *flag |= REF_ISBROKEN;
                return NULL;
+       }
        return ref;
 }
 
@@ -588,8 +613,8 @@ static int do_one_ref(const char *base, each_ref_fn fn, int trim,
                return 0;
 
        if (!(flags & DO_FOR_EACH_INCLUDE_BROKEN)) {
-               if (entry->flag & REF_BROKEN)
-                       return 0; /* ignore dangling symref */
+               if (entry->flag & REF_ISBROKEN)
+                       return 0; /* ignore broken refs e.g. dangling symref */
                if (!has_sha1_file(entry->sha1)) {
                        error("%s does not point to a valid object!", entry->name);
                        return 0;
@@ -877,7 +902,7 @@ static inline int bad_ref_char(int ch)
  * the length of the component found, or -1 if the component is not
  * legal.
  */
-static int check_refname_component(const char *ref)
+static int check_refname_component(const char *ref, int flags)
 {
        const char *cp;
        char last = '\0';
@@ -896,8 +921,18 @@ static int check_refname_component(const char *ref)
        }
        if (cp == ref)
                return -1; /* Component has zero length. */
-       if (ref[0] == '.')
-               return -1; /* Component starts with '.'. */
+       if (ref[0] == '.') {
+               if (!(flags & REFNAME_DOT_COMPONENT))
+                       return -1; /* Component starts with '.'. */
+               /*
+                * Even if leading dots are allowed, don't allow "."
+                * as a component (".." is prevented by a rule above).
+                */
+               if (ref[1] == '\0')
+                       return -1; /* Component equals ".". */
+       }
+       if (cp - ref >= 5 && !memcmp(cp - 5, ".lock", 5))
+               return -1; /* Refname ends with ".lock". */
        return cp - ref;
 }
 
@@ -906,11 +941,8 @@ int check_refname_format(const char *ref, int flags)
        int component_len, component_count = 0;
 
        while (1) {
-               while (*ref == '/')
-                       ref++; /* tolerate leading and repeated slashes */
-
                /* We are at the start of a path component. */
-               component_len = check_refname_component(ref);
+               component_len = check_refname_component(ref, flags);
                if (component_len < 0) {
                        if ((flags & REFNAME_REFSPEC_PATTERN) &&
                                        ref[0] == '*' &&
@@ -931,8 +963,6 @@ int check_refname_format(const char *ref, int flags)
 
        if (ref[component_len - 1] == '.')
                return -1; /* Refname ends with '.'. */
-       if (component_len >= 5 && !memcmp(&ref[component_len - 5], ".lock", 5))
-               return -1; /* Refname ends with ".lock". */
        if (!(flags & REFNAME_ALLOW_ONELEVEL) && component_count < 2)
                return -1; /* Refname has only one component. */
        return 0;
@@ -1037,6 +1067,94 @@ static int is_refname_available(const char *ref, const char *oldref,
        return 1;
 }
 
+/*
+ * *string and *len will only be substituted, and *string returned (for
+ * later free()ing) if the string passed in is a magic short-hand form
+ * to name a branch.
+ */
+static char *substitute_branch_name(const char **string, int *len)
+{
+       struct strbuf buf = STRBUF_INIT;
+       int ret = interpret_branch_name(*string, &buf);
+
+       if (ret == *len) {
+               size_t size;
+               *string = strbuf_detach(&buf, &size);
+               *len = size;
+               return (char *)*string;
+       }
+
+       return NULL;
+}
+
+int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
+{
+       char *last_branch = substitute_branch_name(&str, &len);
+       const char **p, *r;
+       int refs_found = 0;
+
+       *ref = NULL;
+       for (p = ref_rev_parse_rules; *p; p++) {
+               char fullref[PATH_MAX];
+               unsigned char sha1_from_ref[20];
+               unsigned char *this_result;
+               int flag;
+
+               this_result = refs_found ? sha1_from_ref : sha1;
+               mksnpath(fullref, sizeof(fullref), *p, len, str);
+               r = resolve_ref(fullref, this_result, 1, &flag);
+               if (r) {
+                       if (!refs_found++)
+                               *ref = xstrdup(r);
+                       if (!warn_ambiguous_refs)
+                               break;
+               } else if ((flag & REF_ISSYMREF) && strcmp(fullref, "HEAD")) {
+                       warning("ignoring dangling symref %s.", fullref);
+               } else if ((flag & REF_ISBROKEN) && strchr(fullref, '/')) {
+                       warning("ignoring broken ref %s.", fullref);
+               }
+       }
+       free(last_branch);
+       return refs_found;
+}
+
+int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
+{
+       char *last_branch = substitute_branch_name(&str, &len);
+       const char **p;
+       int logs_found = 0;
+
+       *log = NULL;
+       for (p = ref_rev_parse_rules; *p; p++) {
+               struct stat st;
+               unsigned char hash[20];
+               char path[PATH_MAX];
+               const char *ref, *it;
+
+               mksnpath(path, sizeof(path), *p, len, str);
+               ref = resolve_ref(path, hash, 1, NULL);
+               if (!ref)
+                       continue;
+               if (!stat(git_path("logs/%s", path), &st) &&
+                   S_ISREG(st.st_mode))
+                       it = path;
+               else if (strcmp(ref, path) &&
+                        !stat(git_path("logs/%s", ref), &st) &&
+                        S_ISREG(st.st_mode))
+                       it = ref;
+               else
+                       continue;
+               if (!logs_found++) {
+                       *log = xstrdup(it);
+                       hashcpy(sha1, hash);
+               }
+               if (!warn_ambiguous_refs)
+                       break;
+       }
+       free(last_branch);
+       return logs_found;
+}
+
 static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int flags, int *type_p)
 {
        char *ref_file;