From 9ed104a8fa9fc28bb29a0ff53c68094696e13aea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 4 Jul 2014 17:16:17 +0200 Subject: [PATCH 1/3] refspec: short-circuit non-pattern refspecs on transform When transforming a non-pattern refspec, we simply need to copy over the opposite string. Move that logic up to the wrapper so we can assume a pattern refspec in the transformation function. --- src/refspec.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/refspec.c b/src/refspec.c index fa60aa7aa..48d34cd95 100644 --- a/src/refspec.c +++ b/src/refspec.c @@ -209,11 +209,21 @@ static int refspec_transform( int git_refspec_transform(git_buf *out, const git_refspec *spec, const char *name) { + git_buf_sanitize(out); + + if (!spec->pattern) + return git_buf_puts(out, spec->dst); + return refspec_transform(out, spec->src, spec->dst, name); } int git_refspec_rtransform(git_buf *out, const git_refspec *spec, const char *name) { + git_buf_sanitize(out); + + if (!spec->pattern) + return git_buf_puts(out, spec->src); + return refspec_transform(out, spec->dst, spec->src, name); } From f5287fa6c39762072e30140329c41e9f304d7c04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 4 Jul 2014 17:17:23 +0200 Subject: [PATCH 2/3] refspec: support asterisks in the middle of a pattern We used to assume a refspec would only have an asterisk in the middle of their respective pattern. This has not been a valid assumption for some time now with git. Instead of assuming where the asterisk is going to be, change the logic to treat each pattern as having two halves with a replacement bit in the middle, where the asterisk is. --- src/refspec.c | 50 +++++++++++++++++++++++++--------------- tests/network/refspecs.c | 23 ++++++++++++++++++ 2 files changed, 55 insertions(+), 18 deletions(-) diff --git a/src/refspec.c b/src/refspec.c index 48d34cd95..77c58c84e 100644 --- a/src/refspec.c +++ b/src/refspec.c @@ -181,30 +181,44 @@ int git_refspec_dst_matches(const git_refspec *refspec, const char *refname) static int refspec_transform( git_buf *out, const char *from, const char *to, const char *name) { - size_t to_len = to ? strlen(to) : 0; - size_t from_len = from ? strlen(from) : 0; - size_t name_len = name ? strlen(name) : 0; + const char *from_star, *to_star; + const char *name_slash, *from_slash; + size_t replacement_len, star_offset; git_buf_sanitize(out); + git_buf_clear(out); - if (git_buf_set(out, to, to_len) < 0) - return -1; + /* + * There are two parts to each side of a refspec, the bit + * before the star and the bit after it. The star can be in + * the middle of the pattern, so we need to look at each bit + * individually. + */ + from_star = strchr(from, '*'); + to_star = strchr(to, '*'); - if (to_len > 0) { - /* No '*' at the end of 'to' means that refspec is mapped to one - * specific branch, so no actual transformation is needed. - */ - if (out->ptr[to_len - 1] != '*') - return 0; - git_buf_shorten(out, 1); /* remove trailing '*' copied from 'to' */ - } + assert(from_star && to_star); - if (from_len > 0) /* ignore trailing '*' from 'from' */ - from_len--; - if (from_len > name_len) - from_len = name_len; + /* star offset, both in 'from' and in 'name' */ + star_offset = from_star - from; - return git_buf_put(out, name + from_len, name_len - from_len); + /* the first half is copied over */ + git_buf_put(out, to, to_star - to); + + /* then we copy over the replacement, from the star's offset to the next slash in 'name' */ + name_slash = strchr(name + star_offset, '/'); + if (!name_slash) + name_slash = strrchr(name, '\0'); + + /* if there is no slash after the star in 'from', we want to copy everything over */ + from_slash = strchr(from + star_offset, '/'); + if (!from_slash) + name_slash = strrchr(name, '\0'); + + replacement_len = (name_slash - name) - star_offset; + git_buf_put(out, name + star_offset, replacement_len); + + return git_buf_puts(out, to_star + 1); } int git_refspec_transform(git_buf *out, const git_refspec *spec, const char *name) diff --git a/tests/network/refspecs.c b/tests/network/refspecs.c index 676a1fa99..aa9b36e58 100644 --- a/tests/network/refspecs.c +++ b/tests/network/refspecs.c @@ -84,4 +84,27 @@ void test_network_refspecs__parsing(void) assert_refspec(GIT_DIRECTION_FETCH, "master", true); assert_refspec(GIT_DIRECTION_PUSH, "master", true); + + assert_refspec(GIT_DIRECTION_FETCH, "refs/pull/*/head:refs/remotes/origin/pr/*", true); +} + +void assert_transform(const char *refspec, const char *name, const char *result) +{ + git_refspec spec; + git_buf buf = GIT_BUF_INIT; + + git_refspec__parse(&spec, refspec, true); + cl_git_pass(git_refspec_transform(&buf, &spec, name)); + cl_assert_equal_s(result, buf.ptr); + + git_buf_free(&buf); + git_refspec__free(&spec); +} + +void test_network_refspecs__transform_mid_star(void) +{ + assert_transform("refs/pull/*/head:refs/remotes/origin/pr/*", "refs/pull/23/head", "refs/remotes/origin/pr/23"); + assert_transform("refs/heads/*:refs/remotes/origin/*", "refs/heads/master", "refs/remotes/origin/master"); + assert_transform("refs/heads/*:refs/heads/*", "refs/heads/master", "refs/heads/master"); + assert_transform("refs/*:refs/*", "refs/heads/master", "refs/heads/master"); } From 9fef46deaf5af415a4bab774a223d3816cf12fa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 4 Jul 2014 17:33:35 +0200 Subject: [PATCH 3/3] Add a CHANGELOG entry for refspecs with asterisk in the middle --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f4714993a..e8e14a876 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,3 +32,6 @@ v0.21 + 1 * git_clone_into and git_clone_local_into have been removed from the public API in favour of git_clone callbacks + +* Add support for refspecs with the asterisk in the middle of a + pattern.