push: fix segfault for odd config
[gitweb.git] / sha1_name.c
index d6972f2d6a2fd5af38b9babbacd97c9388962bb4..2f75179f4c6c1d05bdd7594b23dcf77007c26751 100644 (file)
@@ -238,8 +238,28 @@ static int ambiguous_path(const char *path, int len)
        return slash;
 }
 
+/*
+ * *string and *len will only be substituted, and *string returned (for
+ * later free()ing) if the string passed in is of the form @{-<n>}.
+ */
+static char *substitute_nth_last_branch(const char **string, int *len)
+{
+       struct strbuf buf = STRBUF_INIT;
+       int ret = interpret_nth_last_branch(*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_nth_last_branch(&str, &len);
        const char **p, *r;
        int refs_found = 0;
 
@@ -248,22 +268,27 @@ int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref)
                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, NULL);
+               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) &&
+                          (len != 4 || strcmp(str, "HEAD")))
+                       warning("ignoring dangling symref %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_nth_last_branch(&str, &len);
        const char **p;
        int logs_found = 0;
 
@@ -294,6 +319,7 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
                if (!warn_ambiguous_refs)
                        break;
        }
+       free(last_branch);
        return logs_found;
 }
 
@@ -311,8 +337,8 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 
        /* basic@{time or number or -number} format to query ref-log */
        reflog_len = at = 0;
-       if (str[len-1] == '}') {
-               for (at = 0; at < len - 1; at++) {
+       if (len && str[len-1] == '}') {
+               for (at = len-2; at >= 0; at--) {
                        if (str[at] == '@' && str[at+1] == '{') {
                                reflog_len = (len-1) - (at+2);
                                len = at;
@@ -700,17 +726,13 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
 
        if (!prefixcmp(message, "checkout: moving from ")) {
                match = message + strlen("checkout: moving from ");
-               if ((target = strstr(match, " to ")) != NULL)
-                       target += 4;
+               target = strstr(match, " to ");
        }
 
-       if (!match)
-               return 0;
-
-       len = target - match - 4;
-       if (target[len] == '\n' && !strncmp(match, target, len))
+       if (!match || !target)
                return 0;
 
+       len = target - match;
        nth = cb->cnt++ % cb->alloc;
        strbuf_reset(&cb->buf[nth]);
        strbuf_add(&cb->buf[nth], match, len);
@@ -731,7 +753,7 @@ static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
 int interpret_nth_last_branch(const char *name, struct strbuf *buf)
 {
        long nth;
-       int i;
+       int i, retval;
        struct grab_nth_branch_switch_cbdata cb;
        const char *brace;
        char *num_end;
@@ -751,17 +773,27 @@ int interpret_nth_last_branch(const char *name, struct strbuf *buf)
        for (i = 0; i < nth; i++)
                strbuf_init(&cb.buf[i], 20);
        cb.cnt = 0;
-       for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
+       retval = 0;
+       for_each_recent_reflog_ent("HEAD", grab_nth_branch_switch, 40960, &cb);
+       if (cb.cnt < nth) {
+               cb.cnt = 0;
+               for (i = 0; i < nth; i++)
+                       strbuf_release(&cb.buf[i]);
+               for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
+       }
        if (cb.cnt < nth)
-               return 0;
+               goto release_return;
        i = cb.cnt % nth;
        strbuf_reset(buf);
        strbuf_add(buf, cb.buf[i].buf, cb.buf[i].len);
+       retval = brace-name+1;
+
+release_return:
        for (i = 0; i < nth; i++)
                strbuf_release(&cb.buf[i]);
        free(cb.buf);
 
-       return brace-name+1;
+       return retval;
 }
 
 /*