From 40a40e8e9daa8187450258ba538c90d70eac12fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Wed, 26 Oct 2011 18:06:36 -0700 Subject: [PATCH 1/7] net: move the reference storage to common code --- src/protocol.c | 50 ++++++++++++++++++++++++++++++++++++++++ src/protocol.h | 23 +++++++++++++++++++ src/transports/git.c | 45 +++++++++++++----------------------- src/transports/http.c | 53 +++++++++++-------------------------------- 4 files changed, 102 insertions(+), 69 deletions(-) create mode 100644 src/protocol.c create mode 100644 src/protocol.h 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/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 From 95057b85034a226ed5f9bcd7a461a296bbdb7bd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 18 Nov 2011 21:18:39 +0100 Subject: [PATCH 2/7] remote: get rid of git_remote_negotiate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There is no good reason to expose the negotiation as a different step to downloading the packfile. Signed-off-by: Carlos Martín Nieto --- include/git2/remote.h | 17 +++++------------ src/remote.c | 10 +++++----- 2 files changed, 10 insertions(+), 17 deletions(-) diff --git a/include/git2/remote.h b/include/git2/remote.h index 54116c22e..0781bb773 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -106,21 +106,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 diff --git a/src/remote.c b/src/remote.c index 3ff08a21e..6ac3bc168 100644 --- a/src/remote.c +++ b/src/remote.c @@ -206,13 +206,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); } From 617bfdf47fbe307070dcd084a4e3ea410823c88c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 18 Nov 2011 21:28:07 +0100 Subject: [PATCH 3/7] Add a name to a remote created from the API MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Make it a bit more resilient. Signed-off-by: Carlos Martín Nieto --- include/git2/remote.h | 8 +++++--- src/remote.c | 14 +++++++++++++- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/include/git2/remote.h b/include/git2/remote.h index 0781bb773..15d0aa3c0 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 diff --git a/src/remote.c b/src/remote.c index 6ac3bc168..d541cd0cb 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; } From dc9e960f4be2e2fab10ecaeb5934c236ec729d9b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 9 Oct 2011 02:59:01 +0200 Subject: [PATCH 4/7] refspec: make the structure more complete MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add a next pointer to make it a linked list and add the 'pattern' and 'matching' flags. Signed-off-by: Carlos Martín Nieto --- src/refspec.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) 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); From 0ca7ca3ef788a45293852a41c55040bc94607961 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 9 Oct 2011 03:07:53 +0200 Subject: [PATCH 5/7] refspec: allow a simple branchname MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A simple branchname as refspec is valid and we shouldn't throw an error when encountering one. Signed-off-by: Carlos Martín Nieto --- src/refspec.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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) From 4cf01e9a1a3b81d1c8bc77fcdf7e0c20792175a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 21 Nov 2011 20:44:03 +0100 Subject: [PATCH 6/7] Add git_remote_disconnect MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It can be useful to separate disconnecting from actually destroying the object. Signed-off-by: Carlos Martín Nieto --- include/git2/remote.h | 10 ++++++++++ src/remote.c | 17 +++++++++++------ 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/include/git2/remote.h b/include/git2/remote.h index 15d0aa3c0..94453db56 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -123,6 +123,16 @@ GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headarray *refs); */ GIT_EXTERN(int) git_remote_download(char **filename, 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/remote.c b/src/remote.c index d541cd0cb..c59991002 100644 --- a/src/remote.c +++ b/src/remote.c @@ -267,6 +267,16 @@ int git_remote_update_tips(struct git_remote *remote) return GIT_SUCCESS; } +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) @@ -278,11 +288,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); } From 6ac3b707b14217602152c032a11304757c88612c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Mon, 21 Nov 2011 20:48:59 +0100 Subject: [PATCH 7/7] Add git_remote_connected MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Carlos Martín Nieto --- include/git2/remote.h | 10 ++++++++++ src/remote.c | 5 +++++ 2 files changed, 15 insertions(+) diff --git a/include/git2/remote.h b/include/git2/remote.h index 94453db56..87f84f8e0 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -123,6 +123,16 @@ GIT_EXTERN(int) git_remote_ls(git_remote *remote, git_headarray *refs); */ 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 * diff --git a/src/remote.c b/src/remote.c index c59991002..e0c127bd3 100644 --- a/src/remote.c +++ b/src/remote.c @@ -267,6 +267,11 @@ 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) {