diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/fetch.c | 110 | ||||
-rw-r--r-- | src/pkt.c | 24 | ||||
-rw-r--r-- | src/pkt.h | 4 | ||||
-rw-r--r-- | src/remote.c | 35 | ||||
-rw-r--r-- | src/remote.h | 2 | ||||
-rw-r--r-- | src/transport.c | 14 | ||||
-rw-r--r-- | src/transport.h | 11 | ||||
-rw-r--r-- | src/transports/git.c | 23 | ||||
-rw-r--r-- | src/transports/http.c | 17 | ||||
-rw-r--r-- | src/transports/local.c | 167 |
10 files changed, 201 insertions, 206 deletions
diff --git a/src/fetch.c b/src/fetch.c index 93f0980ca..f447248c5 100644 --- a/src/fetch.c +++ b/src/fetch.c @@ -18,30 +18,46 @@ #include "fetch.h" #include "netops.h" -static int filter_wants(git_remote *remote) -{ - git_vector list; - git_headarray refs; - git_remote_head *head; - git_transport *t = remote->transport; - git_odb *odb = NULL; +struct filter_payload { + git_remote *remote; const git_refspec *spec; + git_odb *odb; + int found_head; +}; + +static int filter_ref__cb(git_remote_head *head, void *payload) +{ + struct filter_payload *p = payload; int error; - unsigned int i = 0; - error = git_vector_init(&list, 16, NULL); - if (error < GIT_SUCCESS) - return error; + if (!p->found_head && strcmp(head->name, GIT_HEAD_FILE) == 0) { + p->found_head = 1; + } else { + /* If it doesn't match the refpec, we don't want it */ + error = git_refspec_src_match(p->spec, head->name); - error = t->ls(t, &refs); - if (error < GIT_SUCCESS) { - error = git__rethrow(error, "Failed to get remote ref list"); - goto cleanup; + if (error == GIT_ENOMATCH) + return GIT_SUCCESS; + + if (error < GIT_SUCCESS) + return git__rethrow(error, "Error matching remote ref name"); } - error = git_repository_odb__weakptr(&odb, remote->repo); - if (error < GIT_SUCCESS) - goto cleanup; + /* If we have the object, mark it so we don't ask for it */ + if (git_odb_exists(p->odb, &head->oid)) + head->local = 1; + else + p->remote->need_pack = 1; + + return git_vector_insert(&p->remote->refs, head); +} + +static int filter_wants(git_remote *remote) +{ + int error; + struct filter_payload p; + + git_vector_clear(&remote->refs); /* * The fetch refspec can be NULL, and what this means is that the @@ -49,56 +65,15 @@ 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. */ - spec = git_remote_fetchspec(remote); + p.spec = git_remote_fetchspec(remote); + p.found_head = 0; + p.remote = remote; - /* - * We need to handle HEAD separately, as we always want it, but it - * probably won't matcht he refspec. - */ - head = refs.heads[0]; - if (refs.len > 0 && !strcmp(head->name, GIT_HEAD_FILE)) { - if (git_odb_exists(odb, &head->oid)) - head->local = 1; - else - remote->need_pack = 1; - - i = 1; - error = git_vector_insert(&list, refs.heads[0]); - if (error < GIT_SUCCESS) - goto cleanup; - } - - for (; i < refs.len; ++i) { - head = refs.heads[i]; - - /* If it doesn't match the refpec, we don't want it */ - error = git_refspec_src_match(spec, head->name); - if (error == GIT_ENOMATCH) - continue; - if (error < GIT_SUCCESS) { - error = git__rethrow(error, "Error matching remote ref name"); - goto cleanup; - } - - /* If we have the object, mark it so we don't ask for it */ - if (git_odb_exists(odb, &head->oid)) - head->local = 1; - else - remote->need_pack = 1; - - error = git_vector_insert(&list, head); - if (error < GIT_SUCCESS) - goto cleanup; - } - - remote->refs.len = list.length; - remote->refs.heads = (git_remote_head **) list.contents; - - return GIT_SUCCESS; + error = git_repository_odb__weakptr(&p.odb, remote->repo); + if (error < GIT_SUCCESS) + return error; -cleanup: - git_vector_free(&list); - return error; + return remote->transport->ls(remote->transport, &filter_ref__cb, &p); } /* @@ -116,8 +91,9 @@ int git_fetch_negotiate(git_remote *remote) return git__rethrow(error, "Failed to filter the reference list for wants"); /* Don't try to negotiate when we don't want anything */ - if (remote->refs.len == 0) + if (remote->refs.length == 0) return GIT_SUCCESS; + if (!remote->need_pack) return GIT_SUCCESS; @@ -316,30 +316,30 @@ static int send_want_with_caps(git_remote_head *head, git_transport_caps *caps, * is overwrite the OID each time. */ -int git_pkt_buffer_wants(git_headarray *refs, git_transport_caps *caps, git_buf *buf) +int git_pkt_buffer_wants(const git_vector *refs, git_transport_caps *caps, git_buf *buf) { unsigned int i = 0; int error; git_remote_head *head; if (caps->common) { - for (; i < refs->len; ++i) { - head = refs->heads[i]; + for (; i < refs->length; ++i) { + head = refs->contents[i]; if (!head->local) break; } - error = buffer_want_with_caps(refs->heads[i], caps, buf); + error = buffer_want_with_caps(refs->contents[i], caps, buf); if (error < GIT_SUCCESS) return git__rethrow(error, "Failed to buffer want with caps"); i++; } - for (; i < refs->len; ++i) { + for (; i < refs->length; ++i) { char oid[GIT_OID_HEXSZ]; - head = refs->heads[i]; + head = refs->contents[i]; if (head->local) continue; @@ -352,7 +352,7 @@ int git_pkt_buffer_wants(git_headarray *refs, git_transport_caps *caps, git_buf return git_pkt_buffer_flush(buf); } -int git_pkt_send_wants(git_headarray *refs, git_transport_caps *caps, int fd) +int git_pkt_send_wants(const git_vector *refs, git_transport_caps *caps, int fd) { unsigned int i = 0; int error = GIT_SUCCESS; @@ -365,15 +365,15 @@ int git_pkt_send_wants(git_headarray *refs, git_transport_caps *caps, int fd) /* If there are common caps, find the first one */ if (caps->common) { - for (; i < refs->len; ++i) { - head = refs->heads[i]; + for (; i < refs->length; ++i) { + head = refs->contents[i]; if (head->local) continue; else break; } - error = send_want_with_caps(refs->heads[i], caps, fd); + error = send_want_with_caps(refs->contents[i], caps, fd); if (error < GIT_SUCCESS) return git__rethrow(error, "Failed to send want pkt with caps"); /* Increase it here so it's correct whether we run this or not */ @@ -381,8 +381,8 @@ int git_pkt_send_wants(git_headarray *refs, git_transport_caps *caps, int fd) } /* Continue from where we left off */ - for (; i < refs->len; ++i) { - head = refs->heads[i]; + for (; i < refs->length; ++i) { + head = refs->contents[i]; if (head->local) continue; @@ -68,8 +68,8 @@ int git_pkt_buffer_flush(git_buf *buf); int git_pkt_send_flush(int s); int git_pkt_buffer_done(git_buf *buf); int git_pkt_send_done(int s); -int git_pkt_buffer_wants(git_headarray *refs, git_transport_caps *caps, git_buf *buf); -int git_pkt_send_wants(git_headarray *refs, git_transport_caps *caps, int fd); +int git_pkt_buffer_wants(const git_vector *refs, git_transport_caps *caps, git_buf *buf); +int git_pkt_send_wants(const git_vector *refs, git_transport_caps *caps, int fd); int git_pkt_buffer_have(git_oid *oid, git_buf *buf); int git_pkt_send_have(git_oid *oid, int fd); void git_pkt_free(git_pkt *pkt); diff --git a/src/remote.c b/src/remote.c index c6a9173af..75e861681 100644 --- a/src/remote.c +++ b/src/remote.c @@ -70,16 +70,21 @@ int git_remote_new(git_remote **out, git_repository *repo, const char *url, cons memset(remote, 0x0, sizeof(git_remote)); remote->repo = repo; + if (git_vector_init(&remote->refs, 32, NULL) < 0) { + git_remote_free(remote); + return GIT_ENOMEM; + } + remote->url = git__strdup(url); if (remote->url == NULL) { - git__free(remote); + git_remote_free(remote); return GIT_ENOMEM; } if (name != NULL) { remote->name = git__strdup(name); if (remote->name == NULL) { - git__free(remote); + git_remote_free(remote); return GIT_ENOMEM; } } @@ -113,6 +118,11 @@ int git_remote_load(git_remote **out, git_repository *repo, const char *name) goto cleanup; } + if (git_vector_init(&remote->refs, 32, NULL) < 0) { + error = GIT_ENOMEM; + goto cleanup; + } + /* "fetch" is the longest var name we're interested in */ buf_len = strlen("remote.") + strlen(".fetch") + strlen(name) + 1; buf = git__malloc(buf_len); @@ -227,10 +237,14 @@ cleanup: return error; } -int git_remote_ls(git_remote *remote, git_headarray *refs) +int git_remote_ls(git_remote *remote, git_headlist_cb list_cb, void *payload) { - assert(remote && refs); - return remote->transport->ls(remote->transport, refs); + assert(remote); + + if (!remote->transport) + return git__throw(GIT_ERROR, "The remote is not connected"); + + return remote->transport->ls(remote->transport, list_cb, payload); } int git_remote_download(char **filename, git_remote *remote) @@ -250,7 +264,7 @@ int git_remote_update_tips(git_remote *remote) int error = GIT_SUCCESS; unsigned int i = 0; char refname[GIT_PATH_MAX]; - git_headarray *refs = &remote->refs; + git_vector *refs = &remote->refs; git_remote_head *head; git_reference *ref; struct git_refspec *spec = &remote->fetch; @@ -259,11 +273,11 @@ int git_remote_update_tips(git_remote *remote) memset(refname, 0x0, sizeof(refname)); - if (refs->len == 0) + if (refs->length == 0) return GIT_SUCCESS; /* HEAD is only allowed to be the first in the list */ - head = refs->heads[0]; + head = refs->contents[0]; if (!strcmp(head->name, GIT_HEAD_FILE)) { error = git_reference_create_oid(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1); i = 1; @@ -272,8 +286,8 @@ int git_remote_update_tips(git_remote *remote) return git__rethrow(error, "Failed to update FETCH_HEAD"); } - for (; i < refs->len; ++i) { - head = refs->heads[i]; + for (; i < refs->length; ++i) { + head = refs->contents[i]; error = git_refspec_transform(refname, sizeof(refname), spec, head->name); if (error < GIT_SUCCESS) @@ -319,6 +333,7 @@ void git_remote_free(git_remote *remote) git__free(remote->push.dst); git__free(remote->url); git__free(remote->name); + git_vector_free(&remote->refs); git_remote_disconnect(remote); git__free(remote); } diff --git a/src/remote.h b/src/remote.h index 4b1db6c4e..a24e14845 100644 --- a/src/remote.h +++ b/src/remote.h @@ -14,7 +14,7 @@ struct git_remote { char *name; char *url; - git_headarray refs; + git_vector refs; struct git_refspec fetch; struct git_refspec push; git_transport *transport; diff --git a/src/transport.c b/src/transport.c index 0d67e1967..d836561b4 100644 --- a/src/transport.c +++ b/src/transport.c @@ -6,7 +6,7 @@ */ #include "common.h" #include "git2/types.h" -#include "git2/transport.h" +#include "git2/remote.h" #include "git2/net.h" #include "transport.h" @@ -49,11 +49,6 @@ int git_transport_dummy(git_transport **GIT_UNUSED(transport)) return git__throw(GIT_ENOTIMPLEMENTED, "This protocol isn't implemented. Sorry"); } -int git_transport_valid_url(const char *url) -{ - return transport_find_fn(url) != NULL; -} - int git_transport_new(git_transport **out, const char *url) { git_transport_cb fn; @@ -81,3 +76,10 @@ int git_transport_new(git_transport **out, const char *url) return GIT_SUCCESS; } + +/* from remote.h */ +int git_remote_valid_url(const char *url) +{ + return transport_find_fn(url) != NULL; +} + diff --git a/src/transport.h b/src/transport.h index 23b83b690..2ed8ad32a 100644 --- a/src/transport.h +++ b/src/transport.h @@ -7,7 +7,6 @@ #ifndef INCLUDE_transport_h__ #define INCLUDE_transport_h__ -#include "git2/transport.h" #include "git2/net.h" #include "vector.h" @@ -61,7 +60,7 @@ struct git_transport { /** * Give a list of references, useful for ls-remote */ - int (*ls)(struct git_transport *transport, git_headarray *headarray); + int (*ls)(struct git_transport *transport, git_headlist_cb list_cb, void *opaque); /** * Push the changes over */ @@ -74,7 +73,7 @@ struct git_transport { * Negotiate the minimal amount of objects that need to be * retrieved */ - int (*negotiate_fetch)(struct git_transport *transport, git_repository *repo, git_headarray *list); + int (*negotiate_fetch)(struct git_transport *transport, git_repository *repo, const git_vector *wants); /** * Send a flush */ @@ -97,9 +96,15 @@ struct git_transport { void (*free)(struct git_transport *transport); }; + +int git_transport_new(struct git_transport **transport, const char *url); int git_transport_local(struct git_transport **transport); int git_transport_git(struct git_transport **transport); int git_transport_http(struct git_transport **transport); int git_transport_dummy(struct git_transport **transport); +int git_transport_valid_url(const char *url); + +typedef struct git_transport git_transport; +typedef int (*git_transport_cb)(git_transport **transport); #endif diff --git a/src/transports/git.c b/src/transports/git.c index 2ee2e4831..bdb94d090 100644 --- a/src/transports/git.c +++ b/src/transports/git.c @@ -226,32 +226,30 @@ cleanup: return error; } -static int git_ls(git_transport *transport, git_headarray *array) +static int git_ls(git_transport *transport, git_headlist_cb list_cb, void *opaque) { transport_git *t = (transport_git *) transport; git_vector *refs = &t->refs; - int len = 0; unsigned int i; + git_pkt *p = NULL; - array->heads = git__calloc(refs->length, sizeof(git_remote_head *)); - if (array->heads == NULL) - return GIT_ENOMEM; + git_vector_foreach(refs, i, p) { + git_pkt_ref *pkt = NULL; - for (i = 0; i < refs->length; ++i) { - git_pkt *p = git_vector_get(refs, i); if (p->type != GIT_PKT_REF) continue; - ++len; - array->heads[i] = &(((git_pkt_ref *) p)->head); + pkt = (git_pkt_ref *)p; + + if (list_cb(&pkt->head, opaque) < 0) + return git__throw(GIT_ERROR, + "The user callback returned an error code"); } - array->len = len; - t->heads = array->heads; return GIT_SUCCESS; } -static int git_negotiate_fetch(git_transport *transport, git_repository *repo, git_headarray *wants) +static int git_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants) { transport_git *t = (transport_git *) transport; git_revwalk *walk; @@ -290,6 +288,7 @@ static int git_negotiate_fetch(git_transport *transport, git_repository *repo, g if (git_reference_type(ref) == GIT_REF_SYMBOLIC) continue; + error = git_revwalk_push(walk, git_reference_oid(ref)); if (error < GIT_ERROR) { error = git__rethrow(error, "Failed to push %s", refs.strings[i]); diff --git a/src/transports/http.c b/src/transports/http.c index ae0c56a73..e463a0f59 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -301,29 +301,22 @@ cleanup: return error; } -static int http_ls(git_transport *transport, git_headarray *array) +static int http_ls(git_transport *transport, git_headlist_cb list_cb, void *opaque) { transport_http *t = (transport_http *) transport; git_vector *refs = &t->refs; unsigned int i; - int len = 0; git_pkt_ref *p; - array->heads = git__calloc(refs->length, sizeof(git_remote_head*)); - if (array->heads == NULL) - return GIT_ENOMEM; - git_vector_foreach(refs, i, p) { if (p->type != GIT_PKT_REF) continue; - array->heads[len] = &p->head; - len++; + if (list_cb(&p->head, opaque) < 0) + return git__throw(GIT_ERROR, + "The user callback returned an error code"); } - array->len = len; - t->heads = array->heads; - return GIT_SUCCESS; } @@ -470,7 +463,7 @@ cleanup: return error; } -static int http_negotiate_fetch(git_transport *transport, git_repository *repo, git_headarray *wants) +static int http_negotiate_fetch(git_transport *transport, git_repository *repo, const git_vector *wants) { transport_http *t = (transport_http *) transport; int error; diff --git a/src/transports/local.c b/src/transports/local.c index afc17e55f..f50a96173 100644 --- a/src/transports/local.c +++ b/src/transports/local.c @@ -6,7 +6,6 @@ */ #include "common.h" #include "git2/types.h" -#include "git2/transport.h" #include "git2/net.h" #include "git2/repository.h" #include "git2/object.h" @@ -18,39 +17,10 @@ typedef struct { git_transport parent; git_repository *repo; - git_vector *refs; + git_vector refs; } transport_local; -/* - * Try to open the url as a git directory. The direction doesn't - * matter in this case because we're calulating the heads ourselves. - */ -static int local_connect(git_transport *transport, int GIT_UNUSED(direction)) -{ - git_repository *repo; - int error; - transport_local *t = (transport_local *) transport; - const char *path; - const char file_prefix[] = "file://"; - GIT_UNUSED_ARG(direction); - - /* The repo layer doesn't want the prefix */ - if (!git__prefixcmp(transport->url, file_prefix)) - path = transport->url + strlen(file_prefix); - else - path = transport->url; - - error = git_repository_open(&repo, path); - if (error < GIT_SUCCESS) - return git__rethrow(error, "Failed to open remote"); - - t->repo = repo; - t->parent.connected = 1; - - return GIT_SUCCESS; -} - -static int add_ref(const char *name, git_repository *repo, git_vector *vec) +static int add_ref(transport_local *t, const char *name) { const char peeled[] = "^{}"; git_remote_head *head; @@ -68,7 +38,7 @@ static int add_ref(const char *name, git_repository *repo, git_vector *vec) goto out; } - error = git_reference_lookup(&ref, repo, name); + error = git_reference_lookup(&ref, t->repo, name); if (error < GIT_SUCCESS) goto out; @@ -78,15 +48,17 @@ static int add_ref(const char *name, git_repository *repo, git_vector *vec) git_oid_cpy(&head->oid, git_reference_oid(ref)); - error = git_vector_insert(vec, head); + error = git_vector_insert(&t->refs, head); if (error < GIT_SUCCESS) goto out; + head = NULL; + /* If it's not a tag, we don't need to try to peel it */ if (git__prefixcmp(name, GIT_REFS_TAGS_DIR)) goto out; - error = git_object_lookup(&obj, repo, &head->oid, GIT_OBJ_ANY); + error = git_object_lookup(&obj, t->repo, &head->oid, GIT_OBJ_ANY); if (error < GIT_SUCCESS) { git__rethrow(error, "Failed to lookup object"); } @@ -100,13 +72,12 @@ static int add_ref(const char *name, git_repository *repo, git_vector *vec) peel_len = strlen(name) + strlen(peeled); head->name = git__malloc(peel_len + 1); ret = p_snprintf(head->name, peel_len + 1, "%s%s", name, peeled); - if (ret >= peel_len + 1) { - error = git__throw(GIT_ERROR, "The string is magically to long"); - } + + assert(ret < peel_len + 1); git_oid_cpy(&head->oid, git_tag_target_oid((git_tag *) obj)); - error = git_vector_insert(vec, head); + error = git_vector_insert(&t->refs, head); if (error < GIT_SUCCESS) goto out; @@ -115,70 +86,108 @@ static int add_ref(const char *name, git_repository *repo, git_vector *vec) git_reference_free(resolved_ref); git_object_free(obj); - if (error < GIT_SUCCESS) { + if (head && error < GIT_SUCCESS) { git__free(head->name); git__free(head); } + return error; } -static int local_ls(git_transport *transport, git_headarray *array) +static int store_refs(transport_local *t) { int error; unsigned int i; - git_repository *repo; - git_vector *vec; - git_strarray refs; - transport_local *t = (transport_local *) transport; + git_strarray ref_names = {0}; - assert(transport && transport->connected); - - repo = t->repo; + assert(t); - error = git_reference_listall(&refs, repo, GIT_REF_LISTALL); + error = git_vector_init(&t->refs, ref_names.count, NULL); if (error < GIT_SUCCESS) - return git__rethrow(error, "Failed to list remote heads"); - - vec = git__malloc(sizeof(git_vector)); - if (vec == NULL) { - error = GIT_ENOMEM; - goto out; - } + return error; - error = git_vector_init(vec, refs.count, NULL); + error = git_reference_listall(&ref_names, t->repo, GIT_REF_LISTALL); if (error < GIT_SUCCESS) - return error; + return git__rethrow(error, "Failed to list remote heads"); /* Sort the references first */ - git__tsort((void **)refs.strings, refs.count, &git__strcmp_cb); + git__tsort((void **)ref_names.strings, ref_names.count, &git__strcmp_cb); /* Add HEAD */ - error = add_ref(GIT_HEAD_FILE, repo, vec); + error = add_ref(t, GIT_HEAD_FILE); if (error < GIT_SUCCESS) - goto out; + goto cleanup; - for (i = 0; i < refs.count; ++i) { - error = add_ref(refs.strings[i], repo, vec); + for (i = 0; i < ref_names.count; ++i) { + error = add_ref(t, ref_names.strings[i]); if (error < GIT_SUCCESS) - goto out; + goto cleanup; } - array->len = vec->length; - array->heads = (git_remote_head **)vec->contents; +cleanup: + git_strarray_free(&ref_names); + return error; +} - t->refs = vec; +static int local_ls(git_transport *transport, git_headlist_cb list_cb, void *payload) +{ + transport_local *t = (transport_local *) transport; + git_vector *refs = &t->refs; + unsigned int i; + git_remote_head *h; - out: + assert(transport && transport->connected); - git_strarray_free(&refs); + git_vector_foreach(refs, i, h) { + if (list_cb(h, payload) < 0) + return git__throw(GIT_ERROR, + "The user callback returned an error code"); + } - return error; + return GIT_SUCCESS; +} + + +/* + * Try to open the url as a git directory. The direction doesn't + * matter in this case because we're calulating the heads ourselves. + */ +static int local_connect(git_transport *transport, int GIT_UNUSED(direction)) +{ + git_repository *repo; + int error; + transport_local *t = (transport_local *) transport; + const char *path; + const char file_prefix[] = "file://"; + GIT_UNUSED_ARG(direction); + + /* The repo layer doesn't want the prefix */ + if (!git__prefixcmp(transport->url, file_prefix)) + path = transport->url + strlen(file_prefix); + else + path = transport->url; + + error = git_repository_open(&repo, path); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to open remote"); + + error = store_refs(t); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to retrieve references"); + + t->repo = repo; + t->parent.connected = 1; + + return GIT_SUCCESS; } static int local_close(git_transport *GIT_UNUSED(transport)) { - /* Nothing to do */ - GIT_UNUSED_ARG(transport); + transport_local *t = (transport_local *)transport; + + git_repository_free(t->repo); + t->repo = NULL; + return GIT_SUCCESS; } @@ -186,21 +195,17 @@ static void local_free(git_transport *transport) { unsigned int i; transport_local *t = (transport_local *) transport; - git_vector *vec = t->refs; + git_vector *vec = &t->refs; git_remote_head *h; assert(transport); - if (t->refs != NULL) { - git_vector_foreach (vec, i, h) { - git__free(h->name); - git__free(h); - } - git_vector_free(vec); - git__free(vec); + git_vector_foreach (vec, i, h) { + git__free(h->name); + git__free(h); } + git_vector_free(vec); - git_repository_free(t->repo); git__free(t->parent.url); git__free(t); } |