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 <gitster@pobox.com>
Junio C Hamano committed
Jan 17, 2009 at 17:09 UTC
ae5a6c3684c378bc32c1f6ecc0e6dc45300c14c1
3 files changed
+87
-2
builtin-checkout.c
+8
-2
@@ -361,8 +361,14 @@ struct branch_info {
361
static void setup_branch_path(struct branch_info *branch)
362
{
363
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
+ }
372
branch->path = strbuf_detach(&buf, NULL);
373
}
374
cache.h
+1
@@ -663,6 +663,7 @@ extern int read_ref(const char *filename, unsigned char *sha1);
663
extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);
664
extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref);
665
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 *);
667
668
extern int refname_match(const char *abbrev_name, const char *full_name, const char **rules);
669
extern const char *ref_rev_parse_rules[];
sha1_name.c
+78
@@ -674,6 +674,84 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1)
674
return retval;
675
}
676
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
+
755
/*
756
* This is like "get_sha1_basic()", except it allows "sha1 expressions",
757
* notably "xyz^" for "parent of xyz"