diff --git a/include/git2/remote.h b/include/git2/remote.h index 54116c22e..87f84f8e0 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -28,16 +28,18 @@ GIT_BEGIN_DECL */ /** - * Create a new unnamed remote + * Create a remote in memory * - * Useful when you don't want to store the remote + * Create a remote with the default refspecs in memory. You can use + * this when you have a URL instead of a remote's name. * * @param out pointer to the new remote object * @param repo the associtated repository * @param url the remote repository's URL + * @param name the remote's name * @return GIT_SUCCESS or an error code */ -int git_remote_new(git_remote **out, git_repository *repo, const char *url); +int git_remote_new(git_remote **out, git_repository *repo, const char *url, const char *name); /** * Get the information for a particular remote @@ -106,21 +108,14 @@ GIT_EXTERN(int) git_remote_connect(struct git_remote *remote, int direction); */ GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headarray *refs); -/** - * Negotiate what data needs to be exchanged to synchroize the remtoe - * and local references - * - * @param remote the remote you want to negotiate with - */ -GIT_EXTERN(int) git_remote_negotiate(git_remote *remote); - /** * Download the packfile * - * The packfile is downloaded with a temporary filename, as it's final - * name is not known yet. If there was no packfile needed (all the - * objects were available locally), filename will be NULL and the - * function will return success. + * Negotiate what objects should be downloaded and download the + * packfile with those objects. The packfile is downloaded with a + * temporary filename, as it's final name is not known yet. If there + * was no packfile needed (all the objects were available locally), + * filename will be NULL and the function will return success. * * @param remote the remote to download from * @param filename where to store the temproray filename @@ -128,6 +123,26 @@ GIT_EXTERN(int) git_remote_negotiate(git_remote *remote); */ GIT_EXTERN(int) git_remote_download(char **filename, git_remote *remote); +/** + * Check whether the remote is connected + * + * Check whether the remote's underlying transport is connected to the + * remote host. + * + * @return 1 if it's connected, 0 otherwise. + */ +GIT_EXTERN(int) git_remote_connected(git_remote *remote); + +/** + * Disconnect from the remote + * + * Close the connection to the remote and free the underlying + * transport. + * + * @param remote the remote to disconnect from + */ +GIT_EXTERN(void) git_remote_disconnect(git_remote *remote); + /** * Free the memory associated with a remote * diff --git a/src/protocol.c b/src/protocol.c new file mode 100644 index 000000000..1f39f105b --- /dev/null +++ b/src/protocol.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2009-2011 the libgit2 contributors + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#include "common.h" +#include "protocol.h" +#include "pkt.h" +#include "buffer.h" + +int git_protocol_store_refs(git_protocol *p, const char *data, size_t len) +{ + git_buf *buf = &p->buf; + git_vector *refs = p->refs; + int error; + const char *line_end, *ptr; + + if (len == 0) { /* EOF */ + if (buf->size != 0) + return p->error = git__throw(GIT_ERROR, "EOF and unprocessed data"); + else + return 0; + } + + git_buf_put(buf, data, len); + ptr = buf->ptr; + while (1) { + git_pkt *pkt; + + if (buf->size == 0) + return 0; + + error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->size); + if (error == GIT_ESHORTBUFFER) + return 0; /* Ask for more */ + if (error < GIT_SUCCESS) + return p->error = git__rethrow(error, "Failed to parse pkt-line"); + + git_buf_consume(buf, line_end); + error = git_vector_insert(refs, pkt); + if (error < GIT_SUCCESS) + return p->error = git__rethrow(error, "Failed to add pkt to list"); + + if (pkt->type == GIT_PKT_FLUSH) + p->flush = 1; + } + + return error; +} diff --git a/src/protocol.h b/src/protocol.h new file mode 100644 index 000000000..e3315738a --- /dev/null +++ b/src/protocol.h @@ -0,0 +1,23 @@ +/* + * Copyright (C) 2009-2011 the libgit2 contributors + * + * This file is part of libgit2, distributed under the GNU GPL v2 with + * a Linking Exception. For full terms see the included COPYING file. + */ +#ifndef INCLUDE_protocol_h__ +#define INCLUDE_protocol_h__ + +#include "transport.h" +#include "buffer.h" + +typedef struct { + git_transport *transport; + git_vector *refs; + git_buf buf; + int error; + unsigned int flush :1; +} git_protocol; + +int git_protocol_store_refs(git_protocol *p, const char *data, size_t len); + +#endif diff --git a/src/refspec.c b/src/refspec.c index e60e8f5b5..62683e7b7 100644 --- a/src/refspec.c +++ b/src/refspec.c @@ -23,8 +23,13 @@ int git_refspec_parse(git_refspec *refspec, const char *str) } delim = strchr(str, ':'); - if (delim == NULL) - return git__throw(GIT_EOBJCORRUPTED, "Failed to parse refspec. No ':'"); + if (delim == NULL) { + refspec->src = git__strdup(str); + if (refspec->src == NULL) + return GIT_ENOMEM; + + return GIT_SUCCESS; + } refspec->src = git__strndup(str, delim - str); if (refspec->src == NULL) diff --git a/src/refspec.h b/src/refspec.h index 58f3fe472..7c389719b 100644 --- a/src/refspec.h +++ b/src/refspec.h @@ -10,9 +10,12 @@ #include "git2/refspec.h" struct git_refspec { - int force; + struct git_refspec *next; char *src; char *dst; + unsigned int force :1, + pattern :1, + matching :1; }; int git_refspec_parse(struct git_refspec *refspec, const char *str); diff --git a/src/remote.c b/src/remote.c index 3ff08a21e..e0c127bd3 100644 --- a/src/remote.c +++ b/src/remote.c @@ -56,22 +56,34 @@ static int parse_remote_refspec(git_config *cfg, git_refspec *refspec, const cha return refspec_parse(refspec, val); } -int git_remote_new(git_remote **out, git_repository *repo, const char *url) +int git_remote_new(git_remote **out, git_repository *repo, const char *url, const char *name) { git_remote *remote; + if (url == NULL) + return git__throw(GIT_EINVALIDARGS, "No URL was given"); + remote = git__malloc(sizeof(git_remote)); if (remote == NULL) return GIT_ENOMEM; memset(remote, 0x0, sizeof(git_remote)); remote->repo = repo; + remote->url = git__strdup(url); if (remote->url == NULL) { git__free(remote); return GIT_ENOMEM; } + if (name != NULL) { + remote->name = git__strdup(name); + if (remote->name == NULL) { + git__free(remote); + return GIT_ENOMEM; + } + } + *out = remote; return GIT_SUCCESS; } @@ -206,13 +218,13 @@ int git_remote_ls(git_remote *remote, git_headarray *refs) return remote->transport->ls(remote->transport, refs); } -int git_remote_negotiate(git_remote *remote) -{ - return git_fetch_negotiate(remote); -} - int git_remote_download(char **filename, git_remote *remote) { + int error; + + if ((error = git_fetch_negotiate(remote)) < 0) + return git__rethrow(error, "Error negotiating"); + return git_fetch_download_pack(filename, remote); } @@ -255,6 +267,21 @@ int git_remote_update_tips(struct git_remote *remote) return GIT_SUCCESS; } +int git_remote_connected(git_remote *remote) +{ + return remote->transport == NULL ? 0 : remote->transport->connected; +} + +void git_remote_disconnect(git_remote *remote) +{ + if (remote->transport != NULL) { + if (remote->transport->connected) + remote->transport->close(remote->transport); + + remote->transport->free(remote->transport); + } +} + void git_remote_free(git_remote *remote) { if (remote == NULL) @@ -266,11 +293,6 @@ void git_remote_free(git_remote *remote) git__free(remote->push.dst); git__free(remote->url); git__free(remote->name); - if (remote->transport != NULL) { - if (remote->transport->connected) - remote->transport->close(remote->transport); - - remote->transport->free(remote->transport); - } + git_remote_disconnect(remote); git__free(remote); } diff --git a/src/transports/git.c b/src/transports/git.c index c2014529b..2ee2e4831 100644 --- a/src/transports/git.c +++ b/src/transports/git.c @@ -20,9 +20,11 @@ #include "filebuf.h" #include "repository.h" #include "fetch.h" +#include "protocol.h" typedef struct { git_transport parent; + git_protocol proto; GIT_SOCKET socket; git_vector refs; git_remote_head **heads; @@ -126,11 +128,7 @@ static int do_connect(transport_git *t, const char *url) static int store_refs(transport_git *t) { gitno_buffer *buf = &t->buf; - git_vector *refs = &t->refs; int error = GIT_SUCCESS; - const char *line_end, *ptr; - git_pkt *pkt; - while (1) { error = gitno_recv(buf); @@ -139,34 +137,20 @@ static int store_refs(transport_git *t) if (error == GIT_SUCCESS) /* Orderly shutdown, so exit */ return GIT_SUCCESS; - ptr = buf->data; - while (1) { - if (buf->offset == 0) - break; - error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->offset); - /* - * If the error is GIT_ESHORTBUFFER, it means the buffer - * isn't long enough to satisfy the request. Break out and - * wait for more input. - * On any other error, fail. - */ - if (error == GIT_ESHORTBUFFER) { - break; - } - if (error < GIT_SUCCESS) { - return error; - } + error = git_protocol_store_refs(&t->proto, buf->data, buf->offset); + if (error == GIT_ESHORTBUFFER) { + gitno_consume_n(buf, buf->len); + continue; + } - /* Get rid of the part we've used already */ - gitno_consume(buf, line_end); + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to store refs"); - error = git_vector_insert(refs, pkt); - if (error < GIT_SUCCESS) - return error; - - if (pkt->type == GIT_PKT_FLUSH) - return GIT_SUCCESS; + gitno_consume_n(buf, buf->offset); + if (t->proto.flush) { /* No more refs */ + t->proto.flush = 0; + return GIT_SUCCESS; } } @@ -476,6 +460,7 @@ static void git_free(git_transport *transport) git_vector_free(refs); git__free(t->heads); + git_buf_free(&t->proto.buf); git__free(t->parent.url); git__free(t); } @@ -501,6 +486,8 @@ int git_transport_git(git_transport **out) t->parent.download_pack = git_download_pack; t->parent.close = git_close; t->parent.free = git_free; + t->proto.refs = &t->refs; + t->proto.transport = (git_transport *) t; *out = (git_transport *) t; diff --git a/src/transports/http.c b/src/transports/http.c index 66b6f252c..ae0c56a73 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -19,6 +19,7 @@ #include "fetch.h" #include "filebuf.h" #include "repository.h" +#include "protocol.h" enum last_cb { NONE, @@ -28,6 +29,7 @@ enum last_cb { typedef struct { git_transport parent; + git_protocol proto; git_vector refs; git_vector common; int socket; @@ -186,47 +188,8 @@ static int on_headers_complete(http_parser *parser) static int on_body_store_refs(http_parser *parser, const char *str, size_t len) { transport_http *t = (transport_http *) parser->data; - git_buf *buf = &t->buf; - git_vector *refs = &t->refs; - int error; - const char *line_end, *ptr; - static int first_pkt = 1; - if (len == 0) { /* EOF */ - if (buf->size != 0) - return t->error = git__throw(GIT_ERROR, "EOF and unprocessed data"); - else - return 0; - } - - git_buf_put(buf, str, len); - ptr = buf->ptr; - while (1) { - git_pkt *pkt; - - if (buf->size == 0) - return 0; - - error = git_pkt_parse_line(&pkt, ptr, &line_end, buf->size); - if (error == GIT_ESHORTBUFFER) - return 0; /* Ask for more */ - if (error < GIT_SUCCESS) - return t->error = git__rethrow(error, "Failed to parse pkt-line"); - - git_buf_consume(buf, line_end); - - if (first_pkt) { - first_pkt = 0; - if (pkt->type != GIT_PKT_COMMENT) - return t->error = git__throw(GIT_EOBJCORRUPTED, "Not a valid smart HTTP response"); - } - - error = git_vector_insert(refs, pkt); - if (error < GIT_SUCCESS) - return t->error = git__rethrow(error, "Failed to add pkt to list"); - } - - return error; + return git_protocol_store_refs(&t->proto, str, len); } static int on_message_complete(http_parser *parser) @@ -243,6 +206,7 @@ static int store_refs(transport_http *t) http_parser_settings settings; char buffer[1024]; gitno_buffer buf; + git_pkt *pkt; http_parser_init(&t->parser, HTTP_RESPONSE); t->parser.data = t; @@ -273,6 +237,12 @@ static int store_refs(transport_http *t) return GIT_SUCCESS; } + pkt = git_vector_get(&t->refs, 0); + if (pkt == NULL || pkt->type != GIT_PKT_COMMENT) + return t->error = git__throw(GIT_EOBJCORRUPTED, "Not a valid smart HTTP response"); + else + git_vector_remove(&t->refs, 0); + return error; } @@ -750,6 +720,7 @@ static void http_free(git_transport *transport) } git_vector_free(common); git_buf_free(&t->buf); + git_buf_free(&t->proto.buf); git__free(t->heads); git__free(t->content_type); git__free(t->host); @@ -775,6 +746,8 @@ int git_transport_http(git_transport **out) t->parent.download_pack = http_download_pack; t->parent.close = http_close; t->parent.free = http_free; + t->proto.refs = &t->refs; + t->proto.transport = (git_transport *) t; #ifdef GIT_WIN32 /* on win32, the WSA context needs to be initialized