Code

Disallow invalid --pretty= abbreviations
[git.git] / sha1_name.c
index b4975289d5063880f8541d1b43aa92d074b74b8d..de8caf8cf6c81480f3e93edc5e3b4582a319a479 100644 (file)
@@ -157,7 +157,7 @@ static int get_short_sha1(const char *name, int len, unsigned char *sha1,
        char canonical[40];
        unsigned char res[20];
 
-       if (len < MINIMUM_ABBREV)
+       if (len < MINIMUM_ABBREV || len > 40)
                return -1;
        hashclr(res);
        memset(canonical, 'x', 40);
@@ -235,7 +235,7 @@ static int ambiguous_path(const char *path, int len)
        return slash;
 }
 
-static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
+int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
 {
        static const char *fmt[] = {
                "%.*s",
@@ -246,44 +246,57 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
                "refs/remotes/%.*s/HEAD",
                NULL
        };
+       const char **p, *r;
+       int refs_found = 0;
+
+       *ref = NULL;
+       for (p = fmt; *p; p++) {
+               unsigned char sha1_from_ref[20];
+               unsigned char *this_result;
+
+               this_result = refs_found ? sha1_from_ref : sha1;
+               r = resolve_ref(mkpath(*p, len, str), this_result, 1, NULL);
+               if (r) {
+                       if (!refs_found++)
+                               *ref = xstrdup(r);
+                       if (!warn_ambiguous_refs)
+                               break;
+               }
+       }
+       return refs_found;
+}
+
+static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
+{
        static const char *warning = "warning: refname '%.*s' is ambiguous.\n";
-       const char **p, *ref;
        char *real_ref = NULL;
-       int refs_found = 0, am;
-       unsigned long at_time = (unsigned long)-1;
-       unsigned char *this_result;
-       unsigned char sha1_from_ref[20];
+       int refs_found = 0;
+       int at, reflog_len;
 
        if (len == 40 && !get_sha1_hex(str, sha1))
                return 0;
 
-       /* At a given period of time? "@{2 hours ago}" */
-       for (am = 1; am < len - 1; am++) {
-               if (str[am] == '@' && str[am+1] == '{' && str[len-1] == '}') {
-                       int date_len = len - am - 3;
-                       char *date_spec = xmalloc(date_len + 1);
-                       strlcpy(date_spec, str + am + 2, date_len + 1);
-                       at_time = approxidate(date_spec);
-                       free(date_spec);
-                       len = am;
-                       break;
+       /* basic@{time or number} format to query ref-log */
+       reflog_len = at = 0;
+       if (str[len-1] == '}') {
+               for (at = 0; at < len - 1; at++) {
+                       if (str[at] == '@' && str[at+1] == '{') {
+                               reflog_len = (len-1) - (at+2);
+                               len = at;
+                               break;
+                       }
                }
        }
 
        /* Accept only unambiguous ref paths. */
-       if (ambiguous_path(str, len))
+       if (len && ambiguous_path(str, len))
                return -1;
 
-       for (p = fmt; *p; p++) {
-               this_result = refs_found ? sha1_from_ref : sha1;
-               ref = resolve_ref(mkpath(*p, len, str), this_result, 1);
-               if (ref) {
-                       if (!refs_found++)
-                               real_ref = xstrdup(ref);
-                       if (!warn_ambiguous_refs)
-                               break;
-               }
-       }
+       if (!len && reflog_len) {
+               /* allow "@{...}" to mean the current branch reflog */
+               refs_found = dwim_ref("HEAD", 4, sha1, &real_ref);
+       } else
+               refs_found = dwim_ref(str, len, sha1, &real_ref);
 
        if (!refs_found)
                return -1;
@@ -291,11 +304,51 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
        if (warn_ambiguous_refs && refs_found > 1)
                fprintf(stderr, warning, len, str);
 
-       if (at_time != (unsigned long)-1) {
-               read_ref_at(
-                       real_ref,
-                       at_time,
-                       sha1);
+       if (reflog_len) {
+               int nth, i;
+               unsigned long at_time;
+               unsigned long co_time;
+               int co_tz, co_cnt;
+
+               /*
+                * We'll have an independent reflog for "HEAD" eventually
+                * which won't be a synonym for the current branch reflog.
+                * In the mean time prevent people from getting used to
+                * such a synonym until the work is completed.
+                */
+               if (len && !strncmp("HEAD", str, len) &&
+                   !strncmp(real_ref, "refs/", 5)) {
+                       error("reflog for HEAD has not been implemented yet\n"
+                             "Maybe you could try %s%s instead, "
+                             "or just %s for current branch..",
+                             strchr(real_ref+5, '/')+1, str+len, str+len);
+                       exit(-1);
+               }
+
+               /* Is it asking for N-th entry, or approxidate? */
+               for (i = nth = 0; 0 <= nth && i < reflog_len; i++) {
+                       char ch = str[at+2+i];
+                       if ('0' <= ch && ch <= '9')
+                               nth = nth * 10 + ch - '0';
+                       else
+                               nth = -1;
+               }
+               if (0 <= nth)
+                       at_time = 0;
+               else
+                       at_time = approxidate(str + at + 2);
+               if (read_ref_at(real_ref, at_time, nth, sha1, NULL,
+                               &co_time, &co_tz, &co_cnt)) {
+                       if (at_time)
+                               fprintf(stderr,
+                                       "warning: Log for '%.*s' only goes "
+                                       "back to %s.\n", len, str,
+                                       show_rfc2822_date(co_time, co_tz));
+                       else
+                               fprintf(stderr,
+                                       "warning: Log for '%.*s' only has "
+                                       "%d entries.\n", len, str, co_cnt);
+               }
        }
 
        free(real_ref);
@@ -431,6 +484,26 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
        return 0;
 }
 
+static int get_describe_name(const char *name, int len, unsigned char *sha1)
+{
+       const char *cp;
+
+       for (cp = name + len - 1; name + 2 <= cp; cp--) {
+               char ch = *cp;
+               if (hexval(ch) & ~0377) {
+                       /* We must be looking at g in "SOMETHING-g"
+                        * for it to be describe output.
+                        */
+                       if (ch == 'g' && cp[-1] == '-') {
+                               cp++;
+                               len -= cp - name;
+                               return get_short_sha1(cp, len, sha1, 1);
+                       }
+               }
+       }
+       return -1;
+}
+
 static int get_sha1_1(const char *name, int len, unsigned char *sha1)
 {
        int ret, has_suffix;
@@ -472,6 +545,12 @@ static int get_sha1_1(const char *name, int len, unsigned char *sha1)
        ret = get_sha1_basic(name, len, sha1);
        if (!ret)
                return 0;
+
+       /* It could be describe output that is "SOMETHING-gXXXX" */
+       ret = get_describe_name(name, len, sha1);
+       if (!ret)
+               return 0;
+
        return get_short_sha1(name, len, sha1, 0);
 }