annotate: fix for cvsserver.
[gitweb.git] / sha1_name.c
index 6d7cd78381414aa2fef31d31d46fbb24b0aaab1d..d77f77097f6e00615732c5e6d50a2c594bef4311 100644 (file)
@@ -235,24 +235,66 @@ static int ambiguous_path(const char *path, int len)
        return slash;
 }
 
+static const char *ref_fmt[] = {
+       "%.*s",
+       "refs/%.*s",
+       "refs/tags/%.*s",
+       "refs/heads/%.*s",
+       "refs/remotes/%.*s",
+       "refs/remotes/%.*s/HEAD",
+       NULL
+};
+
+int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
+{
+       const char **p, *r;
+       int refs_found = 0;
+
+       *ref = NULL;
+       for (p = ref_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 dwim_log(const char *str, int len, unsigned char *sha1, char **log)
+{
+       const char **p;
+       int logs_found = 0;
+
+       *log = NULL;
+       for (p = ref_fmt; *p; p++) {
+               struct stat st;
+               char *path = mkpath(*p, len, str);
+               if (!stat(git_path("logs/%s", path), &st) &&
+                   S_ISREG(st.st_mode)) {
+                       if (!logs_found++) {
+                               *log = xstrdup(path);
+                               resolve_ref(path, sha1, 0, NULL);
+                       }
+                       if (!warn_ambiguous_refs)
+                               break;
+               }
+       }
+       return logs_found;
+}
+
 static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 {
-       static const char *fmt[] = {
-               "%.*s",
-               "refs/%.*s",
-               "refs/tags/%.*s",
-               "refs/heads/%.*s",
-               "refs/remotes/%.*s",
-               "refs/remotes/%.*s/HEAD",
-               NULL
-       };
        static const char *warning = "warning: refname '%.*s' is ambiguous.\n";
-       const char **p, *ref;
        char *real_ref = NULL;
        int refs_found = 0;
        int at, reflog_len;
-       unsigned char *this_result;
-       unsigned char sha1_from_ref[20];
 
        if (len == 40 && !get_sha1_hex(str, sha1))
                return 0;
@@ -260,7 +302,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
        /* basic@{time or number} format to query ref-log */
        reflog_len = at = 0;
        if (str[len-1] == '}') {
-               for (at = 1; at < len - 1; at++) {
+               for (at = 0; at < len - 1; at++) {
                        if (str[at] == '@' && str[at+1] == '{') {
                                reflog_len = (len-1) - (at+2);
                                len = at;
@@ -270,19 +312,16 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
        }
 
        /* 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, NULL);
-               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 if (reflog_len)
+               refs_found = dwim_log(str, len, sha1, &real_ref);
+       else
+               refs_found = dwim_ref(str, len, sha1, &real_ref);
 
        if (!refs_found)
                return -1;
@@ -291,9 +330,12 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
                fprintf(stderr, warning, len, str);
 
        if (reflog_len) {
-               /* Is it asking for N-th entry, or approxidate? */
                int nth, i;
                unsigned long at_time;
+               unsigned long co_time;
+               int co_tz, co_cnt;
+
+               /* 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')
@@ -305,7 +347,18 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
                        at_time = 0;
                else
                        at_time = approxidate(str + at + 2);
-               read_ref_at(real_ref, at_time, nth, sha1);
+               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);