diff --git a/Makefile b/Makefile index ad880d1fc5..4bca65383a 100644 --- a/Makefile +++ b/Makefile @@ -928,6 +928,7 @@ LIB_OBJS += refs/files-backend.o LIB_OBJS += refs/iterator.o LIB_OBJS += refs/packed-backend.o LIB_OBJS += refs/ref-cache.o +LIB_OBJS += refspec.o LIB_OBJS += ref-filter.o LIB_OBJS += remote.o LIB_OBJS += replace-object.o diff --git a/branch.c b/branch.c index 2672054f0b..32ccefc6b5 100644 --- a/branch.c +++ b/branch.c @@ -3,6 +3,7 @@ #include "config.h" #include "branch.h" #include "refs.h" +#include "refspec.h" #include "remote.h" #include "commit.h" #include "worktree.h" diff --git a/builtin/clone.c b/builtin/clone.c index 84f1473d19..6d1614ed37 100644 --- a/builtin/clone.c +++ b/builtin/clone.c @@ -14,6 +14,7 @@ #include "parse-options.h" #include "fetch-pack.h" #include "refs.h" +#include "refspec.h" #include "tree.h" #include "tree-walk.h" #include "unpack-trees.h" diff --git a/builtin/fast-export.c b/builtin/fast-export.c index 530df12f05..a13b7c8ef3 100644 --- a/builtin/fast-export.c +++ b/builtin/fast-export.c @@ -7,6 +7,7 @@ #include "cache.h" #include "config.h" #include "refs.h" +#include "refspec.h" #include "commit.h" #include "object.h" #include "tag.h" diff --git a/builtin/fetch.c b/builtin/fetch.c index 7ee83ac0f8..1fce68e9ac 100644 --- a/builtin/fetch.c +++ b/builtin/fetch.c @@ -5,6 +5,7 @@ #include "config.h" #include "repository.h" #include "refs.h" +#include "refspec.h" #include "commit.h" #include "builtin.h" #include "string-list.h" diff --git a/builtin/merge.c b/builtin/merge.c index 9db5a2cf16..c362cfe90d 100644 --- a/builtin/merge.c +++ b/builtin/merge.c @@ -14,6 +14,7 @@ #include "run-command.h" #include "diff.h" #include "refs.h" +#include "refspec.h" #include "commit.h" #include "diffcore.h" #include "revision.h" diff --git a/builtin/pull.c b/builtin/pull.c index 71aac5005e..6247c956d7 100644 --- a/builtin/pull.c +++ b/builtin/pull.c @@ -15,6 +15,7 @@ #include "remote.h" #include "dir.h" #include "refs.h" +#include "refspec.h" #include "revision.h" #include "submodule.h" #include "submodule-config.h" diff --git a/builtin/push.c b/builtin/push.c index ac3705370e..fa65999b27 100644 --- a/builtin/push.c +++ b/builtin/push.c @@ -4,6 +4,7 @@ #include "cache.h" #include "config.h" #include "refs.h" +#include "refspec.h" #include "run-command.h" #include "builtin.h" #include "remote.h" diff --git a/builtin/remote.c b/builtin/remote.c index 8708e584e9..c495139954 100644 --- a/builtin/remote.c +++ b/builtin/remote.c @@ -7,6 +7,7 @@ #include "strbuf.h" #include "run-command.h" #include "refs.h" +#include "refspec.h" #include "argv-array.h" static const char * const builtin_remote_usage[] = { diff --git a/builtin/submodule--helper.c b/builtin/submodule--helper.c index c2403a915f..6ab032acb1 100644 --- a/builtin/submodule--helper.c +++ b/builtin/submodule--helper.c @@ -12,6 +12,7 @@ #include "run-command.h" #include "remote.h" #include "refs.h" +#include "refspec.h" #include "connect.h" #include "revision.h" #include "diffcore.h" diff --git a/checkout.c b/checkout.c index ac42630f74..193ba85675 100644 --- a/checkout.c +++ b/checkout.c @@ -1,5 +1,6 @@ #include "cache.h" #include "remote.h" +#include "refspec.h" #include "checkout.h" struct tracking_name_data { 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); +} diff --git a/refspec.h b/refspec.h new file mode 100644 index 0000000000..62625c23a3 --- /dev/null +++ b/refspec.h @@ -0,0 +1,23 @@ +#ifndef REFSPEC_H +#define REFSPEC_H + +#define TAG_REFSPEC "refs/tags/*:refs/tags/*" +extern const struct refspec *tag_refspec; + +struct refspec { + unsigned force : 1; + unsigned pattern : 1; + unsigned matching : 1; + unsigned exact_sha1 : 1; + + char *src; + char *dst; +}; + +int valid_fetch_refspec(const char *refspec); +struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec); +struct refspec *parse_push_refspec(int nr_refspec, const char **refspec); + +void free_refspec(int nr_refspec, struct refspec *refspec); + +#endif /* REFSPEC_H */ diff --git a/remote.c b/remote.c index 91eb010ca9..4d67c061a0 100644 --- a/remote.c +++ b/remote.c @@ -2,6 +2,7 @@ #include "config.h" #include "remote.h" #include "refs.h" +#include "refspec.h" #include "commit.h" #include "diff.h" #include "revision.h" @@ -13,18 +14,6 @@ enum map_direction { FROM_SRC, FROM_DST }; -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; - struct counted_string { size_t len; const char *s; @@ -499,158 +488,6 @@ static void read_config(void) alias_all_urls(); } -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); -} - static int valid_remote_nick(const char *name) { if (!name[0] || is_dot_or_dotdot(name)) diff --git a/remote.h b/remote.h index 2b3180f94d..386ced9012 100644 --- a/remote.h +++ b/remote.h @@ -68,18 +68,6 @@ int for_each_remote(each_remote_fn fn, void *priv); int remote_has_url(struct remote *remote, const char *url); -struct refspec { - unsigned force : 1; - unsigned pattern : 1; - unsigned matching : 1; - unsigned exact_sha1 : 1; - - char *src; - char *dst; -}; - -extern const struct refspec *tag_refspec; - struct ref { struct ref *next; struct object_id old_oid; @@ -175,12 +163,6 @@ int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid); */ struct ref *ref_remove_duplicates(struct ref *ref_map); -int valid_fetch_refspec(const char *refspec); -struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec); -extern struct refspec *parse_push_refspec(int nr_refspec, const char **refspec); - -void free_refspec(int nr_refspec, struct refspec *refspec); - extern int query_refspecs(struct refspec *specs, int nr, struct refspec *query); char *apply_refspecs(struct refspec *refspecs, int nr_refspec, const char *name); @@ -313,8 +295,6 @@ extern int parseopt_push_cas_option(const struct option *, const char *arg, int extern int is_empty_cas(const struct push_cas_option *); void apply_push_cas(struct push_cas_option *, struct remote *, struct ref *); -#define TAG_REFSPEC "refs/tags/*:refs/tags/*" - void add_prune_tags_to_fetch_refspec(struct remote *remote); #endif diff --git a/transport-helper.c b/transport-helper.c index 11f1055b47..b99e1cce9e 100644 --- a/transport-helper.c +++ b/transport-helper.c @@ -11,6 +11,7 @@ #include "sigchain.h" #include "argv-array.h" #include "refs.h" +#include "refspec.h" #include "transport-internal.h" #include "protocol.h" diff --git a/transport.c b/transport.c index 37410d8aad..2cf63d18b7 100644 --- a/transport.c +++ b/transport.c @@ -11,6 +11,7 @@ #include "bundle.h" #include "dir.h" #include "refs.h" +#include "refspec.h" #include "branch.h" #include "url.h" #include "submodule.h"