diff options
author | Vicent Martà <vicent@github.com> | 2013-11-11 06:47:15 -0800 |
---|---|---|
committer | Vicent Martà <vicent@github.com> | 2013-11-11 06:47:15 -0800 |
commit | 6414fd338df89eaa5bd4c64f7ab310fb7d5758bb (patch) | |
tree | 4af0ea37b387c791bd89e73edea10e3353c54423 | |
parent | 5e1281f873e7eb5b51569ef33218dd20b69ff707 (diff) | |
parent | a6192d7c98976edb0ce4fd10438ac7a19c283598 (diff) | |
download | libgit2-6414fd338df89eaa5bd4c64f7ab310fb7d5758bb.tar.gz |
Merge pull request #1956 from libgit2/cmn/fetch-default-head
Remote revamp (director's cut)
-rw-r--r-- | examples/network/ls-remote.c | 26 | ||||
-rw-r--r-- | include/git2/remote.h | 52 | ||||
-rw-r--r-- | include/git2/transport.h | 12 | ||||
-rw-r--r-- | src/clone.c | 41 | ||||
-rw-r--r-- | src/fetch.c | 59 | ||||
-rw-r--r-- | src/fetchhead.c | 6 | ||||
-rw-r--r-- | src/push.c | 20 | ||||
-rw-r--r-- | src/refspec.c | 68 | ||||
-rw-r--r-- | src/refspec.h | 8 | ||||
-rw-r--r-- | src/remote.c | 205 | ||||
-rw-r--r-- | src/remote.h | 1 | ||||
-rw-r--r-- | src/transports/local.c | 10 | ||||
-rw-r--r-- | src/transports/smart.c | 44 | ||||
-rw-r--r-- | src/transports/smart.h | 3 | ||||
-rw-r--r-- | src/transports/smart_protocol.c | 26 | ||||
-rw-r--r-- | tests-clar/network/remote/local.c | 51 | ||||
-rw-r--r-- | tests-clar/online/fetch.c | 19 | ||||
-rw-r--r-- | tests-clar/online/push.c | 15 | ||||
-rw-r--r-- | tests-clar/online/push_util.c | 40 | ||||
-rw-r--r-- | tests-clar/online/push_util.h | 14 |
20 files changed, 433 insertions, 287 deletions
diff --git a/examples/network/ls-remote.c b/examples/network/ls-remote.c index 18cd02367..1e08b293e 100644 --- a/examples/network/ls-remote.c +++ b/examples/network/ls-remote.c @@ -4,21 +4,12 @@ #include <string.h> #include "common.h" -/** Callback to show each item */ -static int show_ref__cb(git_remote_head *head, void *payload) -{ - char oid[GIT_OID_HEXSZ + 1] = {0}; - - (void)payload; - git_oid_fmt(oid, &head->oid); - printf("%s\t%s\n", oid, head->name); - return 0; -} - static int use_remote(git_repository *repo, char *name) { git_remote *remote = NULL; int error; + const git_remote_head **refs; + size_t refs_len, i; git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT; // Find the remote by name @@ -40,7 +31,18 @@ static int use_remote(git_repository *repo, char *name) if (error < 0) goto cleanup; - error = git_remote_ls(remote, &show_ref__cb, NULL); + /** + * Get the list of references on the remote and print out + * their name next to what they point to. + */ + if (git_remote_ls(&refs, &refs_len, remote) < 0) + goto cleanup; + + for (i = 0; i < refs_len; i++) { + char oid[GIT_OID_HEXSZ + 1] = {0}; + git_oid_fmt(oid, &refs[i]->oid); + printf("%s\t%s\n", oid, refs[i]->name); + } cleanup: git_remote_free(remote); diff --git a/include/git2/remote.h b/include/git2/remote.h index b9cf86ef1..9e7d218a8 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -54,7 +54,7 @@ GIT_EXTERN(int) git_remote_create( * * @param out pointer to the new remote object * @param repo the associated repository - * @param fetch the fetch refspec to use for this remote. May be NULL for defaults. + * @param fetch the fetch refspec to use for this remote. * @param url the remote repository's URL * @return 0 or an error code */ @@ -145,8 +145,11 @@ GIT_EXTERN(int) git_remote_set_pushurl(git_remote *remote, const char* url); /** * Add a fetch refspec to the remote * + * Convenience function for adding a single fetch refspec to the + * current list in the remote. + * * @param remote the remote - * @apram refspec the new fetch refspec + * @param refspec the new fetch refspec * @return 0 or an error value */ GIT_EXTERN(int) git_remote_add_fetch(git_remote *remote, const char *refspec); @@ -163,8 +166,21 @@ GIT_EXTERN(int) git_remote_add_fetch(git_remote *remote, const char *refspec); GIT_EXTERN(int) git_remote_get_fetch_refspecs(git_strarray *array, git_remote *remote); /** + * Set the remote's list of fetch refspecs + * + * The contents of the string array are copied. + * + * @param remote the remote to modify + * @param array the new list of fetch resfpecs + */ +GIT_EXTERN(int) git_remote_set_fetch_refspecs(git_remote *remote, git_strarray *array); + +/** * Add a push refspec to the remote * + * Convenience function for adding a single push refspec to the + * current list in the remote. + * * @param remote the remote * @param refspec the new push refspec * @return 0 or an error value @@ -183,6 +199,16 @@ GIT_EXTERN(int) git_remote_add_push(git_remote *remote, const char *refspec); GIT_EXTERN(int) git_remote_get_push_refspecs(git_strarray *array, git_remote *remote); /** + * Set the remote's list of push refspecs + * + * The contents of the string array are copied. + * + * @param remote the remote to modify + * @param array the new list of push resfpecs + */ +GIT_EXTERN(int) git_remote_set_push_refspecs(git_remote *remote, git_strarray *array); + +/** * Clear the refspecs * * Remove all configured fetch and push refspecs from the remote. @@ -209,15 +235,6 @@ GIT_EXTERN(size_t) git_remote_refspec_count(git_remote *remote); GIT_EXTERN(const git_refspec *)git_remote_get_refspec(git_remote *remote, size_t n); /** - * Remove a refspec from the remote - * - * @param remote the remote to query - * @param n the refspec to remove - * @return 0 or GIT_ENOTFOUND - */ -GIT_EXTERN(int) git_remote_remove_refspec(git_remote *remote, size_t n); - -/** * Open a connection to a remote * * The transport is selected based on the URL. The direction argument @@ -237,15 +254,16 @@ GIT_EXTERN(int) git_remote_connect(git_remote *remote, git_direction direction); * The remote (or more exactly its transport) must be connected. The * memory belongs to the remote. * - * If you a return a non-zero value from the callback, this will stop - * looping over the refs. + * The array will stay valid as long as the remote object exists and + * its transport isn't changed, but a copy is recommended for usage of + * the data. * + * @param out pointer to the array + * @param size the number of remote heads * @param remote the remote - * @param list_cb function to call with each ref discovered at the remote - * @param payload additional data to pass to the callback - * @return 0 on success, GIT_EUSER on non-zero callback, or error code + * @return 0 on success, or an error code */ -GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload); +GIT_EXTERN(int) git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote); /** * Download and index the packfile diff --git a/include/git2/transport.h b/include/git2/transport.h index 147b3fdcb..81ebf4dc9 100644 --- a/include/git2/transport.h +++ b/include/git2/transport.h @@ -203,13 +203,13 @@ struct git_transport { int direction, int flags); - /* This function may be called after a successful call to connect(). The - * provided callback is invoked for each ref discovered on the remote - * end. */ + /* This function may be called after a successful call to + * connect(). The array returned is owned by the transport and + * is guranteed until the next call of a transport function. */ int (*ls)( - git_transport *transport, - git_headlist_cb list_cb, - void *payload); + const git_remote_head ***out, + size_t *size, + git_transport *transport); /* Executes the push whose context is in the git_push object. */ int (*push)(git_transport *transport, git_push *push); diff --git a/src/clone.c b/src/clone.c index 657243945..23aacd478 100644 --- a/src/clone.c +++ b/src/clone.c @@ -176,25 +176,20 @@ static int update_head_to_new_branch( return error; } -static int get_head_callback(git_remote_head *head, void *payload) -{ - git_remote_head **destination = (git_remote_head **)payload; - - /* Save the first entry, and terminate the enumeration */ - *destination = head; - return 1; -} - static int update_head_to_remote(git_repository *repo, git_remote *remote) { int retcode = -1; + size_t refs_len; git_refspec dummy_spec; - git_remote_head *remote_head; + const git_remote_head *remote_head, **refs; struct head_info head_info; git_buf remote_master_name = GIT_BUF_INIT; + if (git_remote_ls(&refs, &refs_len, remote) < 0) + return -1; + /* Did we just clone an empty repository? */ - if (remote->refs.length == 0) { + if (refs_len == 0) { return setup_tracking_config( repo, "master", @@ -202,12 +197,8 @@ static int update_head_to_remote(git_repository *repo, git_remote *remote) GIT_REFS_HEADS_MASTER_FILE); } - /* Get the remote's HEAD. This is always the first ref in remote->refs. */ - remote_head = NULL; - - if (!remote->transport->ls(remote->transport, get_head_callback, &remote_head)) - return -1; - + /* Get the remote's HEAD. This is always the first ref in the list. */ + remote_head = refs[0]; assert(remote_head); git_oid_cpy(&head_info.remote_head_oid, &remote_head->oid); @@ -349,7 +340,7 @@ static bool should_checkout( int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_opts *co_opts, const char *branch) { int error = 0, old_fetchhead; - size_t nspecs; + git_strarray refspecs; assert(repo && remote); @@ -358,6 +349,10 @@ int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_ return -1; } + + if ((error = git_remote_get_fetch_refspecs(&refspecs, remote)) < 0) + return error; + if ((error = git_remote_add_fetch(remote, "refs/tags/*:refs/tags/*")) < 0) return error; @@ -378,9 +373,13 @@ int git_clone_into(git_repository *repo, git_remote *remote, const git_checkout_ cleanup: git_remote_set_update_fetchhead(remote, old_fetchhead); - /* Remove the tags refspec */ - nspecs = git_remote_refspec_count(remote); - git_remote_remove_refspec(remote, nspecs); + /* Go back to the original refspecs */ + if (git_remote_set_fetch_refspecs(remote, &refspecs) < 0) { + git_strarray_free(&refspecs); + return -1; + } + + git_strarray_free(&refspecs); return error; } diff --git a/src/fetch.c b/src/fetch.c index 5d97913e8..276591821 100644 --- a/src/fetch.c +++ b/src/fetch.c @@ -19,55 +19,47 @@ #include "repository.h" #include "refs.h" -struct filter_payload { - git_remote *remote; - const git_refspec *spec, *tagspec; - git_odb *odb; - int found_head; -}; - -static int filter_ref__cb(git_remote_head *head, void *payload) +static int maybe_want(git_remote *remote, git_remote_head *head, git_odb *odb, git_refspec *tagspec) { - struct filter_payload *p = payload; int match = 0; if (!git_reference_is_valid_name(head->name)) return 0; - if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0) - p->found_head = 1; - else if (p->remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) { + if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) { /* * If tagopt is --tags, then we only use the default * tags refspec and ignore the remote's */ - if (git_refspec_src_matches(p->tagspec, head->name)) + if (git_refspec_src_matches(tagspec, head->name)) match = 1; else return 0; - } else if (git_remote__matching_refspec(p->remote, head->name)) + } else if (git_remote__matching_refspec(remote, head->name)) match = 1; if (!match) return 0; /* If we have the object, mark it so we don't ask for it */ - if (git_odb_exists(p->odb, &head->oid)) + if (git_odb_exists(odb, &head->oid)) head->local = 1; else - p->remote->need_pack = 1; + remote->need_pack = 1; - return git_vector_insert(&p->remote->refs, head); + return git_vector_insert(&remote->refs, head); } static int filter_wants(git_remote *remote) { - struct filter_payload p; - git_refspec tagspec; - int error = -1; + git_remote_head **heads; + git_refspec tagspec, head; + int error = 0; + git_odb *odb; + size_t i, heads_len; git_vector_clear(&remote->refs); - if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0) + if ((error = git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true)) < 0) return error; /* @@ -76,14 +68,27 @@ static int filter_wants(git_remote *remote) * not interested in any particular branch but just the remote's * HEAD, which will be stored in FETCH_HEAD after the fetch. */ - p.tagspec = &tagspec; - p.found_head = 0; - p.remote = remote; + if (remote->active_refspecs.length == 0) { + if ((error = git_refspec__parse(&head, "HEAD", true)) < 0) + goto cleanup; - if (git_repository_odb__weakptr(&p.odb, remote->repo) < 0) + error = git_refspec__dwim_one(&remote->active_refspecs, &head, &remote->refs); + git_refspec__free(&head); + + if (error < 0) + goto cleanup; + } + + if (git_repository_odb__weakptr(&odb, remote->repo) < 0) + goto cleanup; + + if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0) goto cleanup; - error = git_remote_ls(remote, filter_ref__cb, &p); + for (i = 0; i < heads_len; i++) { + if ((error = maybe_want(remote, heads[i], odb, &tagspec)) < 0) + break; + } cleanup: git_refspec__free(&tagspec); @@ -106,7 +111,7 @@ int git_fetch_negotiate(git_remote *remote) } /* Don't try to negotiate when we don't want anything */ - if (remote->refs.length == 0 || !remote->need_pack) + if (!remote->need_pack) return 0; /* diff --git a/src/fetchhead.c b/src/fetchhead.c index 9672623ff..67089d13d 100644 --- a/src/fetchhead.c +++ b/src/fetchhead.c @@ -74,6 +74,7 @@ static int fetchhead_ref_write( { char oid[GIT_OID_HEXSZ + 1]; const char *type, *name; + int head = 0; assert(file && fetchhead_ref); @@ -87,11 +88,16 @@ static int fetchhead_ref_write( GIT_REFS_TAGS_DIR) == 0) { type = "tag "; name = fetchhead_ref->ref_name + strlen(GIT_REFS_TAGS_DIR); + } else if (!git__strcmp(fetchhead_ref->ref_name, GIT_HEAD_FILE)) { + head = 1; } else { type = ""; name = fetchhead_ref->ref_name; } + if (head) + return git_filebuf_printf(file, "%s\t\t%s\n", oid, fetchhead_ref->remote_url); + return git_filebuf_printf(file, "%s\t%s\t%s'%s' of %s\n", oid, (fetchhead_ref->is_merge) ? "" : "not-for-merge", diff --git a/src/push.c b/src/push.c index a799db8d0..4f442c6a2 100644 --- a/src/push.c +++ b/src/push.c @@ -616,16 +616,22 @@ on_error: return error; } -static int cb_filter_refs(git_remote_head *ref, void *data) -{ - git_remote *remote = (git_remote *) data; - return git_vector_insert(&remote->refs, ref); -} - static int filter_refs(git_remote *remote) { + const git_remote_head **heads; + size_t heads_len, i; + git_vector_clear(&remote->refs); - return git_remote_ls(remote, cb_filter_refs, remote); + + if (git_remote_ls(&heads, &heads_len, remote) < 0) + return -1; + + for (i = 0; i < heads_len; i++) { + if (git_vector_insert(&remote->refs, heads[i]) < 0) + return -1; + } + + return 0; } int git_push_finish(git_push *push) diff --git a/src/refspec.c b/src/refspec.c index 492c6ed3f..a97340071 100644 --- a/src/refspec.c +++ b/src/refspec.c @@ -12,6 +12,7 @@ #include "util.h" #include "posix.h" #include "refs.h" +#include "vector.h" int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch) { @@ -287,3 +288,70 @@ git_direction git_refspec_direction(const git_refspec *spec) return spec->push; } + +int git_refspec__dwim_one(git_vector *out, git_refspec *spec, git_vector *refs) +{ + git_buf buf = GIT_BUF_INIT; + size_t 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_refspec *cur = git__calloc(1, sizeof(git_refspec)); + GITERR_CHECK_ALLOC(cur); + + cur->force = spec->force; + cur->push = spec->push; + cur->pattern = spec->pattern; + cur->matching = spec->matching; + cur->string = git__strdup(spec->string); + + /* 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 */ + cur->src = git_buf_detach(&buf); + } + } + } + + /* No shorthands found, copy over the name */ + if (cur->src == NULL && spec->src != NULL) { + cur->src = git__strdup(spec->src); + GITERR_CHECK_ALLOC(cur->src); + } + + 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; + + cur->dst = git_buf_detach(&buf); + } + + git_buf_free(&buf); + + if (cur->dst == NULL && spec->dst != NULL) { + cur->dst = git__strdup(spec->dst); + GITERR_CHECK_ALLOC(cur->dst); + } + + return git_vector_insert(out, cur); +} diff --git a/src/refspec.h b/src/refspec.h index 44d484c7b..51b7bfee9 100644 --- a/src/refspec.h +++ b/src/refspec.h @@ -9,6 +9,7 @@ #include "git2/refspec.h" #include "buffer.h" +#include "vector.h" struct git_refspec { char *string; @@ -17,7 +18,6 @@ struct git_refspec { unsigned int force :1, push : 1, pattern :1, - dwim :1, matching :1; }; @@ -63,4 +63,10 @@ int git_refspec__serialize(git_buf *out, const git_refspec *refspec); */ int git_refspec_is_wildcard(const git_refspec *spec); +/** + * DWIM `spec` with `refs` existing on the remote, append the dwim'ed + * result in `out`. + */ +int git_refspec__dwim_one(git_vector *out, git_refspec *spec, git_vector *refs); + #endif diff --git a/src/remote.c b/src/remote.c index 3528b1c46..62f297a7e 100644 --- a/src/remote.c +++ b/src/remote.c @@ -19,6 +19,8 @@ #include "refspec.h" #include "fetchhead.h" +static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs); + static int add_refspec(git_remote *remote, const char *string, bool is_fetch) { git_refspec *spec; @@ -288,7 +290,8 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) GITERR_CHECK_ALLOC(remote->name); if ((git_vector_init(&remote->refs, 32, NULL) < 0) || - (git_vector_init(&remote->refspecs, 2, NULL))) { + (git_vector_init(&remote->refspecs, 2, NULL) < 0) || + (git_vector_init(&remote->active_refspecs, 2, NULL) < 0)) { error = -1; goto cleanup; } @@ -347,6 +350,10 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) if (download_tags_value(remote, config) < 0) goto cleanup; + /* Move the data over to where the matching functions can find them */ + if (dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs) < 0) + goto cleanup; + *out = remote; cleanup: @@ -613,11 +620,11 @@ on_error: return -1; } -int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload) +int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote) { assert(remote); - return remote->transport->ls(remote->transport, list_cb, payload); + return remote->transport->ls(out, size, remote->transport); } int git_remote__get_http_proxy(git_remote *remote, bool use_ssl, char **proxy_url) @@ -677,67 +684,31 @@ 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) +/* DWIM `refspecs` based on `refs` and append the output to `out` */ +static int dwim_refspecs(git_vector *out, git_vector *refspecs, git_vector *refs) { - git_buf buf = GIT_BUF_INIT; + size_t i; 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_refspec__dwim_one(out, spec, refs) < 0) + return -1; + } - if (git_buf_puts(&buf, spec->dst) < 0) - return -1; + return 0; +} - git__free(spec->dst); - spec->dst = git_buf_detach(&buf); - } +static void free_refspecs(git_vector *vec) +{ + size_t i; + git_refspec *spec; - spec->dwim = 1; + git_vector_foreach(vec, i, spec) { + git_refspec__free(spec); + git__free(spec); } - git_buf_free(&buf); - return 0; + git_vector_clear(vec); } static int remote_head_cmp(const void *_a, const void *_b) @@ -748,6 +719,25 @@ static int remote_head_cmp(const void *_a, const void *_b) return git__strcmp_cb(a->name, b->name); } +static int ls_to_vector(git_vector *out, git_remote *remote) +{ + git_remote_head **heads; + size_t heads_len, i; + + if (git_remote_ls((const git_remote_head ***)&heads, &heads_len, remote) < 0) + return -1; + + if (git_vector_init(out, heads_len, remote_head_cmp) < 0) + return -1; + + for (i = 0; i < heads_len; i++) { + if (git_vector_insert(out, heads[i]) < 0) + return -1; + } + + return 0; +} + int git_remote_download(git_remote *remote) { int error; @@ -755,15 +745,14 @@ int git_remote_download(git_remote *remote) assert(remote); - if (git_vector_init(&refs, 16, remote_head_cmp) < 0) + if (ls_to_vector(&refs, remote) < 0) return -1; - if (git_remote_ls(remote, store_refs, &refs) < 0) { - return -1; - } + free_refspecs(&remote->active_refspecs); - error = dwim_refspecs(&remote->refspecs, &refs); + error = dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &refs); git_vector_free(&refs); + if (error < 0) return -1; @@ -1013,10 +1002,8 @@ int git_remote_update_tips(git_remote *remote) if (git_refspec__parse(&tagspec, GIT_REFSPEC_TAGS, true) < 0) return -1; - if (git_vector_init(&refs, 16, NULL) < 0) - return -1; - if ((error = git_remote_ls(remote, store_refs, &refs)) < 0) + if ((error = ls_to_vector(&refs, remote)) < 0) goto out; if (remote->download_tags == GIT_REMOTE_DOWNLOAD_TAGS_ALL) { @@ -1024,7 +1011,7 @@ int git_remote_update_tips(git_remote *remote) goto out; } - git_vector_foreach(&remote->refspecs, i, spec) { + git_vector_foreach(&remote->active_refspecs, i, spec) { if (spec->push) continue; @@ -1033,8 +1020,8 @@ int git_remote_update_tips(git_remote *remote) } out: - git_refspec__free(&tagspec); git_vector_free(&refs); + git_refspec__free(&tagspec); return error; } @@ -1067,9 +1054,6 @@ void git_remote_disconnect(git_remote *remote) void git_remote_free(git_remote *remote) { - git_refspec *spec; - size_t i; - if (remote == NULL) return; @@ -1082,12 +1066,12 @@ void git_remote_free(git_remote *remote) git_vector_free(&remote->refs); - git_vector_foreach(&remote->refspecs, i, spec) { - git_refspec__free(spec); - git__free(spec); - } + free_refspecs(&remote->refspecs); git_vector_free(&remote->refspecs); + free_refspecs(&remote->active_refspecs); + git_vector_free(&remote->active_refspecs); + git__free(remote->url); git__free(remote->pushurl); git__free(remote->name); @@ -1496,7 +1480,7 @@ git_refspec *git_remote__matching_refspec(git_remote *remote, const char *refnam git_refspec *spec; size_t i; - git_vector_foreach(&remote->refspecs, i, spec) { + git_vector_foreach(&remote->active_refspecs, i, spec) { if (spec->push) continue; @@ -1512,7 +1496,7 @@ git_refspec *git_remote__matching_dst_refspec(git_remote *remote, const char *re git_refspec *spec; size_t i; - git_vector_foreach(&remote->refspecs, i, spec) { + git_vector_foreach(&remote->active_refspecs, i, spec) { if (spec->push) continue; @@ -1535,14 +1519,68 @@ void git_remote_clear_refspecs(git_remote *remote) git_vector_clear(&remote->refspecs); } +static int add_and_dwim(git_remote *remote, const char *str, int push) +{ + git_refspec *spec; + git_vector *vec; + + if (add_refspec(remote, str, !push) < 0) + return -1; + + vec = &remote->refspecs; + spec = git_vector_get(vec, vec->length - 1); + return git_refspec__dwim_one(&remote->active_refspecs, spec, &remote->refs); +} + int git_remote_add_fetch(git_remote *remote, const char *refspec) { - return add_refspec(remote, refspec, true); + return add_and_dwim(remote, refspec, false); } int git_remote_add_push(git_remote *remote, const char *refspec) { - return add_refspec(remote, refspec, false); + return add_and_dwim(remote, refspec, true); +} + +static int set_refspecs(git_remote *remote, git_strarray *array, int push) +{ + git_vector *vec = &remote->refspecs; + git_refspec *spec; + size_t i; + + /* Start by removing any refspecs of the same type */ + for (i = 0; i < vec->length; i++) { + spec = git_vector_get(vec, i); + if (spec->push != push) + continue; + + git_refspec__free(spec); + git__free(spec); + git_vector_remove(vec, i); + i--; + } + + /* And now we add the new ones */ + + for (i = 0; i < array->count; i++) { + if (add_refspec(remote, array->strings[i], !push) < 0) + return -1; + } + + free_refspecs(&remote->active_refspecs); + git_vector_clear(&remote->active_refspecs); + + return dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs); +} + +int git_remote_set_fetch_refspecs(git_remote *remote, git_strarray *array) +{ + return set_refspecs(remote, array, false); +} + +int git_remote_set_push_refspecs(git_remote *remote, git_strarray *array) +{ + return set_refspecs(remote, array, true); } static int copy_refspecs(git_strarray *array, git_remote *remote, unsigned int push) @@ -1600,18 +1638,3 @@ const git_refspec *git_remote_get_refspec(git_remote *remote, size_t n) { return git_vector_get(&remote->refspecs, n); } - -int git_remote_remove_refspec(git_remote *remote, size_t n) -{ - git_refspec *spec; - - assert(remote); - - spec = git_vector_get(&remote->refspecs, n); - if (spec) { - git_refspec__free(spec); - git__free(spec); - } - - return git_vector_remove(&remote->refspecs, n); -} diff --git a/src/remote.h b/src/remote.h index 269584d96..33e4d68f8 100644 --- a/src/remote.h +++ b/src/remote.h @@ -21,6 +21,7 @@ struct git_remote { char *pushurl; git_vector refs; git_vector refspecs; + git_vector active_refspecs; git_transport *transport; git_repository *repo; git_remote_callbacks callbacks; diff --git a/src/transports/local.c b/src/transports/local.c index 3163d2eac..4502f0202 100644 --- a/src/transports/local.c +++ b/src/transports/local.c @@ -213,21 +213,17 @@ static int local_connect( return 0; } -static int local_ls(git_transport *transport, git_headlist_cb list_cb, void *payload) +static int local_ls(const git_remote_head ***out, size_t *size, git_transport *transport) { transport_local *t = (transport_local *)transport; - unsigned int i; - git_remote_head *head = NULL; if (!t->have_refs) { giterr_set(GITERR_NET, "The transport has not yet loaded the refs"); return -1; } - git_vector_foreach(&t->refs, i, head) { - if (list_cb(head, payload)) - return GIT_EUSER; - } + *out = (const git_remote_head **) t->refs.contents; + *size = t->refs.length; return 0; } diff --git a/src/transports/smart.c b/src/transports/smart.c index a681d5f40..5242beb65 100644 --- a/src/transports/smart.c +++ b/src/transports/smart.c @@ -63,6 +63,24 @@ static int git_smart__set_callbacks( return 0; } +int git_smart__update_heads(transport_smart *t) +{ + size_t i; + git_pkt *pkt; + + git_vector_clear(&t->heads); + git_vector_foreach(&t->refs, i, pkt) { + git_pkt_ref *ref = (git_pkt_ref *) pkt; + if (pkt->type != GIT_PKT_REF) + continue; + + if (git_vector_insert(&t->heads, &ref->head) < 0) + return -1; + } + + return 0; +} + static int git_smart__connect( git_transport *transport, const char *url, @@ -140,6 +158,9 @@ static int git_smart__connect( git_pkt_free((git_pkt *)first); } + /* Keep a list of heads for _ls */ + git_smart__update_heads(t); + if (t->rpc && git_smart__reset_stream(t, false) < 0) return -1; @@ -149,28 +170,17 @@ static int git_smart__connect( return 0; } -static int git_smart__ls(git_transport *transport, git_headlist_cb list_cb, void *payload) +static int git_smart__ls(const git_remote_head ***out, size_t *size, git_transport *transport) { transport_smart *t = (transport_smart *)transport; - unsigned int i; - git_pkt *p = NULL; if (!t->have_refs) { giterr_set(GITERR_NET, "The transport has not yet loaded the refs"); return -1; } - git_vector_foreach(&t->refs, i, p) { - git_pkt_ref *pkt = NULL; - - if (p->type != GIT_PKT_REF) - continue; - - pkt = (git_pkt_ref *)p; - - if (list_cb(&pkt->head, payload)) - return GIT_EUSER; - } + *out = (const git_remote_head **) t->heads.contents; + *size = t->heads.length; return 0; } @@ -293,6 +303,7 @@ static void git_smart__free(git_transport *transport) /* Free the subtransport */ t->wrapped->free(t->wrapped); + git_vector_free(&t->heads); git_vector_foreach(refs, i, p) git_pkt_free(p); @@ -340,6 +351,11 @@ int git_transport_smart(git_transport **out, git_remote *owner, void *param) return -1; } + if (git_vector_init(&t->heads, 16, ref_name_cmp) < 0) { + git__free(t); + return -1; + } + if (definition->callback(&t->wrapped, &t->parent) < 0) { git__free(t); return -1; diff --git a/src/transports/smart.h b/src/transports/smart.h index 5232e54de..32f0be7f2 100644 --- a/src/transports/smart.h +++ b/src/transports/smart.h @@ -140,6 +140,7 @@ typedef struct { git_smart_subtransport_stream *current_stream; transport_smart_caps caps; git_vector refs; + git_vector heads; git_vector common; git_atomic cancelled; packetsize_cb packetsize_cb; @@ -173,6 +174,8 @@ int git_smart__download_pack( int git_smart__negotiation_step(git_transport *transport, void *data, size_t len); int git_smart__get_push_stream(transport_smart *t, git_smart_subtransport_stream **out); +int git_smart__update_heads(transport_smart *t); + /* smart_pkt.c */ int git_pkt_parse_line(git_pkt **head, const char *line, const char **out, size_t len); int git_pkt_buffer_flush(git_buf *buf); diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index 4e9e112f4..7288a4820 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -269,7 +269,7 @@ static int wait_while_ack(gitno_buffer *buf) return 0; } -int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *refs, size_t count) +int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, const git_remote_head * const *wants, size_t count) { transport_smart *t = (transport_smart *)transport; gitno_buffer *buf = &t->buffer; @@ -279,19 +279,20 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c unsigned int i; git_oid oid; - /* No own logic, do our thing */ - if ((error = git_pkt_buffer_wants(refs, count, &t->caps, &data)) < 0) + if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0) return error; if ((error = fetch_setup_walk(&walk, repo)) < 0) goto on_error; + /* - * We don't support any kind of ACK extensions, so the negotiation - * boils down to sending what we have and listening for an ACK - * every once in a while. + * Our support for ACK extensions is simply to parse them. On + * the first ACK we will accept that as enough common + * objects. We give up if we haven't found an answer in the + * first 256 we send. */ i = 0; - while (true) { + while (i < 256) { error = git_revwalk_next(&oid, walk); if (error < 0) { @@ -349,7 +350,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c git_pkt_ack *pkt; unsigned int i; - if ((error = git_pkt_buffer_wants(refs, count, &t->caps, &data)) < 0) + if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0) goto on_error; git_vector_foreach(&t->common, i, pkt) { @@ -369,7 +370,7 @@ int git_smart__negotiate_fetch(git_transport *transport, git_repository *repo, c git_pkt_ack *pkt; unsigned int i; - if ((error = git_pkt_buffer_wants(refs, count, &t->caps, &data)) < 0) + if ((error = git_pkt_buffer_wants(wants, count, &t->caps, &data)) < 0) goto on_error; git_vector_foreach(&t->common, i, pkt) { @@ -943,8 +944,13 @@ int git_smart__push(git_transport *transport, git_push *push) push->transfer_progress_cb(push->pb->nr_written, push->pb->nr_objects, packbuilder_payload.last_bytes, push->transfer_progress_cb_payload); } - if (push->status.length) + if (push->status.length) { error = update_refs_from_report(&t->refs, &push->specs, &push->status); + if (error < 0) + goto done; + + error = git_smart__update_heads(t); + } done: git_buf_free(&pktline); diff --git a/tests-clar/network/remote/local.c b/tests-clar/network/remote/local.c index 6d658a2e4..309142925 100644 --- a/tests-clar/network/remote/local.c +++ b/tests-clar/network/remote/local.c @@ -26,26 +26,6 @@ void test_network_remote_local__cleanup(void) cl_fixture_cleanup("remotelocal"); } -static int count_ref__cb(git_remote_head *head, void *payload) -{ - int *count = (int *)payload; - - (void)head; - (*count)++; - - return 0; -} - -static int ensure_peeled__cb(git_remote_head *head, void *payload) -{ - GIT_UNUSED(payload); - - if(strcmp(head->name, "refs/tags/test^{}") != 0) - return 0; - - return git_oid_streq(&head->oid, "e90810b8df3e80c413d903f631643c716887138d"); -} - static void connect_to_local_repository(const char *local_repository) { git_buf_sets(&file_path_buf, cl_git_path_url(local_repository)); @@ -65,39 +45,42 @@ void test_network_remote_local__connected(void) void test_network_remote_local__retrieve_advertised_references(void) { - int how_many_refs = 0; + const git_remote_head **refs; + size_t refs_len; connect_to_local_repository(cl_fixture("testrepo.git")); - cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs)); + cl_git_pass(git_remote_ls(&refs, &refs_len, remote)); - cl_assert_equal_i(how_many_refs, 28); + cl_assert_equal_i(refs_len, 28); } void test_network_remote_local__retrieve_advertised_references_after_disconnect(void) { - int how_many_refs = 0; + const git_remote_head **refs; + size_t refs_len; connect_to_local_repository(cl_fixture("testrepo.git")); git_remote_disconnect(remote); - cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs)); + cl_git_pass(git_remote_ls(&refs, &refs_len, remote)); - cl_assert_equal_i(how_many_refs, 28); + cl_assert_equal_i(refs_len, 28); } void test_network_remote_local__retrieve_advertised_references_from_spaced_repository(void) { - int how_many_refs = 0; + const git_remote_head **refs; + size_t refs_len; cl_fixture_sandbox("testrepo.git"); cl_git_pass(p_rename("testrepo.git", "spaced testrepo.git")); connect_to_local_repository("spaced testrepo.git"); - cl_git_pass(git_remote_ls(remote, &count_ref__cb, &how_many_refs)); + cl_git_pass(git_remote_ls(&refs, &refs_len, remote)); - cl_assert_equal_i(how_many_refs, 28); + cl_assert_equal_i(refs_len, 28); git_remote_free(remote); /* Disconnect from the "spaced repo" before the cleanup */ remote = NULL; @@ -107,9 +90,17 @@ void test_network_remote_local__retrieve_advertised_references_from_spaced_repos void test_network_remote_local__nested_tags_are_completely_peeled(void) { + const git_remote_head **refs; + size_t refs_len, i; + connect_to_local_repository(cl_fixture("testrepo.git")); - cl_git_pass(git_remote_ls(remote, &ensure_peeled__cb, NULL)); + cl_git_pass(git_remote_ls(&refs, &refs_len, remote)); + + for (i = 0; i < refs_len; i++) { + if (!strcmp(refs[i]->name, "refs/tags/test^{}")) + cl_git_pass(git_oid_streq(&refs[i]->oid, "e90810b8df3e80c413d903f631643c716887138d")); + } } void test_network_remote_local__shorthand_fetch_refspec0(void) diff --git a/tests-clar/online/fetch.c b/tests-clar/online/fetch.c index df1b2e288..5153a7ae0 100644 --- a/tests-clar/online/fetch.c +++ b/tests-clar/online/fetch.c @@ -152,29 +152,20 @@ void test_online_fetch__can_cancel(void) git_remote_free(remote); } -int ls_cb(git_remote_head *rhead, void *payload) -{ - int *nr = payload; - GIT_UNUSED(rhead); - - (*nr)++; - - return 0; -} - void test_online_fetch__ls_disconnected(void) { + const git_remote_head **refs; + size_t refs_len_before, refs_len_after; git_remote *remote; - int nr_before = 0, nr_after = 0; cl_git_pass(git_remote_create(&remote, _repo, "test", "http://github.com/libgit2/TestGitRepository.git")); cl_git_pass(git_remote_connect(remote, GIT_DIRECTION_FETCH)); - cl_git_pass(git_remote_ls(remote, ls_cb, &nr_before)); + cl_git_pass(git_remote_ls(&refs, &refs_len_before, remote)); git_remote_disconnect(remote); - cl_git_pass(git_remote_ls(remote, ls_cb, &nr_after)); + cl_git_pass(git_remote_ls(&refs, &refs_len_after, remote)); - cl_assert_equal_i(nr_before, nr_after); + cl_assert_equal_i(refs_len_before, refs_len_after); git_remote_free(remote); } diff --git a/tests-clar/online/push.c b/tests-clar/online/push.c index d9ffe8aa9..aeb1ab47d 100644 --- a/tests-clar/online/push.c +++ b/tests-clar/online/push.c @@ -155,12 +155,11 @@ static void do_verify_push_status(git_push *push, const push_status expected[], */ static void verify_refs(git_remote *remote, expected_ref expected_refs[], size_t expected_refs_len) { - git_vector actual_refs = GIT_VECTOR_INIT; - - git_remote_ls(remote, record_ref_cb, &actual_refs); - verify_remote_refs(&actual_refs, expected_refs, expected_refs_len); + const git_remote_head **actual_refs; + size_t actual_refs_len; - git_vector_free(&actual_refs); + git_remote_ls(&actual_refs, &actual_refs_len, remote); + verify_remote_refs(actual_refs, actual_refs_len, expected_refs, expected_refs_len); } /** @@ -257,7 +256,8 @@ failed: void test_online_push__initialize(void) { git_vector delete_specs = GIT_VECTOR_INIT; - size_t i; + const git_remote_head **heads; + size_t i, heads_len; char *curr_del_spec; _repo = cl_git_sandbox_init("push_src"); @@ -314,7 +314,8 @@ void test_online_push__initialize(void) * order to delete the remote branch pointed to by HEAD (usually master). * See: https://raw.github.com/git/git/master/Documentation/RelNotes/1.7.0.txt */ - cl_git_pass(git_remote_ls(_remote, delete_ref_cb, &delete_specs)); + cl_git_pass(git_remote_ls(&heads, &heads_len, _remote)); + cl_git_pass(create_deletion_refspecs(&delete_specs, heads, heads_len)); if (delete_specs.length) { git_push *push; diff --git a/tests-clar/online/push_util.c b/tests-clar/online/push_util.c index 2e457844d..038c144db 100644 --- a/tests-clar/online/push_util.c +++ b/tests-clar/online/push_util.c @@ -44,20 +44,23 @@ int record_update_tips_cb(const char *refname, const git_oid *a, const git_oid * return 0; } -int delete_ref_cb(git_remote_head *head, void *payload) +int create_deletion_refspecs(git_vector *out, const git_remote_head **heads, size_t heads_len) { - git_vector *delete_specs = (git_vector *)payload; git_buf del_spec = GIT_BUF_INIT; + size_t i; - /* Ignore malformed ref names (which also saves us from tag^{} */ - if (!git_reference_is_valid_name(head->name)) - return 0; - - /* Create a refspec that deletes a branch in the remote */ - if (strcmp(head->name, "refs/heads/master")) { - cl_git_pass(git_buf_putc(&del_spec, ':')); - cl_git_pass(git_buf_puts(&del_spec, head->name)); - cl_git_pass(git_vector_insert(delete_specs, git_buf_detach(&del_spec))); + for (i = 0; i < heads_len; i++) { + const git_remote_head *head = heads[i]; + /* Ignore malformed ref names (which also saves us from tag^{} */ + if (!git_reference_is_valid_name(head->name)) + return 0; + + /* Create a refspec that deletes a branch in the remote */ + if (strcmp(head->name, "refs/heads/master")) { + cl_git_pass(git_buf_putc(&del_spec, ':')); + cl_git_pass(git_buf_puts(&del_spec, head->name)); + cl_git_pass(git_vector_insert(out, git_buf_detach(&del_spec))); + } } return 0; @@ -69,26 +72,28 @@ int record_ref_cb(git_remote_head *head, void *payload) return git_vector_insert(refs, head); } -void verify_remote_refs(git_vector *actual_refs, const expected_ref expected_refs[], size_t expected_refs_len) +void verify_remote_refs(const git_remote_head *actual_refs[], size_t actual_refs_len, const expected_ref expected_refs[], size_t expected_refs_len) { size_t i, j = 0; git_buf msg = GIT_BUF_INIT; - git_remote_head *actual; + const git_remote_head *actual; char *oid_str; bool master_present = false; /* We don't care whether "master" is present on the other end or not */ - git_vector_foreach(actual_refs, i, actual) { + for (i = 0; i < actual_refs_len; i++) { + actual = actual_refs[i]; if (!strcmp(actual->name, "refs/heads/master")) { master_present = true; break; } } - if (expected_refs_len + (master_present ? 1 : 0) != actual_refs->length) + if (expected_refs_len + (master_present ? 1 : 0) != actual_refs_len) goto failed; - git_vector_foreach(actual_refs, i, actual) { + for (i = 0; i < actual_refs_len; i++) { + actual = actual_refs[i]; if (master_present && !strcmp(actual->name, "refs/heads/master")) continue; @@ -111,7 +116,8 @@ failed: } git_buf_puts(&msg, "\nACTUAL:\n"); - git_vector_foreach(actual_refs, i, actual) { + for (i = 0; i < actual_refs_len; i++) { + actual = actual_refs[i]; if (master_present && !strcmp(actual->name, "refs/heads/master")) continue; diff --git a/tests-clar/online/push_util.h b/tests-clar/online/push_util.h index 64f02cf2f..a7207c49e 100644 --- a/tests-clar/online/push_util.h +++ b/tests-clar/online/push_util.h @@ -41,12 +41,13 @@ void record_callbacks_data_clear(record_callbacks_data *data); int record_update_tips_cb(const char *refname, const git_oid *a, const git_oid *b, void *data); /** - * Callback for git_remote_list that adds refspecs to delete each ref + * Create a set of refspecs that deletes each of the inputs * - * @param head a ref on the remote - * @param payload a git_push instance + * @param out the vector in which to store the refspecs + * @param heads the remote heads + * @param heads_len the size of the array */ -int delete_ref_cb(git_remote_head *head, void *payload); +int create_deletion_refspecs(git_vector *out, const git_remote_head **heads, size_t heads_len); /** * Callback for git_remote_list that adds refspecs to vector @@ -60,10 +61,11 @@ int record_ref_cb(git_remote_head *head, void *payload); * Verifies that refs on remote stored by record_ref_cb match the expected * names, oids, and order. * - * @param actual_refs actual refs stored by record_ref_cb() + * @param actual_refs actual refs in the remote + * @param actual_refs_len length of actual_refs * @param expected_refs expected remote refs * @param expected_refs_len length of expected_refs */ -void verify_remote_refs(git_vector *actual_refs, const expected_ref expected_refs[], size_t expected_refs_len); +void verify_remote_refs(const git_remote_head *actual_refs[], size_t actual_refs_len, const expected_ref expected_refs[], size_t expected_refs_len); #endif /* INCLUDE_cl_push_util_h__ */ |