diff options
author | Vicent Martà <vicent@github.com> | 2013-04-29 13:30:31 -0700 |
---|---|---|
committer | Vicent Martà <vicent@github.com> | 2013-04-29 13:30:31 -0700 |
commit | 4157851076d476b3b7f9a8bb9b85497517b14cdf (patch) | |
tree | aeebf52e7141625bfbc9f91847f5d22cc32dc9bd | |
parent | bb503dbd0326abd6fe714b99cd7e64c482130867 (diff) | |
parent | d84884571d04d609ed5f3508aecd9e24b84f47c7 (diff) | |
download | libgit2-4157851076d476b3b7f9a8bb9b85497517b14cdf.tar.gz |
Merge pull request #1511 from carlosmn/refspec-shorthand
dwim shorthand refspecs for fetch
-rw-r--r-- | include/git2/refs.h | 7 | ||||
-rw-r--r-- | src/refs.c | 1 | ||||
-rw-r--r-- | src/refspec.c | 2 | ||||
-rw-r--r-- | src/refspec.h | 1 | ||||
-rw-r--r-- | src/remote.c | 94 | ||||
-rw-r--r-- | tests-clar/network/refspecs.c | 3 | ||||
-rw-r--r-- | tests-clar/network/remote/local.c | 41 |
7 files changed, 139 insertions, 10 deletions
diff --git a/include/git2/refs.h b/include/git2/refs.h index 1ff0d4544..e1d425352 100644 --- a/include/git2/refs.h +++ b/include/git2/refs.h @@ -422,6 +422,13 @@ typedef enum { * (e.g., foo/<star>/bar but not foo/bar<star>). */ GIT_REF_FORMAT_REFSPEC_PATTERN = (1 << 1), + + /** + * Interpret the name as part of a refspec in shorthand form + * so the `ONELEVEL` naming rules aren't enforced and 'master' + * becomes a valid name. + */ + GIT_REF_FORMAT_REFSPEC_SHORTHAND = (1 << 2), } git_reference_normalize_t; /** diff --git a/src/refs.c b/src/refs.c index 9c6684a5a..2faa4cb83 100644 --- a/src/refs.c +++ b/src/refs.c @@ -752,6 +752,7 @@ int git_reference__normalize_name( goto cleanup; if ((segments_count == 1 ) && + !(flags & GIT_REF_FORMAT_REFSPEC_SHORTHAND) && !(is_all_caps_and_underscore(name, (size_t)segment_len) || ((flags & GIT_REF_FORMAT_REFSPEC_PATTERN) && !strcmp("*", name)))) goto cleanup; diff --git a/src/refspec.c b/src/refspec.c index fbdea9d93..256540819 100644 --- a/src/refspec.c +++ b/src/refspec.c @@ -60,7 +60,7 @@ int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch) refspec->pattern = is_glob; refspec->src = git__strndup(lhs, llen); - flags = GIT_REF_FORMAT_ALLOW_ONELEVEL + flags = GIT_REF_FORMAT_ALLOW_ONELEVEL | GIT_REF_FORMAT_REFSPEC_SHORTHAND | (is_glob ? GIT_REF_FORMAT_REFSPEC_PATTERN : 0); if (is_fetch) { diff --git a/src/refspec.h b/src/refspec.h index 29f4d5354..44d484c7b 100644 --- a/src/refspec.h +++ b/src/refspec.h @@ -17,6 +17,7 @@ struct git_refspec { unsigned int force :1, push : 1, pattern :1, + dwim :1, matching :1; }; diff --git a/src/remote.c b/src/remote.c index ffce2b6e2..1183137a6 100644 --- a/src/remote.c +++ b/src/remote.c @@ -632,28 +632,104 @@ int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_ur return 0; } +static int store_refs(git_remote_head *head, void *payload) +{ + git_vector *refs = (git_vector *)payload; + + return git_vector_insert(refs, head); +} + +static int dwim_refspecs(git_vector *refspecs, git_vector *refs) +{ + git_buf buf = GIT_BUF_INIT; + git_refspec *spec; + size_t i, j, pos; + git_remote_head key; + + const char* formatters[] = { + GIT_REFS_DIR "%s", + GIT_REFS_TAGS_DIR "%s", + GIT_REFS_HEADS_DIR "%s", + NULL + }; + + git_vector_foreach(refspecs, i, spec) { + if (spec->dwim) + continue; + + /* shorthand on the lhs */ + if (git__prefixcmp(spec->src, GIT_REFS_DIR)) { + for (j = 0; formatters[j]; j++) { + git_buf_clear(&buf); + if (git_buf_printf(&buf, formatters[j], spec->src) < 0) + return -1; + + key.name = (char *) git_buf_cstr(&buf); + if (!git_vector_search(&pos, refs, &key)) { + /* we found something to match the shorthand, set src to that */ + git__free(spec->src); + spec->src = git_buf_detach(&buf); + } + } + } + + if (spec->dst && git__prefixcmp(spec->dst, GIT_REFS_DIR)) { + /* if it starts with "remotes" then we just prepend "refs/" */ + if (!git__prefixcmp(spec->dst, "remotes/")) { + git_buf_puts(&buf, GIT_REFS_DIR); + } else { + git_buf_puts(&buf, GIT_REFS_HEADS_DIR); + } + + if (git_buf_puts(&buf, spec->dst) < 0) + return -1; + + git__free(spec->dst); + spec->dst = git_buf_detach(&buf); + } + + spec->dwim = 1; + } + + return 0; +} + +static int remote_head_cmp(const void *_a, const void *_b) +{ + const git_remote_head *a = (git_remote_head *) _a; + const git_remote_head *b = (git_remote_head *) _b; + + return git__strcmp_cb(a->name, b->name); +} + int git_remote_download( git_remote *remote, git_transfer_progress_callback progress_cb, void *progress_payload) { int error; + git_vector refs; assert(remote); + if (git_vector_init(&refs, 16, remote_head_cmp) < 0) + return -1; + + if (git_remote_ls(remote, store_refs, &refs) < 0) { + return -1; + } + + error = dwim_refspecs(&remote->refspecs, &refs); + git_vector_free(&refs); + if (error < 0) + return -1; + if ((error = git_fetch_negotiate(remote)) < 0) return error; return git_fetch_download_pack(remote, progress_cb, progress_payload); } -static int update_tips_callback(git_remote_head *head, void *payload) -{ - git_vector *refs = (git_vector *)payload; - - return git_vector_insert(refs, head); -} - static int remote_head_for_fetchspec_src(git_remote_head **out, git_vector *update_heads, const char *fetchspec_src) { unsigned int i; @@ -814,7 +890,7 @@ static int update_tips_for_spec(git_remote *remote, git_refspec *spec, git_vecto if (!git_reference_is_valid_name(head->name)) continue; - if (git_refspec_src_matches(spec, head->name)) { + if (git_refspec_src_matches(spec, head->name) && spec->dst) { if (git_refspec_transform_r(&refname, spec, head->name) < 0) goto on_error; } else if (remote->download_tags != GIT_REMOTE_DOWNLOAD_TAGS_NONE) { @@ -887,7 +963,7 @@ int git_remote_update_tips(git_remote *remote) if (git_vector_init(&refs, 16, NULL) < 0) return -1; - if (git_remote_ls(remote, update_tips_callback, &refs) < 0) + if (git_remote_ls(remote, store_refs, &refs) < 0) goto on_error; git_vector_foreach(&remote->refspecs, i, spec) { diff --git a/tests-clar/network/refspecs.c b/tests-clar/network/refspecs.c index b3d80fb85..676a1fa99 100644 --- a/tests-clar/network/refspecs.c +++ b/tests-clar/network/refspecs.c @@ -81,4 +81,7 @@ void test_network_refspecs__parsing(void) assert_refspec(GIT_DIRECTION_FETCH, "refs/heads/*/for-linus:refs/remotes/mine/*", true); assert_refspec(GIT_DIRECTION_PUSH, "refs/heads/*/for-linus:refs/remotes/mine/*", true); + + assert_refspec(GIT_DIRECTION_FETCH, "master", true); + assert_refspec(GIT_DIRECTION_PUSH, "master", true); } diff --git a/tests-clar/network/remote/local.c b/tests-clar/network/remote/local.c index 7e847e654..74ef63dc9 100644 --- a/tests-clar/network/remote/local.c +++ b/tests-clar/network/remote/local.c @@ -100,3 +100,44 @@ void test_network_remote_local__nested_tags_are_completely_peeled(void) cl_git_pass(git_remote_ls(remote, &ensure_peeled__cb, NULL)); } + +void test_network_remote_local__shorthand_fetch_refspec0(void) +{ + const char *refspec = "master:remotes/sloppy/master"; + const char *refspec2 = "master:boh/sloppy/master"; + + git_reference *ref; + + connect_to_local_repository(cl_fixture("testrepo.git")); + cl_git_pass(git_remote_add_fetch(remote, refspec)); + cl_git_pass(git_remote_add_fetch(remote, refspec2)); + + cl_git_pass(git_remote_download(remote, NULL, NULL)); + cl_git_pass(git_remote_update_tips(remote)); + + cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/sloppy/master")); + git_reference_free(ref); + + cl_git_pass(git_reference_lookup(&ref, repo, "refs/heads/boh/sloppy/master")); + git_reference_free(ref); +} + +void test_network_remote_local__shorthand_fetch_refspec1(void) +{ + const char *refspec = "master"; + const char *refspec2 = "hard_tag"; + + git_reference *ref; + + connect_to_local_repository(cl_fixture("testrepo.git")); + git_remote_clear_refspecs(remote); + cl_git_pass(git_remote_add_fetch(remote, refspec)); + cl_git_pass(git_remote_add_fetch(remote, refspec2)); + + cl_git_pass(git_remote_download(remote, NULL, NULL)); + cl_git_pass(git_remote_update_tips(remote)); + + cl_git_fail(git_reference_lookup(&ref, repo, "refs/remotes/master")); + + cl_git_fail(git_reference_lookup(&ref, repo, "refs/tags/hard_tag")); +} |