Skip to content

Commit ae5a6c3

Browse files
committed
checkout: implement "@{-N}" shortcut name for N-th last branch
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. Signed-off-by: Junio C Hamano <[email protected]>
1 parent 7bbd8d6 commit ae5a6c3

File tree

3 files changed

+87
-2
lines changed

3 files changed

+87
-2
lines changed

builtin-checkout.c

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,8 +361,14 @@ struct branch_info {
361361
static void setup_branch_path(struct branch_info *branch)
362362
{
363363
struct strbuf buf = STRBUF_INIT;
364-
strbuf_addstr(&buf, "refs/heads/");
365-
strbuf_addstr(&buf, branch->name);
364+
365+
if (!interpret_nth_last_branch(branch->name, &buf)) {
366+
branch->name = xstrdup(buf.buf);
367+
strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
368+
} else {
369+
strbuf_addstr(&buf, "refs/heads/");
370+
strbuf_addstr(&buf, branch->name);
371+
}
366372
branch->path = strbuf_detach(&buf, NULL);
367373
}
368374

cache.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -663,6 +663,7 @@ extern int read_ref(const char *filename, unsigned char *sha1);
663663
extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
664664
extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
665665
extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref);
666+
extern int interpret_nth_last_branch(const char *str, struct strbuf *);
666667

667668
extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
668669
extern const char *ref_rev_parse_rules[];

sha1_name.c

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -674,6 +674,84 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
674674
return retval;
675675
}
676676

677+
struct grab_nth_branch_switch_cbdata {
678+
int counting;
679+
int nth;
680+
struct strbuf *buf;
681+
};
682+
683+
static int grab_nth_branch_switch(unsigned char *osha1, unsigned char *nsha1,
684+
const char *email, unsigned long timestamp, int tz,
685+
const char *message, void *cb_data)
686+
{
687+
struct grab_nth_branch_switch_cbdata *cb = cb_data;
688+
const char *match = NULL;
689+
690+
if (!prefixcmp(message, "checkout: moving to "))
691+
match = message + strlen("checkout: moving to ");
692+
else if (!prefixcmp(message, "checkout: moving from ")) {
693+
const char *cp = message + strlen("checkout: moving from ");
694+
if ((cp = strstr(cp, " to ")) != NULL) {
695+
match = cp + 4;
696+
}
697+
}
698+
699+
if (!match)
700+
return 0;
701+
702+
if (cb->counting) {
703+
cb->nth++;
704+
return 0;
705+
}
706+
707+
if (--cb->nth <= 0) {
708+
size_t len = strlen(match);
709+
while (match[len-1] == '\n')
710+
len--;
711+
strbuf_reset(cb->buf);
712+
strbuf_add(cb->buf, match, len);
713+
return 1;
714+
}
715+
return 0;
716+
}
717+
718+
/*
719+
* This reads "@{-N}" syntax, finds the name of the Nth previous
720+
* branch we were on, and places the name of the branch in the given
721+
* buf and returns 0 if successful.
722+
*
723+
* If the input is not of the accepted format, it returns a negative
724+
* number to signal an error.
725+
*/
726+
int interpret_nth_last_branch(const char *name, struct strbuf *buf)
727+
{
728+
int nth, i;
729+
struct grab_nth_branch_switch_cbdata cb;
730+
731+
if (name[0] != '@' || name[1] != '{' || name[2] != '-')
732+
return -1;
733+
for (i = 3, nth = 0; name[i] && name[i] != '}'; i++) {
734+
char ch = name[i];
735+
if ('0' <= ch && ch <= '9')
736+
nth = nth * 10 + ch - '0';
737+
else
738+
return -1;
739+
}
740+
if (nth < 0 || 10 <= nth)
741+
return -1;
742+
743+
cb.counting = 1;
744+
cb.nth = 0;
745+
cb.buf = buf;
746+
for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
747+
748+
cb.counting = 0;
749+
cb.nth -= nth;
750+
cb.buf = buf;
751+
for_each_reflog_ent("HEAD", grab_nth_branch_switch, &cb);
752+
return 0;
753+
}
754+
677755
/*
678756
* This is like "get_sha1_basic()", except it allows "sha1 expressions",
679757
* notably "xyz^" for "parent of xyz"

0 commit comments

Comments
 (0)