Support '*' in the middle of a refspec
authorDaniel Barkalow <barkalow@iabervon.org>
Sat, 7 Mar 2009 06:11:39 +0000 (01:11 -0500)
committerJunio C Hamano <gitster@pobox.com>
Sat, 7 Mar 2009 20:19:28 +0000 (12:19 -0800)
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>
refs.c
remote.c
t/t5511-refspec.sh

diff --git a/refs.c b/refs.c
index a50ba79270d897d2f5c75bfc189ee3d1fd7f2b9b..fef7c9f26defb394cfa2ec1e5f77a99f78bc834b 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -694,6 +694,7 @@ static inline int bad_ref_char(int ch)
 int check_ref_format(const char *ref)
 {
        int ch, level, bad_type;
+       int ret = CHECK_REF_FORMAT_OK;
        const char *cp = ref;
 
        level = 0;
@@ -709,9 +710,11 @@ int check_ref_format(const char *ref)
                        return CHECK_REF_FORMAT_ERROR;
                bad_type = bad_ref_char(ch);
                if (bad_type) {
-                       return (bad_type == 2 && !*cp)
-                               ? CHECK_REF_FORMAT_WILDCARD
-                               : CHECK_REF_FORMAT_ERROR;
+                       if (bad_type == 2 && (!*cp || *cp == '/') &&
+                           ret == CHECK_REF_FORMAT_OK)
+                               ret = CHECK_REF_FORMAT_WILDCARD;
+                       else
+                               return CHECK_REF_FORMAT_ERROR;
                }
 
                /* scan the rest of the path component */
@@ -729,7 +732,7 @@ int check_ref_format(const char *ref)
                if (!ch) {
                        if (level < 2)
                                return CHECK_REF_FORMAT_ONELEVEL;
-                       return CHECK_REF_FORMAT_OK;
+                       return ret;
                }
        }
 }
index d596a4865189c1219692ed2181d629ca838c2961..90203e2fe1dea5462a618fe51fe119fdb75f3dbd 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -511,12 +511,12 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
 
                if (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);
                }
 
                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))
                                goto invalid;
                        is_glob = 1;
@@ -718,22 +718,32 @@ static int match_name_with_pattern(const char *key, const char *name,
 {
        const char *kstar = strchr(key, '*');
        size_t klen;
+       size_t ksuffixlen;
+       size_t namelen;
        int ret;
        if (!kstar)
                die("Key '%s' of pattern had no '*'", 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) {
                const char *vstar = strchr(value, '*');
                size_t vlen;
+               size_t vsuffixlen;
                if (!vstar)
                        die("Value '%s' of pattern has no '*'", value);
                vlen = vstar - value;
-               *result = xmalloc(vlen +
+               vsuffixlen = strlen(vstar + 1);
+               *result = xmalloc(vlen + vsuffixlen +
                                  strlen(name) -
-                                 klen + 1);
-               strcpy(*result, value);
-               strcpy(*result + vlen, name + klen);
+                                 klen - ksuffixlen + 1);
+               strncpy(*result, value, vlen);
+               strncpy(*result + vlen,
+                       name + klen, namelen - klen - ksuffixlen);
+               strcpy(*result + vlen + namelen - klen - ksuffixlen,
+                      vstar + 1);
        }
        return ret;
 }
index 22ba380034775e7584a33ca606294af34f568443..c28932216b1dd0893bc3c3a0a643963663ff8e1f 100755 (executable)
@@ -72,4 +72,16 @@ test_refspec fetch ':refs/remotes/frotz/HEAD-to-me'
 test_refspec push ':refs/remotes/frotz/delete 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