From: Junio C Hamano <gitster@xxxxxxxxx> Implement a shortcut @{-N} for the N-th last branch checked out, that works by parsing the reflog for the message added by previous git-checkout invocations. We expand the @{-N} to the branch name, so that you end up on an attached HEAD on that branch. --- builtin-checkout.c | 10 +++++- cache.h | 1 + sha1_name.c | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 87 insertions(+), 2 deletions(-) diff --git a/builtin-checkout.c b/builtin-checkout.c index b5dd9c0..a3b69d6 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -361,8 +361,14 @@ struct branch_info { static void setup_branch_path(struct branch_info *branch) { struct strbuf buf = STRBUF_INIT; - strbuf_addstr(&buf, "refs/heads/"); - strbuf_addstr(&buf, branch->name); + + if (!interpret_nth_last_branch(branch->name, &buf)) { + branch->name = xstrdup(buf.buf); + strbuf_splice(&buf, 0, 0, "refs/heads/", 11); + } else { + strbuf_addstr(&buf, "refs/heads/"); + strbuf_addstr(&buf, branch->name); + } branch->path = strbuf_detach(&buf, NULL); } diff --git a/cache.h b/cache.h index 8e1af26..0dd9168 100644 --- a/cache.h +++ b/cache.h @@ -663,6 +663,7 @@ static inline unsigned int hexval(unsigned char c) extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *); extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref); extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); +extern int interpret_nth_last_branch(const char *str, struct strbuf *); extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules); extern const char *ref_rev_parse_rules[]; diff --git a/sha1_name.c b/sha1_name.c index 159c2ab..6377264 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -674,6 +674,84 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1) 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; + + 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; + } + } + + if (!match) + return 0; + + if (cb->counting) { + cb->nth++; + 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; + } + 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. + * + * If the input is not of the accepted format, it returns a negative + * number to signal an error. + */ +int interpret_nth_last_branch(const char *name, struct strbuf *buf) +{ + int nth, i; + struct grab_nth_branch_switch_cbdata cb; + + 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) + return -1; + + 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; +} + /* * This is like "get_sha1_basic()", except it allows "sha1 expressions", * notably "xyz^" for "parent of xyz" -- 1.6.1.315.g92577 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html