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++) {
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)
{
return retval;
}
+struct grab_nth_branch_switch_cbdata {
+ int counting;
+ int nth;
+ struct strbuf *buf;
+};
+
+static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
+ const char *email, unsigned long timestamp, int tz,
+ const char *message, void *cb_data)
+{
+ struct grab_nth_branch_switch_cbdata *cb = cb_data;
+ const char *match = NULL, *target = NULL;
+ size_t len;
+
+ if (!prefixcmp(message, "checkout: moving from ")) {
+ match = message + strlen("checkout: moving from ");
+ if ((target = strstr(match, " to ")) != NULL)
+ target += 4;
+ }
+
+ if (!match)
+ return 0;
+
+ len = target - match - 4;
+ if (target[len] == '\n' && !strncmp(match, target, len))
+ return 0;
+
+ if (cb->counting) {
+ cb->nth++;
+ return 0;
+ }
+
+ if (cb->nth-- <= 0) {
+ strbuf_reset(cb->buf);
+ strbuf_add(cb->buf, match, len);
+ return 1;
+ }
+ 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 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;
+ struct grab_nth_branch_switch_cbdata cb;
+ const char *brace;
+ char *num_end;
+
+ if (name[0] != '@' || name[1] != '{' || name[2] != '-')
+ return -1;
+ brace = strchr(name, '}');
+ if (!brace)
+ return -1;
+ nth = strtol(name+3, &num_end, 10);
+ if (num_end != brace)
+ return -1;
+
+ cb.counting = 1;
+ cb.nth = 0;
+ cb.buf = buf;
+ for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
+
+ if (cb.nth < nth)
+ return 0;
+
+ cb.counting = 0;
+ cb.nth -= nth;
+ cb.buf = buf;
+ for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
+
+ return brace-name+1;
+}
+
/*
* This is like "get_sha1_basic()", except it allows "sha1 expressions",
* notably "xyz^" for "parent of xyz"