@@ -431,6 +431,7 @@ static inline int upstream_mark(const char *string, int len)
431431}
432432
433433static int get_sha1_1 (const char * name , int len , unsigned char * sha1 , unsigned lookup_flags );
434+ static int interpret_nth_prior_checkout (const char * name , struct strbuf * buf );
434435
435436static int get_sha1_basic (const char * str , int len , unsigned char * sha1 )
436437{
@@ -448,7 +449,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
448449 unsigned char tmp_sha1 [20 ];
449450 char * real_ref = NULL ;
450451 int refs_found = 0 ;
451- int at , reflog_len ;
452+ int at , reflog_len , nth_prior = 0 ;
452453
453454 if (len == 40 && !get_sha1_hex (str , sha1 )) {
454455 refs_found = dwim_ref (str , len , tmp_sha1 , & real_ref );
@@ -464,8 +465,15 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
464465 /* basic@{time or number or -number} format to query ref-log */
465466 reflog_len = at = 0 ;
466467 if (len && str [len - 1 ] == '}' ) {
467- for (at = len - 2 ; at >= 0 ; at -- ) {
468+ for (at = len - 4 ; at >= 0 ; at -- ) {
468469 if (str [at ] == '@' && str [at + 1 ] == '{' ) {
470+ if (str [at + 2 ] == '-' ) {
471+ if (at != 0 )
472+ /* @{-N} not at start */
473+ return -1 ;
474+ nth_prior = 1 ;
475+ continue ;
476+ }
469477 if (!upstream_mark (str + at , len - at )) {
470478 reflog_len = (len - 1 ) - (at + 2 );
471479 len = at ;
@@ -479,20 +487,22 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
479487 if (len && ambiguous_path (str , len ))
480488 return -1 ;
481489
482- if (! len && reflog_len ) {
490+ if (nth_prior ) {
483491 struct strbuf buf = STRBUF_INIT ;
484- int ret ;
485- /* try the @{-N} syntax for n-th checkout */
486- ret = interpret_branch_name (str + at , & buf );
487- if (ret > 0 ) {
488- /* substitute this branch name and restart */
489- return get_sha1_1 (buf .buf , buf .len , sha1 , 0 );
490- } else if (ret == 0 ) {
491- return -1 ;
492+ int detached ;
493+
494+ if (interpret_nth_prior_checkout (str , & buf ) > 0 ) {
495+ detached = (buf .len == 40 && !get_sha1_hex (buf .buf , sha1 ));
496+ strbuf_release (& buf );
497+ if (detached )
498+ return 0 ;
492499 }
500+ }
501+
502+ if (!len && reflog_len )
493503 /* allow "@{...}" to mean the current branch reflog */
494504 refs_found = dwim_ref ("HEAD" , 4 , sha1 , & real_ref );
495- } else if (reflog_len )
505+ else if (reflog_len )
496506 refs_found = dwim_log (str , len , sha1 , & real_ref );
497507 else
498508 refs_found = dwim_ref (str , len , sha1 , & real_ref );
@@ -511,10 +521,6 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
511521 unsigned long co_time ;
512522 int co_tz , co_cnt ;
513523
514- /* a @{-N} placed anywhere except the start is an error */
515- if (str [at + 2 ] == '-' )
516- return -1 ;
517-
518524 /* Is it asking for N-th entry, or approxidate? */
519525 for (i = nth = 0 ; 0 <= nth && i < reflog_len ; i ++ ) {
520526 char ch = str [at + 2 + i ];
@@ -996,6 +1002,38 @@ int get_sha1_mb(const char *name, unsigned char *sha1)
9961002 return st ;
9971003}
9981004
1005+ /* parse @something syntax, when 'something' is not {.*} */
1006+ static int interpret_empty_at (const char * name , int namelen , int len , struct strbuf * buf )
1007+ {
1008+ if (len || name [1 ] == '{' )
1009+ return -1 ;
1010+
1011+ strbuf_reset (buf );
1012+ strbuf_add (buf , "HEAD" , 4 );
1013+ return 1 ;
1014+ }
1015+
1016+ static int reinterpret (const char * name , int namelen , int len , struct strbuf * buf )
1017+ {
1018+ /* we have extra data, which might need further processing */
1019+ struct strbuf tmp = STRBUF_INIT ;
1020+ int used = buf -> len ;
1021+ int ret ;
1022+
1023+ strbuf_add (buf , name + len , namelen - len );
1024+ ret = interpret_branch_name (buf -> buf , & tmp );
1025+ /* that data was not interpreted, remove our cruft */
1026+ if (ret < 0 ) {
1027+ strbuf_setlen (buf , used );
1028+ return len ;
1029+ }
1030+ strbuf_reset (buf );
1031+ strbuf_addbuf (buf , & tmp );
1032+ strbuf_release (& tmp );
1033+ /* tweak for size of {-N} versus expanded ref name */
1034+ return ret - used + len ;
1035+ }
1036+
9991037/*
10001038 * This reads short-hand syntax that not only evaluates to a commit
10011039 * object name, but also can act as if the end user spelled the name
@@ -1025,36 +1063,27 @@ int interpret_branch_name(const char *name, struct strbuf *buf)
10251063 int len = interpret_nth_prior_checkout (name , buf );
10261064 int tmp_len ;
10271065
1028- if (!len )
1066+ if (!len ) {
10291067 return len ; /* syntax Ok, not enough switches */
1030- if (0 < len && len == namelen )
1031- return len ; /* consumed all */
1032- else if (0 < len ) {
1033- /* we have extra data, which might need further processing */
1034- struct strbuf tmp = STRBUF_INIT ;
1035- int used = buf -> len ;
1036- int ret ;
1037-
1038- strbuf_add (buf , name + len , namelen - len );
1039- ret = interpret_branch_name (buf -> buf , & tmp );
1040- /* that data was not interpreted, remove our cruft */
1041- if (ret < 0 ) {
1042- strbuf_setlen (buf , used );
1043- return len ;
1044- }
1045- strbuf_reset (buf );
1046- strbuf_addbuf (buf , & tmp );
1047- strbuf_release (& tmp );
1048- /* tweak for size of {-N} versus expanded ref name */
1049- return ret - used + len ;
1068+ } else if (len > 0 ) {
1069+ if (len == namelen )
1070+ return len ; /* consumed all */
1071+ else
1072+ return reinterpret (name , namelen , len , buf );
10501073 }
10511074
10521075 cp = strchr (name , '@' );
10531076 if (!cp )
10541077 return -1 ;
1078+
1079+ len = interpret_empty_at (name , namelen , cp - name , buf );
1080+ if (len > 0 )
1081+ return reinterpret (name , namelen , len , buf );
1082+
10551083 tmp_len = upstream_mark (cp , namelen - (cp - name ));
10561084 if (!tmp_len )
10571085 return -1 ;
1086+
10581087 len = cp + tmp_len - name ;
10591088 cp = xstrndup (name , cp - name );
10601089 upstream = branch_get (* cp ? cp : NULL );
0 commit comments