diff options
author | Patrick Steinhardt <ps@pks.im> | 2019-04-26 10:38:02 +0200 |
---|---|---|
committer | Patrick Steinhardt <ps@pks.im> | 2019-04-26 10:38:02 +0200 |
commit | 0c71e4cbd646a62fcbe534532178e841107d232d (patch) | |
tree | 4536d5f878985d754a265890c93cb37d8c369076 | |
parent | 51214b85dedc39b734cce9d09b9311221a31de4b (diff) | |
download | libgit2-0c71e4cbd646a62fcbe534532178e841107d232d.tar.gz |
refspec: fix transforming nested stars
When we transform a refspec with a component containing a glob, then
we simply copy over the component until the next separator from
the matching ref. E.g. if we have a ref "refs/heads/foo/bar" and
a refspec "refs/heads/*/bar:refs/remotes/origin/*/bar", we:
1. Copy over everything until hitting the glob from the <dst>
part: "refs/remotes/origin/".
2. Strip the common prefix of ref and <src> part until the glob,
which is "refs/heads/". This leaves us with a ref of "foo/bar".
3. Copy from the ref until the next "/" separator, resulting in
"refs/remotes/origin/foo".
4. Copy over the remaining part of the <dst> spec, which is
"bar": "refs/remotes/origin/foo/bar".
This worked just fine in a world where globs in refspecs were
restricted such that a globbing component may only contain a
single "*", only. But this restriction has been lifted, so that a
glob component may be nested between other characters, causing
the above algorithm to fail. Most notably the third step, where
we copy until hitting the next "/" separator, might result in a
wrong transformation. Given e.g. a ref "refs/gbranchg/head" and a
refspec "refs/g*g/head:refs/remotes/origin/*", we'd also be
copying the "g" between "branch" and "/" and end up with the
wrong transformed ref "refs/remotes/origin/branchg".
Instead of copying until the next component separator, we should
copy until we hit the pattern after the "*". So in the above
example, we'd copy until hitting the string "g/head".
-rw-r--r-- | src/refspec.c | 17 | ||||
-rw-r--r-- | tests/network/refspecs.c | 5 |
2 files changed, 10 insertions, 12 deletions
diff --git a/src/refspec.c b/src/refspec.c index eaf0c2c83..dab053634 100644 --- a/src/refspec.c +++ b/src/refspec.c @@ -228,7 +228,6 @@ static int refspec_transform( git_buf *out, const char *from, const char *to, const char *name) { const char *from_star, *to_star; - const char *name_slash, *from_slash; size_t replacement_len, star_offset; git_buf_sanitize(out); @@ -251,17 +250,11 @@ static int refspec_transform( /* 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; + /* + * Copy over the name, but exclude the trailing part in "from" starting + * after the glob + */ + replacement_len = strlen(name + star_offset) - strlen(from_star + 1); git_buf_put(out, name + star_offset, replacement_len); return git_buf_puts(out, to_star + 1); diff --git a/tests/network/refspecs.c b/tests/network/refspecs.c index 9df0a764f..734759060 100644 --- a/tests/network/refspecs.c +++ b/tests/network/refspecs.c @@ -120,6 +120,11 @@ void test_network_refspecs__transform_loosened_star(void) assert_valid_transform("refs/heads/branch-*/head:refs/remotes/origin/branch-*/head", "refs/heads/branch-a/head", "refs/remotes/origin/branch-a/head"); } +void test_network_refspecs__transform_nested_star(void) +{ + assert_valid_transform("refs/heads/x*x/for-linus:refs/remotes/mine/*", "refs/heads/xbranchx/for-linus", "refs/remotes/mine/branch"); +} + void test_network_refspecs__no_dst(void) { assert_valid_transform("refs/heads/master:", "refs/heads/master", ""); |