summary | shortlog | log | commit | commitdiff | tree
raw | patch | inline | side by side (parent: 71a5099)
raw | patch | inline | side by side (parent: 71a5099)
author | Daniel Barkalow <barkalow@iabervon.org> | |
Tue, 18 Mar 2008 02:05:23 +0000 (22:05 -0400) | ||
committer | Junio C Hamano <gitster@pobox.com> | |
Wed, 26 Mar 2008 07:10:55 +0000 (00:10 -0700) |
This changes the pattern matching code to not store the required final
/ before the *, and then to require each side to be a valid ref (or
empty). In particular, any refspec that looks like it should be a
pattern but doesn't quite meet the requirements will be found to be
invalid as a fallback non-pattern.
This was cherry picked from commit ef00d15 (Tighten refspec processing,
2008-03-17), and two fix-up commits 46220ca (remote.c: Fix overtight
refspec validation, 2008-03-20) and 7d19da4 (refspec: allow colon-less
wildcard "refs/category/*", 2008-03-25) squashed in.
Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
/ before the *, and then to require each side to be a valid ref (or
empty). In particular, any refspec that looks like it should be a
pattern but doesn't quite meet the requirements will be found to be
invalid as a fallback non-pattern.
This was cherry picked from commit ef00d15 (Tighten refspec processing,
2008-03-17), and two fix-up commits 46220ca (remote.c: Fix overtight
refspec validation, 2008-03-20) and 7d19da4 (refspec: allow colon-less
wildcard "refs/category/*", 2008-03-25) squashed in.
Signed-off-by: Daniel Barkalow <barkalow@iabervon.org>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
builtin-fetch.c | patch | blob | history | |
builtin-send-pack.c | patch | blob | history | |
remote.c | patch | blob | history | |
remote.h | patch | blob | history | |
t/t5511-refspec.sh | [new file with mode: 0755] | patch | blob |
diff --git a/builtin-fetch.c b/builtin-fetch.c
index 9a6ddcec9263592462bbec891011b50fb2188330..6fd006f48c6d28d3826b441d2dfb5522a2ed2c62 100644 (file)
--- a/builtin-fetch.c
+++ b/builtin-fetch.c
signal(SIGINT, unlock_pack_on_signal);
atexit(unlock_pack);
- return do_fetch(transport, parse_ref_spec(ref_nr, refs), ref_nr);
+ return do_fetch(transport,
+ parse_fetch_refspec(ref_nr, refs), ref_nr);
}
diff --git a/builtin-send-pack.c b/builtin-send-pack.c
index 8afb1d0bca0635dc22f658455477d9fada231290..98c54d9e2859300879926a9fbc3b4193e553e2cf 100644 (file)
--- a/builtin-send-pack.c
+++ b/builtin-send-pack.c
int i;
for (i = 0; i < nr_heads; i++) {
- const char *remote = strchr(heads[i], ':');
+ const char *remote = strrchr(heads[i], ':');
remote = remote ? (remote + 1) : heads[i];
switch (check_ref_format(remote)) {
diff --git a/remote.c b/remote.c
index 6b56473f5bb7d8ab2226ed83805f30c9e21ba773..95d65a07ef3b2a1076936e47bb98d5b6095c95e7 100644 (file)
--- a/remote.c
+++ b/remote.c
git_config(handle_config);
}
-struct refspec *parse_ref_spec(int nr_refspec, const char **refspec)
+static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch)
{
int i;
+ int st;
struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec);
+
for (i = 0; i < nr_refspec; i++) {
- const char *sp, *ep, *gp;
- sp = refspec[i];
- if (*sp == '+') {
+ size_t llen, rlen;
+ int is_glob;
+ const char *lhs, *rhs;
+
+ llen = rlen = is_glob = 0;
+
+ lhs = refspec[i];
+ if (*lhs == '+') {
rs[i].force = 1;
- sp++;
+ lhs++;
+ }
+
+ rhs = strrchr(lhs, ':');
+ if (rhs) {
+ rhs++;
+ rlen = strlen(rhs);
+ is_glob = (2 <= rlen && !strcmp(rhs + rlen - 2, "/*"));
+ if (is_glob)
+ rlen -= 2;
+ rs[i].dst = xstrndup(rhs, rlen);
}
- gp = strchr(sp, '*');
- ep = strchr(sp, ':');
- if (gp && ep && gp > ep)
- gp = NULL;
- if (ep) {
- if (ep[1]) {
- const char *glob = strchr(ep + 1, '*');
- if (!glob)
- gp = NULL;
- if (gp)
- rs[i].dst = xstrndup(ep + 1,
- glob - ep - 1);
- else
- rs[i].dst = xstrdup(ep + 1);
+
+ llen = (rhs ? (rhs - lhs - 1) : strlen(lhs));
+ if (2 <= llen && !memcmp(lhs + llen - 2, "/*", 2)) {
+ if ((rhs && !is_glob) || (!rhs && fetch))
+ goto invalid;
+ is_glob = 1;
+ llen -= 2;
+ } else if (rhs && is_glob) {
+ goto invalid;
+ }
+
+ rs[i].pattern = is_glob;
+ rs[i].src = xstrndup(lhs, llen);
+
+ if (fetch) {
+ /*
+ * LHS
+ * - empty is allowed; it means HEAD.
+ * - otherwise it must be a valid looking ref.
+ */
+ if (!*rs[i].src)
+ ; /* empty is ok */
+ else {
+ st = check_ref_format(rs[i].src);
+ if (st && st != CHECK_REF_FORMAT_ONELEVEL)
+ goto invalid;
+ }
+ /*
+ * RHS
+ * - missing is ok, and is same as empty.
+ * - empty is ok; it means not to store.
+ * - otherwise it must be a valid looking ref.
+ */
+ if (!rs[i].dst) {
+ ; /* ok */
+ } else if (!*rs[i].dst) {
+ ; /* ok */
+ } else {
+ st = check_ref_format(rs[i].dst);
+ if (st && st != CHECK_REF_FORMAT_ONELEVEL)
+ goto invalid;
}
} else {
- ep = sp + strlen(sp);
- }
- if (gp) {
- rs[i].pattern = 1;
- ep = gp;
+ /*
+ * 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) {
+ st = check_ref_format(rs[i].src);
+ if (st && st != CHECK_REF_FORMAT_ONELEVEL)
+ 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) {
+ st = check_ref_format(rs[i].src);
+ if (st && st != CHECK_REF_FORMAT_ONELEVEL)
+ goto invalid;
+ } else if (!*rs[i].dst) {
+ goto invalid;
+ } else {
+ st = check_ref_format(rs[i].dst);
+ if (st && st != CHECK_REF_FORMAT_ONELEVEL)
+ goto invalid;
+ }
}
- rs[i].src = xstrndup(sp, ep - sp);
}
return rs;
+
+ invalid:
+ die("Invalid refspec '%s'", refspec[i]);
+}
+
+struct refspec *parse_fetch_refspec(int nr_refspec, const char **refspec)
+{
+ return parse_refspec_internal(nr_refspec, refspec, 1);
+}
+
+struct refspec *parse_push_refspec(int nr_refspec, const char **refspec)
+{
+ return parse_refspec_internal(nr_refspec, refspec, 0);
}
static int valid_remote_nick(const char *name)
add_url(ret, name);
if (!ret->url)
return NULL;
- ret->fetch = parse_ref_spec(ret->fetch_refspec_nr, ret->fetch_refspec);
- ret->push = parse_ref_spec(ret->push_refspec_nr, ret->push_refspec);
+ ret->fetch = parse_fetch_refspec(ret->fetch_refspec_nr, ret->fetch_refspec);
+ ret->push = parse_push_refspec(ret->push_refspec_nr, ret->push_refspec);
return ret;
}
if (!r)
continue;
if (!r->fetch)
- r->fetch = parse_ref_spec(r->fetch_refspec_nr,
- r->fetch_refspec);
+ r->fetch = parse_fetch_refspec(r->fetch_refspec_nr,
+ r->fetch_refspec);
if (!r->push)
- r->push = parse_ref_spec(r->push_refspec_nr,
- r->push_refspec);
+ r->push = parse_push_refspec(r->push_refspec_nr,
+ r->push_refspec);
result = fn(r, priv);
}
return result;
if (!fetch->dst)
continue;
if (fetch->pattern) {
- if (!prefixcmp(needle, key)) {
+ if (!prefixcmp(needle, key) &&
+ needle[strlen(key)] == '/') {
*result = xmalloc(strlen(value) +
strlen(needle) -
strlen(key) + 1);
{
int i;
for (i = 0; i < rs_nr; i++) {
- if (rs[i].pattern && !prefixcmp(src->name, rs[i].src))
+ if (rs[i].pattern &&
+ !prefixcmp(src->name, rs[i].src) &&
+ src->name[strlen(rs[i].src)] == '/')
return rs + i;
}
return NULL;
int nr_refspec, const char **refspec, int flags)
{
struct refspec *rs =
- parse_ref_spec(nr_refspec, (const char **) refspec);
+ parse_push_refspec(nr_refspec, (const char **) refspec);
int send_all = flags & MATCH_REFS_ALL;
int send_mirror = flags & MATCH_REFS_MIRROR;
struct ref ***tail,
int missing_ok)
{
- struct ref *ref_map, *rm;
+ struct ref *ref_map, **rmp;
if (refspec->pattern) {
ref_map = get_expanded_map(remote_refs, refspec);
}
}
- for (rm = ref_map; rm; rm = rm->next) {
- if (rm->peer_ref && check_ref_format(rm->peer_ref->name + 5))
- die("* refusing to create funny ref '%s' locally",
- rm->peer_ref->name);
+ for (rmp = &ref_map; *rmp; ) {
+ if ((*rmp)->peer_ref) {
+ int st = check_ref_format((*rmp)->peer_ref->name + 5);
+ if (st && st != CHECK_REF_FORMAT_ONELEVEL) {
+ struct ref *ignore = *rmp;
+ error("* Ignoring funny ref '%s' locally",
+ (*rmp)->peer_ref->name);
+ *rmp = (*rmp)->next;
+ free(ignore->peer_ref);
+ free(ignore);
+ continue;
+ }
+ }
+ rmp = &((*rmp)->next);
}
if (ref_map)
diff --git a/remote.h b/remote.h
index 86e036d61006a577ad091bdc30e58987871415b0..6b8c32cc7bc313b0526369460e976bb16c7c43c1 100644 (file)
--- a/remote.h
+++ b/remote.h
*/
void ref_remove_duplicates(struct ref *ref_map);
-struct refspec *parse_ref_spec(int nr_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);
int match_refs(struct ref *src, struct ref *dst, struct ref ***dst_tail,
int nr_refspec, const char **refspec, int all);
diff --git a/t/t5511-refspec.sh b/t/t5511-refspec.sh
--- /dev/null
+++ b/t/t5511-refspec.sh
@@ -0,0 +1,72 @@
+#!/bin/sh
+
+test_description='refspec parsing'
+
+. ./test-lib.sh
+
+test_refspec () {
+
+ kind=$1 refspec=$2 expect=$3
+ git config remote.frotz.url "." &&
+ git config --remove-section remote.frotz &&
+ git config remote.frotz.url "." &&
+ git config "remote.frotz.$kind" "$refspec" &&
+ if test "$expect" != invalid
+ then
+ title="$kind $refspec"
+ test='git ls-remote frotz'
+ else
+ title="$kind $refspec (invalid)"
+ test='test_must_fail git ls-remote frotz'
+ fi
+ test_expect_success "$title" "$test"
+}
+
+test_refspec push '' invalid
+test_refspec push ':' invalid
+
+test_refspec fetch ''
+test_refspec fetch ':'
+
+test_refspec push 'refs/heads/*:refs/remotes/frotz/*'
+test_refspec push 'refs/heads/*:refs/remotes/frotz' invalid
+test_refspec push 'refs/heads:refs/remotes/frotz/*' invalid
+test_refspec push 'refs/heads/master:refs/remotes/frotz/xyzzy'
+
+
+# These have invalid LHS, but we do not have a formal "valid sha-1
+# expression syntax checker" so they are not checked with the current
+# code. They will be caught downstream anyway, but we may want to
+# have tighter check later...
+
+: test_refspec push 'refs/heads/master::refs/remotes/frotz/xyzzy' invalid
+: test_refspec push 'refs/heads/maste :refs/remotes/frotz/xyzzy' invalid
+
+test_refspec fetch 'refs/heads/*:refs/remotes/frotz/*'
+test_refspec fetch 'refs/heads/*:refs/remotes/frotz' invalid
+test_refspec fetch 'refs/heads:refs/remotes/frotz/*' invalid
+test_refspec fetch 'refs/heads/master:refs/remotes/frotz/xyzzy'
+test_refspec fetch 'refs/heads/master::refs/remotes/frotz/xyzzy' invalid
+test_refspec fetch 'refs/heads/maste :refs/remotes/frotz/xyzzy' invalid
+
+test_refspec push 'master~1:refs/remotes/frotz/backup'
+test_refspec fetch 'master~1:refs/remotes/frotz/backup' invalid
+test_refspec push 'HEAD~4:refs/remotes/frotz/new'
+test_refspec fetch 'HEAD~4:refs/remotes/frotz/new' invalid
+
+test_refspec push 'HEAD'
+test_refspec fetch 'HEAD'
+test_refspec push 'refs/heads/ nitfol' invalid
+test_refspec fetch 'refs/heads/ nitfol' invalid
+
+test_refspec push 'HEAD:' invalid
+test_refspec fetch 'HEAD:'
+test_refspec push 'refs/heads/ nitfol:' invalid
+test_refspec fetch 'refs/heads/ nitfol:' invalid
+
+test_refspec push ':refs/remotes/frotz/deleteme'
+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_done