Support '*' in the middle of a refspec

In order to keep the requirements strict, each * has to be a full path
component, and there may only be one * per side. This requirement is
enforced entirely by check_ref_format(); the matching implementation
will substitute the whatever matches the * in the lhs for the * in the
rhs.

Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
maint
Daniel Barkalow 2009-03-07 01:11:39 -05:00 committed by Junio C Hamano
parent 08fbdb3043
commit abd2bde78b
3 changed files with 36 additions and 11 deletions

11
refs.c
View File

@ -694,6 +694,7 @@ static inline int bad_ref_char(int ch)
int check_ref_format(const char *ref) int check_ref_format(const char *ref)
{ {
int ch, level, bad_type; int ch, level, bad_type;
int ret = CHECK_REF_FORMAT_OK;
const char *cp = ref; const char *cp = ref;


level = 0; level = 0;
@ -709,9 +710,11 @@ int check_ref_format(const char *ref)
return CHECK_REF_FORMAT_ERROR; return CHECK_REF_FORMAT_ERROR;
bad_type = bad_ref_char(ch); bad_type = bad_ref_char(ch);
if (bad_type) { if (bad_type) {
return (bad_type == 2 && !*cp) if (bad_type == 2 && (!*cp || *cp == '/') &&
? CHECK_REF_FORMAT_WILDCARD ret == CHECK_REF_FORMAT_OK)
: CHECK_REF_FORMAT_ERROR; ret = CHECK_REF_FORMAT_WILDCARD;
else
return CHECK_REF_FORMAT_ERROR;
} }


/* scan the rest of the path component */ /* scan the rest of the path component */
@ -729,7 +732,7 @@ int check_ref_format(const char *ref)
if (!ch) { if (!ch) {
if (level < 2) if (level < 2)
return CHECK_REF_FORMAT_ONELEVEL; return CHECK_REF_FORMAT_ONELEVEL;
return CHECK_REF_FORMAT_OK; return ret;
} }
} }
} }

View File

@ -511,12 +511,12 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp


if (rhs) { if (rhs) {
size_t rlen = strlen(++rhs); size_t rlen = strlen(++rhs);
is_glob = (2 <= rlen && !strcmp(rhs + rlen - 2, "/*")); is_glob = (1 <= rlen && strchr(rhs, '*'));
rs[i].dst = xstrndup(rhs, rlen); rs[i].dst = xstrndup(rhs, rlen);
} }


llen = (rhs ? (rhs - lhs - 1) : strlen(lhs)); llen = (rhs ? (rhs - lhs - 1) : strlen(lhs));
if (2 <= llen && !memcmp(lhs + llen - 2, "/*", 2)) { if (1 <= llen && memchr(lhs, '*', llen)) {
if ((rhs && !is_glob) || (!rhs && fetch)) if ((rhs && !is_glob) || (!rhs && fetch))
goto invalid; goto invalid;
is_glob = 1; is_glob = 1;
@ -718,22 +718,32 @@ static int match_name_with_pattern(const char *key, const char *name,
{ {
const char *kstar = strchr(key, '*'); const char *kstar = strchr(key, '*');
size_t klen; size_t klen;
size_t ksuffixlen;
size_t namelen;
int ret; int ret;
if (!kstar) if (!kstar)
die("Key '%s' of pattern had no '*'", key); die("Key '%s' of pattern had no '*'", key);
klen = kstar - key; klen = kstar - key;
ret = !strncmp(key, name, klen); ksuffixlen = strlen(kstar + 1);
namelen = strlen(name);
ret = !strncmp(name, key, klen) && namelen >= klen + ksuffixlen &&
!memcmp(name + namelen - ksuffixlen, kstar + 1, ksuffixlen);
if (ret && value) { if (ret && value) {
const char *vstar = strchr(value, '*'); const char *vstar = strchr(value, '*');
size_t vlen; size_t vlen;
size_t vsuffixlen;
if (!vstar) if (!vstar)
die("Value '%s' of pattern has no '*'", value); die("Value '%s' of pattern has no '*'", value);
vlen = vstar - value; vlen = vstar - value;
*result = xmalloc(vlen + vsuffixlen = strlen(vstar + 1);
*result = xmalloc(vlen + vsuffixlen +
strlen(name) - strlen(name) -
klen + 1); klen - ksuffixlen + 1);
strcpy(*result, value); strncpy(*result, value, vlen);
strcpy(*result + vlen, name + klen); strncpy(*result + vlen,
name + klen, namelen - klen - ksuffixlen);
strcpy(*result + vlen + namelen - klen - ksuffixlen,
vstar + 1);
} }
return ret; return ret;
} }

View File

@ -72,4 +72,16 @@ test_refspec fetch ':refs/remotes/frotz/HEAD-to-me'
test_refspec push ':refs/remotes/frotz/delete me' invalid test_refspec push ':refs/remotes/frotz/delete me' invalid
test_refspec fetch ':refs/remotes/frotz/HEAD to me' invalid test_refspec fetch ':refs/remotes/frotz/HEAD to me' invalid


test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' invalid
test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*-blah' invalid

test_refspec fetch 'refs/heads*/for-linus:refs/remotes/mine/*' invalid
test_refspec push 'refs/heads*/for-linus:refs/remotes/mine/*' invalid

test_refspec fetch 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid
test_refspec push 'refs/heads/*/*/for-linus:refs/remotes/mine/*' invalid

test_refspec fetch 'refs/heads/*/for-linus:refs/remotes/mine/*'
test_refspec push 'refs/heads/*/for-linus:refs/remotes/mine/*'

test_done test_done