diff options
| -rw-r--r-- | .gitignore | 1 | ||||
| -rw-r--r-- | CMakeLists.txt | 2 | ||||
| -rw-r--r-- | README.md | 4 | ||||
| -rw-r--r-- | include/git2/remote.h | 48 | ||||
| -rw-r--r-- | include/git2/repository.h | 2 | ||||
| -rw-r--r-- | include/git2/transport.h | 8 | ||||
| -rw-r--r-- | include/git2/tree.h | 3 | ||||
| -rw-r--r-- | src/config_file.c | 2 | ||||
| -rw-r--r-- | src/fetch.c | 2 | ||||
| -rw-r--r-- | src/filebuf.c | 22 | ||||
| -rw-r--r-- | src/filebuf.h | 15 | ||||
| -rw-r--r-- | src/index.c | 2 | ||||
| -rw-r--r-- | src/odb_loose.c | 2 | ||||
| -rw-r--r-- | src/protocol.c | 50 | ||||
| -rw-r--r-- | src/protocol.h | 23 | ||||
| -rw-r--r-- | src/reflog.c | 2 | ||||
| -rw-r--r-- | src/refs.c | 4 | ||||
| -rw-r--r-- | src/refspec.c | 9 | ||||
| -rw-r--r-- | src/refspec.h | 5 | ||||
| -rw-r--r-- | src/remote.c | 76 | ||||
| -rw-r--r-- | src/transport.c | 36 | ||||
| -rw-r--r-- | src/transports/git.c | 45 | ||||
| -rw-r--r-- | src/transports/http.c | 53 | ||||
| -rw-r--r-- | tests-clay/buf/basic.c | 29 | ||||
| -rw-r--r-- | tests-clay/clay.h | 5 | ||||
| -rw-r--r-- | tests-clay/clay_libgit2.h | 2 | ||||
| -rw-r--r-- | tests-clay/clay_main.c | 21 | ||||
| -rw-r--r-- | tests-clay/config/stress.c | 2 | ||||
| -rw-r--r-- | tests-clay/core/filebuf.c | 54 | ||||
| -rw-r--r-- | tests-clay/index/rename.c | 2 | ||||
| -rw-r--r-- | tests/t00-core.c | 6 | ||||
| -rw-r--r-- | tests/t06-index.c | 2 | ||||
| -rw-r--r-- | tests/t15-config.c | 2 |
33 files changed, 389 insertions, 152 deletions
diff --git a/.gitignore b/.gitignore index 87ba3f34f..6594f1478 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,4 @@ msvc/Release/ CMake* *.cmake .DS_Store +*~ diff --git a/CMakeLists.txt b/CMakeLists.txt index 7655e0ba5..5505a96ca 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -64,7 +64,7 @@ IF (MSVC) SET(CMAKE_C_FLAGS_RELEASE "/MT /O2") SET(WIN_RC "src/win32/git2.rc") ELSE () - SET(CMAKE_C_FLAGS "-O2 -g -Wall -Wextra -Wstrict-aliasing=2 -Wstrict-prototypes -Wmissing-prototypes ${CMAKE_C_FLAGS}") + SET(CMAKE_C_FLAGS "-O2 -g -Wall -Wextra -Wno-missing-field-initializers -Wstrict-aliasing=2 -Wstrict-prototypes -Wmissing-prototypes ${CMAKE_C_FLAGS}") SET(CMAKE_C_FLAGS_DEBUG "-O0 -g") IF (NOT MINGW) # MinGW always does PIC and complains if we tell it to SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fPIC") @@ -20,7 +20,7 @@ What It Can Do libgit2 is already very usable. * SHA conversions, formatting and shortening -* abstracked ODB backend system +* abstracted ODB backend system * commit, tag, tree and blob parsing, editing, and write-back * tree traversal * revision walking @@ -85,7 +85,7 @@ Here are the bindings to libgit2 that are currently available: * node-gitteh (Node.js bindings) <https://github.com/libgit2/node-gitteh> * nodegit (Node.js bindings) <https://github.com/tbranyen/nodegit> * go-git (Go bindings) <https://github.com/str1ngs/go-git> -* libqgit2 (C++ QT bindings) <https://projects.kde.org/projects/playground/libs/libqgit2/> +* libqgit2 (C++ Qt bindings) <https://projects.kde.org/projects/playground/libs/libqgit2/> * libgit2-ocaml (ocaml bindings) <https://github.com/burdges/libgit2-ocaml> * Geef (Erlang bindings) <https://github.com/schacon/geef> * libgit2net (.NET bindings, low level) <https://github.com/txdv/libgit2net> diff --git a/include/git2/remote.h b/include/git2/remote.h index 54116c22e..43bbe9e1c 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 @@ -98,7 +100,8 @@ GIT_EXTERN(int) git_remote_connect(struct git_remote *remote, int direction); /** * Get a list of refs at the remote * - * The remote (or more exactly its transport) must be connected. + * The remote (or more exactly its transport) must be connected. The + * memory belongs to the remote. * * @param refs where to store the refs * @param remote the remote @@ -107,20 +110,13 @@ 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 @@ -129,6 +125,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 * * @param remote the remote to free diff --git a/include/git2/repository.h b/include/git2/repository.h index 161826b26..2e9baf6c0 100644 --- a/include/git2/repository.h +++ b/include/git2/repository.h @@ -277,7 +277,7 @@ GIT_EXTERN(const char *) git_repository_path(git_repository *repo, git_repositor * Check if a repository is bare * * @param repo Repo to test - * @return 1 if the repository is empty, 0 otherwise. + * @return 1 if the repository is bare, 0 otherwise. */ GIT_EXTERN(int) git_repository_is_bare(git_repository *repo); diff --git a/include/git2/transport.h b/include/git2/transport.h index ddae32d40..f56a2f40a 100644 --- a/include/git2/transport.h +++ b/include/git2/transport.h @@ -27,6 +27,14 @@ GIT_BEGIN_DECL */ GIT_EXTERN(int) git_transport_new(git_transport **transport, const char *url); +/** + * Return whether a string is a valid transport URL + * + * @param tranport the url to check + * @param 1 if the url is valid, 0 otherwise + */ +GIT_EXTERN(int) git_transport_valid_url(const char *url); + /** @} */ GIT_END_DECL #endif diff --git a/include/git2/tree.h b/include/git2/tree.h index bd89de34f..8ac8b1682 100644 --- a/include/git2/tree.h +++ b/include/git2/tree.h @@ -308,9 +308,10 @@ enum git_treewalk_mode { * @param tree The tree to walk * @param callback Function to call on each tree entry * @param mode Traversal mode (pre or post-order) + * @param payload Opaque pointer to be passed on each callback * @return GIT_SUCCESS or an error code */ -GIT_EXTERN(int) git_tree_walk(git_tree *walk, git_treewalk_cb callback, int mode, void *payload); +GIT_EXTERN(int) git_tree_walk(git_tree *tree, git_treewalk_cb callback, int mode, void *payload); /** @} */ GIT_END_DECL diff --git a/src/config_file.c b/src/config_file.c index aec29d4e2..87a430759 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -877,7 +877,7 @@ static int config_write(diskfile_backend *cfg, cvar_t *var) int section_matches = 0, last_section_matched = 0; char *current_section = NULL; char *var_name, *var_value, *data_start; - git_filebuf file; + git_filebuf file = GIT_FILEBUF_INIT; const char *pre_end = NULL, *post_start = NULL; /* We need to read in our own config file */ diff --git a/src/fetch.c b/src/fetch.c index af7dbaffd..a42732925 100644 --- a/src/fetch.c +++ b/src/fetch.c @@ -138,7 +138,7 @@ int git_fetch_download_pack(char **out, git_remote *remote) int git_fetch__download_pack(char **out, const char *buffered, size_t buffered_size, GIT_SOCKET fd, git_repository *repo) { - git_filebuf file; + git_filebuf file = GIT_FILEBUF_INIT; int error; char buff[1024], path[GIT_PATH_MAX]; static const char suff[] = "/objects/pack/pack-received"; diff --git a/src/filebuf.c b/src/filebuf.c index 199418032..6600bfa4b 100644 --- a/src/filebuf.c +++ b/src/filebuf.c @@ -66,13 +66,22 @@ void git_filebuf_cleanup(git_filebuf *file) if (file->digest) git_hash_free_ctx(file->digest); - git__free(file->buffer); - git__free(file->z_buf); + if (file->buffer) + git__free(file->buffer); - deflateEnd(&file->zs); + /* use the presence of z_buf to decide if we need to deflateEnd */ + if (file->z_buf) { + git__free(file->z_buf); + deflateEnd(&file->zs); + } - git__free(file->path_original); - git__free(file->path_lock); + if (file->path_original) + git__free(file->path_original); + if (file->path_lock) + git__free(file->path_lock); + + memset(file, 0x0, sizeof(git_filebuf)); + file->fd = -1; } GIT_INLINE(int) flush_buffer(git_filebuf *file) @@ -137,6 +146,9 @@ int git_filebuf_open(git_filebuf *file, const char *path, int flags) assert(file && path); + if (file->buffer) + return git__throw(GIT_EINVALIDARGS, "Tried to reopen an open filebuf"); + memset(file, 0x0, sizeof(git_filebuf)); file->buf_size = WRITE_BUFFER_SIZE; diff --git a/src/filebuf.h b/src/filebuf.h index d08505e8d..6c283bc5c 100644 --- a/src/filebuf.h +++ b/src/filebuf.h @@ -44,6 +44,21 @@ struct git_filebuf { typedef struct git_filebuf git_filebuf; +#define GIT_FILEBUF_INIT {0} + +/* The git_filebuf object lifecycle is: + * - Allocate git_filebuf, preferably using GIT_FILEBUF_INIT. + * - Call git_filebuf_open() to initialize the filebuf for use. + * - Make as many calls to git_filebuf_write(), git_filebuf_printf(), + * git_filebuf_reserve() as you like. + * - While you are writing, you may call git_filebuf_hash() to get + * the hash of all you have written so far. + * - To close the git_filebuf, you may call git_filebuf_commit() or + * git_filebuf_commit_at() to save the file, or + * git_filebuf_cleanup() to abandon the file. All of these will + * clear the git_filebuf object. + */ + int git_filebuf_write(git_filebuf *lock, const void *buff, size_t len); int git_filebuf_reserve(git_filebuf *file, void **buff, size_t len); int git_filebuf_printf(git_filebuf *file, const char *format, ...) GIT_FORMAT_PRINTF(2, 3); diff --git a/src/index.c b/src/index.c index 1a9745a2c..aad117164 100644 --- a/src/index.c +++ b/src/index.c @@ -248,7 +248,7 @@ int git_index_read(git_index *index) int git_index_write(git_index *index) { - git_filebuf file; + git_filebuf file = GIT_FILEBUF_INIT; struct stat indexst; int error; diff --git a/src/odb_loose.c b/src/odb_loose.c index 57a0b0a8e..f1789e071 100644 --- a/src/odb_loose.c +++ b/src/odb_loose.c @@ -769,7 +769,7 @@ static int loose_backend__write(git_oid *oid, git_odb_backend *_backend, const v { int error, header_len; char final_path[GIT_PATH_MAX], header[64]; - git_filebuf fbuf; + git_filebuf fbuf = GIT_FILEBUF_INIT; loose_backend *backend; backend = (loose_backend *)_backend; 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/reflog.c b/src/reflog.c index f52ae585f..fbaaaea67 100644 --- a/src/reflog.c +++ b/src/reflog.c @@ -41,7 +41,7 @@ static int reflog_write(const char *log_path, const char *oid_old, { int error; git_buf log = GIT_BUF_INIT; - git_filebuf fbuf; + git_filebuf fbuf = GIT_FILEBUF_INIT; assert(log_path && oid_old && oid_new && committer); diff --git a/src/refs.c b/src/refs.c index dfdde080f..2374cc72f 100644 --- a/src/refs.c +++ b/src/refs.c @@ -286,7 +286,7 @@ cleanup: static int loose_write(git_reference *ref) { - git_filebuf file; + git_filebuf file = GIT_FILEBUF_INIT; char ref_path[GIT_PATH_MAX]; int error; struct stat st; @@ -745,7 +745,7 @@ static int packed_sort(const void *a, const void *b) */ static int packed_write(git_repository *repo) { - git_filebuf pack_file; + git_filebuf pack_file = GIT_FILEBUF_INIT; int error; unsigned int i; char pack_file_path[GIT_PATH_MAX]; 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..a222023d8 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; + /* name is optional */ + assert(out && repo && url); + 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; } @@ -83,6 +95,8 @@ int git_remote_get(git_remote **out, git_config *cfg, const char *name) const char *val; int ret, error, buf_len; + assert(out && cfg && name); + remote = git__malloc(sizeof(git_remote)); if (remote == NULL) return GIT_ENOMEM; @@ -157,23 +171,27 @@ cleanup: return error; } -const char *git_remote_name(struct git_remote *remote) +const char *git_remote_name(git_remote *remote) { + assert(remote); return remote->name; } -const char *git_remote_url(struct git_remote *remote) +const char *git_remote_url(git_remote *remote) { + assert(remote); return remote->url; } -const git_refspec *git_remote_fetchspec(struct git_remote *remote) +const git_refspec *git_remote_fetchspec(git_remote *remote) { + assert(remote); return &remote->fetch; } -const git_refspec *git_remote_pushspec(struct git_remote *remote) +const git_refspec *git_remote_pushspec(git_remote *remote) { + assert(remote); return &remote->push; } @@ -182,6 +200,8 @@ int git_remote_connect(git_remote *remote, int direction) int error; git_transport *t; + assert(remote); + error = git_transport_new(&t, remote->url); if (error < GIT_SUCCESS) return git__rethrow(error, "Failed to create transport"); @@ -203,20 +223,23 @@ cleanup: int git_remote_ls(git_remote *remote, git_headarray *refs) { + assert(remote && 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; + + assert(filename && remote); + + if ((error = git_fetch_negotiate(remote)) < 0) + return git__rethrow(error, "Error negotiating"); + return git_fetch_download_pack(filename, remote); } -int git_remote_update_tips(struct git_remote *remote) +int git_remote_update_tips(git_remote *remote) { int error = GIT_SUCCESS; unsigned int i = 0; @@ -226,6 +249,8 @@ int git_remote_update_tips(struct git_remote *remote) git_reference *ref; struct git_refspec *spec = &remote->fetch; + assert(remote); + memset(refname, 0x0, sizeof(refname)); if (refs->len == 0) @@ -236,6 +261,7 @@ int git_remote_update_tips(struct git_remote *remote) if (!strcmp(head->name, GIT_HEAD_FILE)) { error = git_reference_create_oid(&ref, remote->repo, GIT_FETCH_HEAD_FILE, &head->oid, 1); i = 1; + git_reference_free(ref); if (error < GIT_SUCCESS) return git__rethrow(error, "Failed to update FETCH_HEAD"); } @@ -250,11 +276,32 @@ int git_remote_update_tips(struct git_remote *remote) error = git_reference_create_oid(&ref, remote->repo, refname, &head->oid, 1); if (error < GIT_SUCCESS) return error; + + git_reference_free(ref); } return GIT_SUCCESS; } +int git_remote_connected(git_remote *remote) +{ + assert(remote); + return remote->transport == NULL ? 0 : remote->transport->connected; +} + +void git_remote_disconnect(git_remote *remote) +{ + assert(remote); + + if (remote->transport != NULL) { + if (remote->transport->connected) + remote->transport->close(remote->transport); + + remote->transport->free(remote->transport); + remote->transport = NULL; + } +} + void git_remote_free(git_remote *remote) { if (remote == NULL) @@ -266,11 +313,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/transport.c b/src/transport.c index 8d69db53e..0d67e1967 100644 --- a/src/transport.c +++ b/src/transport.c @@ -10,7 +10,7 @@ #include "git2/net.h" #include "transport.h" -struct { +static struct { char *prefix; git_transport_cb fn; } transports[] = { @@ -23,26 +23,20 @@ struct { {NULL, 0} }; -static git_transport_cb transport_new_fn(const char *url) +#define GIT_TRANSPORT_COUNT (sizeof(transports)/sizeof(transports[0])) + +static git_transport_cb transport_find_fn(const char *url) { - int i = 0; + size_t i = 0; - while (1) { - if (transports[i].prefix == NULL) - break; + /* TODO: Parse "example.com:project.git" as an SSH URL */ + for (i = 0; i < GIT_TRANSPORT_COUNT; ++i) { if (!strncasecmp(url, transports[i].prefix, strlen(transports[i].prefix))) return transports[i].fn; - - ++i; } - /* - * If we still haven't found the transport, we assume we mean a - * local file. - * TODO: Parse "example.com:project.git" as an SSH URL - */ - return git_transport_local; + return NULL; } /************** @@ -55,13 +49,25 @@ 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; git_transport *transport; int error; - fn = transport_new_fn(url); + fn = transport_find_fn(url); + + /* + * If we haven't found the transport, we assume we mean a + * local file. + */ + if (fn == NULL) + fn = &git_transport_local; error = fn(&transport); if (error < GIT_SUCCESS) 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; - } - - /* Get rid of the part we've used already */ - gitno_consume(buf, line_end); + error = git_protocol_store_refs(&t->proto, buf->data, buf->offset); + if (error == GIT_ESHORTBUFFER) { + gitno_consume_n(buf, buf->len); + continue; + } - error = git_vector_insert(refs, pkt); - if (error < GIT_SUCCESS) - return error; + if (error < GIT_SUCCESS) + return git__rethrow(error, "Failed to store refs"); - 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 diff --git a/tests-clay/buf/basic.c b/tests-clay/buf/basic.c new file mode 100644 index 000000000..860564d13 --- /dev/null +++ b/tests-clay/buf/basic.c @@ -0,0 +1,29 @@ +#include "clay_libgit2.h" +#include "buffer.h" + +static const char *test_string = "Have you seen that? Have you seeeen that??"; + +void test_buf_basic__resize(void) +{ + git_buf buf1 = GIT_BUF_INIT; + git_buf_puts(&buf1, test_string); + cl_assert(git_buf_oom(&buf1) == 0); + cl_assert(strcmp(git_buf_cstr(&buf1), test_string) == 0); + + git_buf_puts(&buf1, test_string); + cl_assert(strlen(git_buf_cstr(&buf1)) == strlen(test_string) * 2); + git_buf_free(&buf1); +} + +void test_buf_basic__printf(void) +{ + git_buf buf2 = GIT_BUF_INIT; + git_buf_printf(&buf2, "%s %s %d ", "shoop", "da", 23); + cl_assert(git_buf_oom(&buf2) == 0); + cl_assert(strcmp(git_buf_cstr(&buf2), "shoop da 23 ") == 0); + + git_buf_printf(&buf2, "%s %d", "woop", 42); + cl_assert(git_buf_oom(&buf2) == 0); + cl_assert(strcmp(git_buf_cstr(&buf2), "shoop da 23 woop 42") == 0); + git_buf_free(&buf2); +}
\ No newline at end of file diff --git a/tests-clay/clay.h b/tests-clay/clay.h index db3a475b2..812209be1 100644 --- a/tests-clay/clay.h +++ b/tests-clay/clay.h @@ -57,6 +57,8 @@ void cl_fixture_cleanup(const char *fixture_name); /** * Test method declarations */ +extern void test_buf_basic__printf(void); +extern void test_buf_basic__resize(void); extern void test_config_stress__cleanup(void); extern void test_config_stress__dont_break_on_invalid_input(void); extern void test_config_stress__initialize(void); @@ -68,6 +70,9 @@ extern void test_core_dirent__traverse_weird_filenames(void); extern void test_core_filebuf__0(void); extern void test_core_filebuf__1(void); extern void test_core_filebuf__2(void); +extern void test_core_filebuf__3(void); +extern void test_core_filebuf__4(void); +extern void test_core_filebuf__5(void); extern void test_core_oid__initialize(void); extern void test_core_oid__streq(void); extern void test_core_path__0(void); diff --git a/tests-clay/clay_libgit2.h b/tests-clay/clay_libgit2.h index a5208962e..8784b7e61 100644 --- a/tests-clay/clay_libgit2.h +++ b/tests-clay/clay_libgit2.h @@ -16,7 +16,7 @@ git_clearerror(); \ if ((expr) != GIT_SUCCESS) \ clay__assert(0, __FILE__, __LINE__, "Function call failed: " #expr, git_lasterror(), 1); \ - } while(0); + } while(0) /** * Wrapper for `clay_must_fail` -- this one is diff --git a/tests-clay/clay_main.c b/tests-clay/clay_main.c index 4ad6fc467..6d964b1ba 100644 --- a/tests-clay/clay_main.c +++ b/tests-clay/clay_main.c @@ -104,6 +104,10 @@ static void clay_unsandbox(void); static int clay_sandbox(void); /* Autogenerated test data by clay */ +static const struct clay_func _clay_cb_buf_basic[] = { + {"printf", &test_buf_basic__printf}, + {"resize", &test_buf_basic__resize} +}; static const struct clay_func _clay_cb_config_stress[] = { {"dont_break_on_invalid_input", &test_config_stress__dont_break_on_invalid_input} }; @@ -117,7 +121,10 @@ static const struct clay_func _clay_cb_core_dirent[] = { static const struct clay_func _clay_cb_core_filebuf[] = { {"0", &test_core_filebuf__0}, {"1", &test_core_filebuf__1}, - {"2", &test_core_filebuf__2} + {"2", &test_core_filebuf__2}, + {"3", &test_core_filebuf__3}, + {"4", &test_core_filebuf__4}, + {"5", &test_core_filebuf__5} }; static const struct clay_func _clay_cb_core_oid[] = { {"streq", &test_core_oid__streq} @@ -216,6 +223,12 @@ static const struct clay_func _clay_cb_status_worktree[] = { static const struct clay_suite _clay_suites[] = { { + "buf::basic", + {NULL, NULL}, + {NULL, NULL}, + _clay_cb_buf_basic, 2 + }, + { "config::stress", {"initialize", &test_config_stress__initialize}, {"cleanup", &test_config_stress__cleanup}, @@ -231,7 +244,7 @@ static const struct clay_suite _clay_suites[] = { "core::filebuf", {NULL, NULL}, {NULL, NULL}, - _clay_cb_core_filebuf, 3 + _clay_cb_core_filebuf, 6 }, { "core::oid", @@ -349,8 +362,8 @@ static const struct clay_suite _clay_suites[] = { } }; -static size_t _clay_suite_count = 22; -static size_t _clay_callback_count = 65; +static size_t _clay_suite_count = 23; +static size_t _clay_callback_count = 70; /* Core test functions */ static void diff --git a/tests-clay/config/stress.c b/tests-clay/config/stress.c index 7b81400c1..832321556 100644 --- a/tests-clay/config/stress.c +++ b/tests-clay/config/stress.c @@ -8,7 +8,7 @@ void test_config_stress__initialize(void) { - git_filebuf file; + git_filebuf file = GIT_FILEBUF_INIT; git_filebuf_open(&file, TEST_CONFIG, 0); diff --git a/tests-clay/core/filebuf.c b/tests-clay/core/filebuf.c index e1ecb2798..5b233fe8e 100644 --- a/tests-clay/core/filebuf.c +++ b/tests-clay/core/filebuf.c @@ -4,7 +4,7 @@ /* make sure git_filebuf_open doesn't delete an existing lock */ void test_core_filebuf__0(void) { - git_filebuf file; + git_filebuf file = GIT_FILEBUF_INIT; int fd; char test[] = "test", testlock[] = "test.lock"; @@ -23,7 +23,7 @@ void test_core_filebuf__0(void) /* make sure GIT_FILEBUF_APPEND works as expected */ void test_core_filebuf__1(void) { - git_filebuf file; + git_filebuf file = GIT_FILEBUF_INIT; int fd; char test[] = "test"; @@ -43,7 +43,7 @@ void test_core_filebuf__1(void) /* make sure git_filebuf_write writes large buffer correctly */ void test_core_filebuf__2(void) { - git_filebuf file; + git_filebuf file = GIT_FILEBUF_INIT; char test[] = "test"; unsigned char buf[4096 * 4]; /* 2 * WRITE_BUFFER_SIZE */ @@ -56,3 +56,51 @@ void test_core_filebuf__2(void) cl_must_pass(p_unlink(test)); } + +/* make sure git_filebuf_open won't reopen an open buffer */ +void test_core_filebuf__3(void) +{ + git_filebuf file = GIT_FILEBUF_INIT; + char test[] = "test"; + + cl_git_pass(git_filebuf_open(&file, test, 0)); + cl_git_fail(git_filebuf_open(&file, test, 0)); + + git_filebuf_cleanup(&file); +} + + +/* make sure git_filebuf_cleanup clears the buffer */ +void test_core_filebuf__4(void) +{ + git_filebuf file = GIT_FILEBUF_INIT; + char test[] = "test"; + + cl_assert(file.buffer == NULL); + + cl_git_pass(git_filebuf_open(&file, test, 0)); + cl_assert(file.buffer != NULL); + + git_filebuf_cleanup(&file); + cl_assert(file.buffer == NULL); +} + + +/* make sure git_filebuf_commit clears the buffer */ +void test_core_filebuf__5(void) +{ + git_filebuf file = GIT_FILEBUF_INIT; + char test[] = "test"; + + cl_assert(file.buffer == NULL); + + cl_git_pass(git_filebuf_open(&file, test, 0)); + cl_assert(file.buffer != NULL); + cl_git_pass(git_filebuf_printf(&file, "%s\n", "libgit2 rocks")); + cl_assert(file.buffer != NULL); + + cl_git_pass(git_filebuf_commit(&file, 0666)); + cl_assert(file.buffer == NULL); + + cl_must_pass(p_unlink(test)); +} diff --git a/tests-clay/index/rename.c b/tests-clay/index/rename.c index ba72b62f7..f81125434 100644 --- a/tests-clay/index/rename.c +++ b/tests-clay/index/rename.c @@ -8,7 +8,7 @@ static void file_create(const char *filename, const char *content) fd = p_creat(filename, 0666); cl_assert(fd != 0); cl_git_pass(p_write(fd, content, strlen(content))); - cl_git_pass(p_close(fd)) + cl_git_pass(p_close(fd)); } void test_index_rename__single_file(void) diff --git a/tests/t00-core.c b/tests/t00-core.c index 94824b438..16a5c6f93 100644 --- a/tests/t00-core.c +++ b/tests/t00-core.c @@ -462,7 +462,7 @@ BEGIN_TEST(dirent4, "make sure that strange looking filenames ('..c') are traver END_TEST BEGIN_TEST(filebuf0, "make sure git_filebuf_open doesn't delete an existing lock") - git_filebuf file; + git_filebuf file = GIT_FILEBUF_INIT; int fd; char test[] = "test", testlock[] = "test.lock"; @@ -475,7 +475,7 @@ BEGIN_TEST(filebuf0, "make sure git_filebuf_open doesn't delete an existing lock END_TEST BEGIN_TEST(filebuf1, "make sure GIT_FILEBUF_APPEND works as expected") - git_filebuf file; + git_filebuf file = GIT_FILEBUF_INIT; int fd; char test[] = "test"; @@ -492,7 +492,7 @@ BEGIN_TEST(filebuf1, "make sure GIT_FILEBUF_APPEND works as expected") END_TEST BEGIN_TEST(filebuf2, "make sure git_filebuf_write writes large buffer correctly") - git_filebuf file; + git_filebuf file = GIT_FILEBUF_INIT; char test[] = "test"; unsigned char buf[4096 * 4]; /* 2 * WRITE_BUFFER_SIZE */ diff --git a/tests/t06-index.c b/tests/t06-index.c index 44562d004..7b0f05129 100644 --- a/tests/t06-index.c +++ b/tests/t06-index.c @@ -163,7 +163,7 @@ END_TEST BEGIN_TEST(add0, "add a new file to the index") git_index *index; - git_filebuf file; + git_filebuf file = GIT_FILEBUF_INIT; git_repository *repo; git_index_entry *entry; git_oid id1; diff --git a/tests/t15-config.c b/tests/t15-config.c index a97f82067..9f0deb3e3 100644 --- a/tests/t15-config.c +++ b/tests/t15-config.c @@ -306,7 +306,7 @@ END_TEST BEGIN_TEST(config16, "add a variable in a new section") git_config *cfg; int32_t i; - git_filebuf buf; + git_filebuf buf = GIT_FILEBUF_INIT; /* By freeing the config, we make sure we flush the values */ must_pass(git_config_open_ondisk(&cfg, CONFIG_BASE "/config10")); |
