From ec0cb496553ac82f97205a415ca77618406b30e3 Mon Sep 17 00:00:00 2001 From: Brandon Williams Date: Wed, 16 May 2018 15:57:48 -0700 Subject: refspec: move refspec parsing logic into its own file In preparation for performing a refactor on refspec related code, move the refspec parsing logic into its own file. Signed-off-by: Brandon Williams Signed-off-by: Junio C Hamano --- refspec.c | 167 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 167 insertions(+) create mode 100644 refspec.c (limited to 'refspec.c') diff --git a/refspec.c b/refspec.c new file mode 100644 index 0000000000..50892f864c --- /dev/null +++ b/refspec.c @@ -0,0 +1,167 @@ +#include "cache.h" +#include "refs.h" +#include "refspec.h" + +static struct refspec s_tag_refspec = { + 0, + 1, + 0, + 0, + "refs/tags/*", + "refs/tags/*" +}; + +/* See TAG_REFSPEC for the string version */ +const struct refspec *tag_refspec = &s_tag_refspec; + +static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify) +{ + int i; + struct refspec *rs = xcalloc(nr_refspec, sizeof(*rs)); + + for (i = 0; i < nr_refspec; i++) { + size_t llen; + int is_glob; + const char *lhs, *rhs; + int flags; + + is_glob = 0; + + lhs = refspec[i]; + if (*lhs == '+') { + rs[i].force = 1; + lhs++; + } + + rhs = strrchr(lhs, ':'); + + /* + * Before going on, special case ":" (or "+:") as a refspec + * for pushing matching refs. + */ + if (!fetch && rhs == lhs && rhs[1] == '\0') { + rs[i].matching = 1; + continue; + } + + if (rhs) { + size_t rlen = strlen(++rhs); + is_glob = (1 <= rlen && strchr(rhs, '*')); + rs[i].dst = xstrndup(rhs, rlen); + } + + llen = (rhs ? (rhs - lhs - 1) : strlen(lhs)); + if (1 <= llen && memchr(lhs, '*', llen)) { + if ((rhs && !is_glob) || (!rhs && fetch)) + goto invalid; + is_glob = 1; + } else if (rhs && is_glob) { + goto invalid; + } + + rs[i].pattern = is_glob; + rs[i].src = xstrndup(lhs, llen); + flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0); + + if (fetch) { + struct object_id unused; + + /* LHS */ + if (!*rs[i].src) + ; /* empty is ok; it means "HEAD" */ + else if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(rs[i].src, &unused)) + rs[i].exact_sha1 = 1; /* ok */ + else if (!check_refname_format(rs[i].src, flags)) + ; /* valid looking ref is ok */ + else + goto invalid; + /* RHS */ + if (!rs[i].dst) + ; /* missing is ok; it is the same as empty */ + else if (!*rs[i].dst) + ; /* empty is ok; it means "do not store" */ + else if (!check_refname_format(rs[i].dst, flags)) + ; /* valid looking ref is ok */ + else + goto invalid; + } else { + /* + * LHS + * - empty is allowed; it means delete. + * - when wildcarded, it must be a valid looking ref. + * - otherwise, it must be an extended SHA-1, but + * there is no existing way to validate this. + */ + if (!*rs[i].src) + ; /* empty is ok */ + else if (is_glob) { + if (check_refname_format(rs[i].src, flags)) + goto invalid; + } + else + ; /* anything goes, for now */ + /* + * RHS + * - missing is allowed, but LHS then must be a + * valid looking ref. + * - empty is not allowed. + * - otherwise it must be a valid looking ref. + */ + if (!rs[i].dst) { + if (check_refname_format(rs[i].src, flags)) + goto invalid; + } else if (!*rs[i].dst) { + goto invalid; + } else { + if (check_refname_format(rs[i].dst, flags)) + goto invalid; + } + } + } + return rs; + + invalid: + if (verify) { + /* + * nr_refspec must be greater than zero and i must be valid + * since it is only possible to reach this point from within + * the for loop above. + */ + free_refspec(i+1, rs); + return NULL; + } + die("Invalid refspec '%s'", refspec[i]); +} + +int valid_fetch_refspec(const char *fetch_refspec_str) +{ + struct refspec *refspec; + + refspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1); + free_refspec(1, refspec); + return !!refspec; +} + +struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec) +{ + return parse_refspec_internal(nr_refspec, refspec, 1, 0); +} + +struct refspec *parse_push_refspec(int nr_refspec, const char **refspec) +{ + return parse_refspec_internal(nr_refspec, refspec, 0, 0); +} + +void free_refspec(int nr_refspec, struct refspec *refspec) +{ + int i; + + if (!refspec) + return; + + for (i = 0; i < nr_refspec; i++) { + free(refspec[i].src); + free(refspec[i].dst); + } + free(refspec); +} -- cgit v1.2.3