refspec: factor out parsing a single refspec

Factor out the logic which parses a single refspec into its own
function.  This makes it easier to reuse this logic in a future patch.

Signed-off-by: Brandon Williams <bmwill@google.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Brandon Williams 2018-05-16 15:57:50 -07:00 committed by Junio C Hamano
parent 0ad4a5ff50
commit 3eec3700fd

205
refspec.c
View File

@ -14,110 +14,123 @@ static struct refspec_item s_tag_refspec = {
/* See TAG_REFSPEC for the string version */
const struct refspec_item *tag_refspec = &s_tag_refspec;
/*
* Parses the provided refspec 'refspec' and populates the refspec_item 'item'.
* Returns 1 if successful and 0 if the refspec is invalid.
*/
static int parse_refspec(struct refspec_item *item, const char *refspec, int fetch)
{
size_t llen;
int is_glob;
const char *lhs, *rhs;
int flags;
is_glob = 0;
lhs = refspec;
if (*lhs == '+') {
item->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') {
item->matching = 1;
return 1;
}
if (rhs) {
size_t rlen = strlen(++rhs);
is_glob = (1 <= rlen && strchr(rhs, '*'));
item->dst = xstrndup(rhs, rlen);
}
llen = (rhs ? (rhs - lhs - 1) : strlen(lhs));
if (1 <= llen && memchr(lhs, '*', llen)) {
if ((rhs && !is_glob) || (!rhs && fetch))
return 0;
is_glob = 1;
} else if (rhs && is_glob) {
return 0;
}
item->pattern = is_glob;
item->src = xstrndup(lhs, llen);
flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);
if (fetch) {
struct object_id unused;
/* LHS */
if (!*item->src)
; /* empty is ok; it means "HEAD" */
else if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(item->src, &unused))
item->exact_sha1 = 1; /* ok */
else if (!check_refname_format(item->src, flags))
; /* valid looking ref is ok */
else
return 0;
/* RHS */
if (!item->dst)
; /* missing is ok; it is the same as empty */
else if (!*item->dst)
; /* empty is ok; it means "do not store" */
else if (!check_refname_format(item->dst, flags))
; /* valid looking ref is ok */
else
return 0;
} 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 (!*item->src)
; /* empty is ok */
else if (is_glob) {
if (check_refname_format(item->src, flags))
return 0;
}
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 (!item->dst) {
if (check_refname_format(item->src, flags))
return 0;
} else if (!*item->dst) {
return 0;
} else {
if (check_refname_format(item->dst, flags))
return 0;
}
}
return 1;
}
static struct refspec_item *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
{
int i;
struct refspec_item *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) {
if (!parse_refspec(&rs[i], refspec[i], fetch))
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: