diff --git a/sha1_name.c b/sha1_name.c
index 6377264300c134db82354d11a5dd62fd7b783a59..5d0ac0263d04d7ec72a3b7dec4aaf47aec80da5e 100644 (file)
--- a/sha1_name.c
+++ b/sha1_name.c
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;
break;
}
}
+ 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;
if (!warn_ambiguous_refs)
break;
}
+ free(last_branch);
return logs_found;
}
+static int get_sha1_1(const char *name, int len, unsigned char *sha1);
+
static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
{
static const char *warning = "warning: refname '%.*s' is ambiguous.\n";
if (len == 40 && !get_sha1_hex(str, sha1))
return 0;
- /* basic@{time or number} format to query ref-log */
+ /* 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;
return -1;
if (!len && reflog_len) {
+ struct strbuf buf = STRBUF_INIT;
+ int ret;
+ /* try the @{-N} syntax for n-th checkout */
+ ret = interpret_nth_last_branch(str+at, &buf);
+ if (ret > 0) {
+ /* substitute this branch name and restart */
+ return get_sha1_1(buf.buf, buf.len, sha1);
+ } else if (ret == 0) {
+ return -1;
+ }
/* allow "@{...}" to mean the current branch reflog */
refs_found = dwim_ref("HEAD", 4, sha1, &real_ref);
} else if (reflog_len)
return 0;
}
-static int get_sha1_1(const char *name, int len, unsigned char *sha1);
-
static int get_parent(const char *name, int len,
unsigned char *result, int idx)
{
}
struct grab_nth_branch_switch_cbdata {
- int counting;
- int nth;
+ long cnt, alloc;
struct strbuf *buf;
};
const char *message, void *cb_data)
{
struct grab_nth_branch_switch_cbdata *cb = cb_data;
- const char *match = NULL;
-
- if (!prefixcmp(message, "checkout: moving to "))
- match = message + strlen("checkout: moving to ");
- else if (!prefixcmp(message, "checkout: moving from ")) {
- const char *cp = message + strlen("checkout: moving from ");
- if ((cp = strstr(cp, " to ")) != NULL) {
- match = cp + 4;
- }
- }
+ const char *match = NULL, *target = NULL;
+ size_t len;
+ int nth;
- if (!match)
- return 0;
+ if (!prefixcmp(message, "checkout: moving from ")) {
+ match = message + strlen("checkout: moving from ");
+ target = strstr(match, " to ");
+ }
- if (cb->counting) {
- cb->nth++;
+ if (!match || !target)
return 0;
- }
- if (--cb->nth <= 0) {
- size_t len = strlen(match);
- while (match[len-1] == '\n')
- len--;
- strbuf_reset(cb->buf);
- strbuf_add(cb->buf, match, len);
- return 1;
- }
+ len = target - match;
+ nth = cb->cnt++ % cb->alloc;
+ strbuf_reset(&cb->buf[nth]);
+ strbuf_add(&cb->buf[nth], match, len);
return 0;
}
/*
* This reads "@{-N}" syntax, finds the name of the Nth previous
* branch we were on, and places the name of the branch in the given
- * buf and returns 0 if successful.
+ * buf and returns the number of characters parsed if successful.
*
* If the input is not of the accepted format, it returns a negative
* number to signal an error.
+ *
+ * If the input was ok but there are not N branch switches in the
+ * reflog, it returns 0.
*/
int interpret_nth_last_branch(const char *name, struct strbuf *buf)
{
- int nth, i;
+ long nth;
+ int i, retval;
struct grab_nth_branch_switch_cbdata cb;
+ const char *brace;
+ char *num_end;
if (name[0] != '@' || name[1] != '{' || name[2] != '-')
return -1;
- for (i = 3, nth = 0; name[i] && name[i] != '}'; i++) {
- char ch = name[i];
- if ('0' <= ch && ch <= '9')
- nth = nth * 10 + ch - '0';
- else
- return -1;
- }
- if (nth < 0 || 10 <= nth)
+ brace = strchr(name, '}');
+ if (!brace)
return -1;
+ nth = strtol(name+3, &num_end, 10);
+ if (num_end != brace)
+ return -1;
+ if (nth <= 0)
+ return -1;
+ cb.alloc = nth;
+ cb.buf = xmalloc(nth * sizeof(struct strbuf));
+ for (i = 0; i < nth; i++)
+ strbuf_init(&cb.buf[i], 20);
+ cb.cnt = 0;
+ 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)
+ 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);
- cb.counting = 1;
- cb.nth = 0;
- cb.buf = buf;
- for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
-
- cb.counting = 0;
- cb.nth -= nth;
- cb.buf = buf;
- for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
- return 0;
+ return retval;
}
/*