diff options
44 files changed, 777 insertions, 422 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt index 2440ed0b8..271af690c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -128,6 +128,8 @@ INCLUDE_DIRECTORIES(src include) IF (WIN32 AND WINHTTP AND NOT MINGW) ADD_DEFINITIONS(-DGIT_WINHTTP) + INCLUDE_DIRECTORIES(deps/http-parser) + FILE(GLOB SRC_HTTP deps/http-parser/*.c deps/http-parser/*.h) ELSE () IF (NOT AMIGA) FIND_PACKAGE(OpenSSL) diff --git a/examples/blame.c b/examples/blame.c index 95bce6b9c..06310d540 100644 --- a/examples/blame.c +++ b/examples/blame.c @@ -114,7 +114,7 @@ int main(int argc, char *argv[]) rawdata+i); } - i = eol - rawdata + 1; + i = (int)(eol - rawdata + 1); line++; } diff --git a/examples/network/index-pack.c b/examples/network/index-pack.c index 59fff416a..314f21160 100644 --- a/examples/network/index-pack.c +++ b/examples/network/index-pack.c @@ -46,7 +46,7 @@ int index_pack(git_repository *repo, int argc, char **argv) return EXIT_FAILURE; } - if (git_indexer_new(&idx, ".", NULL, NULL, NULL) < 0) { + if (git_indexer_new(&idx, ".", 0, NULL, NULL, NULL) < 0) { puts("bad idx"); return -1; } 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/indexer.h b/include/git2/indexer.h index fb55672a9..e4c03ad06 100644 --- a/include/git2/indexer.h +++ b/include/git2/indexer.h @@ -20,6 +20,7 @@ typedef struct git_indexer git_indexer; * * @param out where to store the indexer instance * @param path to the directory where the packfile should be stored + * @param mode permissions to use creating packfile or 0 for defaults * @param odb object database from which to read base objects when * fixing thin packs. Pass NULL if no thin pack is expected (an error * will be returned if there are bases missing) @@ -29,6 +30,7 @@ typedef struct git_indexer git_indexer; GIT_EXTERN(int) git_indexer_new( git_indexer **out, const char *path, + unsigned int mode, git_odb *odb, git_transfer_progress_callback progress_cb, void *progress_cb_payload); diff --git a/include/git2/pack.h b/include/git2/pack.h index 748817666..52e7adad2 100644 --- a/include/git2/pack.h +++ b/include/git2/pack.h @@ -119,6 +119,7 @@ GIT_EXTERN(int) git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid * * @param pb The packbuilder * @param path to the directory where the packfile and index should be stored + * @param mode permissions to use creating a packfile or 0 for defaults * @param progress_cb function to call with progress information from the indexer (optional) * @param progress_cb_payload payload for the progress callback (optional) * @@ -127,9 +128,20 @@ GIT_EXTERN(int) git_packbuilder_insert_commit(git_packbuilder *pb, const git_oid GIT_EXTERN(int) git_packbuilder_write( git_packbuilder *pb, const char *path, + unsigned int mode, git_transfer_progress_callback progress_cb, void *progress_cb_payload); +/** +* Get the packfile's hash +* +* A packfile's name is derived from the sorted hashing of all object +* names. This is only correct after the packfile has been written. +* +* @param pb The packbuilder object +*/ +GIT_EXTERN(const git_oid *) git_packbuilder_hash(git_packbuilder *pb); + typedef int (*git_packbuilder_foreach_cb)(void *buf, size_t size, void *payload); /** * Create the new pack and pass each object to the callback diff --git a/include/git2/remote.h b/include/git2/remote.h index 559b8dcc1..7410909dc 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -73,7 +73,7 @@ GIT_EXTERN(int) git_remote_create_with_fetchspec( * * @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 */ @@ -164,8 +164,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); @@ -182,8 +185,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 @@ -202,6 +218,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. @@ -228,15 +254,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 @@ -256,15 +273,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/blame.c b/src/blame.c index 01d3fc03c..219a6bfe4 100644 --- a/src/blame.c +++ b/src/blame.c @@ -75,6 +75,9 @@ static git_blame_hunk* dup_hunk(git_blame_hunk *hunk) hunk->orig_path); git_oid_cpy(&newhunk->orig_commit_id, &hunk->orig_commit_id); git_oid_cpy(&newhunk->final_commit_id, &hunk->final_commit_id); + newhunk->boundary = hunk->boundary; + newhunk->final_signature = git_signature_dup(hunk->final_signature); + newhunk->orig_signature = git_signature_dup(hunk->orig_signature); return newhunk; } @@ -381,9 +384,13 @@ static int buffer_hunk_cb( wedge_line = (hunk->old_lines == 0) ? hunk->new_start : hunk->old_start; blame->current_diff_line = wedge_line; - /* If this hunk doesn't start between existing hunks, split a hunk up so it does */ blame->current_hunk = (git_blame_hunk*)git_blame_get_hunk_byline(blame, wedge_line); - if (!hunk_starts_at_or_after_line(blame->current_hunk, wedge_line)){ + if (!blame->current_hunk) { + /* Line added at the end of the file */ + blame->current_hunk = new_hunk(wedge_line, 0, wedge_line, blame->path); + git_vector_insert(&blame->hunks, blame->current_hunk); + } else if (!hunk_starts_at_or_after_line(blame->current_hunk, wedge_line)){ + /* If this hunk doesn't start between existing hunks, split a hunk up so it does */ blame->current_hunk = split_hunk_in_vector(&blame->hunks, blame->current_hunk, wedge_line - blame->current_hunk->orig_start_line_number, true); } @@ -404,13 +411,6 @@ static int buffer_line_cb( GIT_UNUSED(hunk); GIT_UNUSED(line); -#ifdef DO_DEBUG - { - char *str = git__substrdup(content, content_len); - git__free(str); - } -#endif - if (line->origin == GIT_DIFF_LINE_ADDITION) { if (hunk_is_bufferblame(blame->current_hunk) && hunk_ends_at_or_before_line(blame->current_hunk, blame->current_diff_line)) { 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/config_file.c b/src/config_file.c index 0bd4e4ece..15c8de49c 100644 --- a/src/config_file.c +++ b/src/config_file.c @@ -1178,6 +1178,24 @@ static int write_section(git_filebuf *file, const char *key) return result; } +static const char *quotes_for_value(const char *value) +{ + const char *ptr; + + if (value[0] == ' ' || value[0] == '\0') + return "\""; + + for (ptr = value; *ptr; ++ptr) { + if (*ptr == ';' || *ptr == '#') + return "\""; + } + + if (ptr[-1] == ' ') + return "\""; + + return ""; +} + /* * This is pretty much the parsing, except we write out anything we don't have */ @@ -1302,7 +1320,8 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p /* Then replace the variable. If the value is NULL, it * means we want to delete it, so don't write anything. */ if (value != NULL) { - git_filebuf_printf(&file, "\t%s = %s\n", name, value); + const char *q = quotes_for_value(value); + git_filebuf_printf(&file, "\t%s = %s%s%s\n", name, q, value, q); } /* @@ -1343,6 +1362,8 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p if (preg_replaced) { git_filebuf_printf(&file, "\n%s", write_start); } else { + const char *q; + git_filebuf_write(&file, reader->buffer.ptr, reader->buffer.size); /* And now if we just need to add a variable */ @@ -1362,7 +1383,8 @@ static int config_write(diskfile_backend *cfg, const char *key, const regex_t *p if (reader->buffer.size > 0 && *(reader->buffer.ptr + reader->buffer.size - 1) != '\n') git_filebuf_write(&file, "\n", 1); - git_filebuf_printf(&file, "\t%s = %s\n", name, value); + q = quotes_for_value(value); + git_filebuf_printf(&file, "\t%s = %s%s%s\n", name, q, value, q); } } 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/indexer.c b/src/indexer.c index 90fb52187..df1ce7cfb 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -37,6 +37,7 @@ struct git_indexer { struct git_pack_header hdr; struct git_pack_file *pack; git_filebuf pack_file; + unsigned int mode; git_off_t off; git_off_t entry_start; git_packfile_stream stream; @@ -119,6 +120,7 @@ static int objects_cmp(const void *a, const void *b) int git_indexer_new( git_indexer **out, const char *prefix, + unsigned int mode, git_odb *odb, git_transfer_progress_callback progress_cb, void *progress_payload) @@ -133,6 +135,7 @@ int git_indexer_new( idx->odb = odb; idx->progress_cb = progress_cb; idx->progress_payload = progress_payload; + idx->mode = mode ? mode : GIT_PACK_FILE_MODE; git_hash_ctx_init(&idx->trailer); error = git_buf_joinpath(&path, prefix, suff); @@ -141,7 +144,7 @@ int git_indexer_new( error = git_filebuf_open(&idx->pack_file, path.ptr, GIT_FILEBUF_TEMPORARY | GIT_FILEBUF_DO_NOT_BUFFER, - GIT_PACK_FILE_MODE); + idx->mode); git_buf_free(&path); if (error < 0) goto cleanup; @@ -905,7 +908,7 @@ int git_indexer_commit(git_indexer *idx, git_transfer_progress *stats) return -1; if (git_filebuf_open(&index_file, filename.ptr, - GIT_FILEBUF_HASH_CONTENTS, GIT_PACK_FILE_MODE) < 0) + GIT_FILEBUF_HASH_CONTENTS, idx->mode) < 0) goto on_error; /* Write out the header */ diff --git a/src/merge.c b/src/merge.c index 2cbf531f9..115867971 100644 --- a/src/merge.c +++ b/src/merge.c @@ -1649,7 +1649,7 @@ static int write_merge_head( goto cleanup; for (i = 0; i < heads_len; i++) { - if ((error = git_filebuf_printf(&file, "%s\n", &heads[i]->oid_str)) < 0) + if ((error = git_filebuf_printf(&file, "%s\n", heads[i]->oid_str)) < 0) goto cleanup; } @@ -2240,6 +2240,8 @@ static int merge_check_workdir(size_t *conflicts, git_repository *repo, git_inde git_diff_options opts = GIT_DIFF_OPTIONS_INIT; int error = 0; + GIT_UNUSED(index_new); + *conflicts = 0; opts.flags |= GIT_DIFF_INCLUDE_UNTRACKED; diff --git a/src/netops.c b/src/netops.c index 7e13f12e7..15ed0fc26 100644 --- a/src/netops.c +++ b/src/netops.c @@ -32,6 +32,7 @@ #include "netops.h" #include "posix.h" #include "buffer.h" +#include "http_parser.h" #ifdef GIT_WIN32 static void net_set_error(const char *str) @@ -582,7 +583,7 @@ int gitno_connection_data_from_url( const char *service_suffix) { int error = -1; - const char *default_port = NULL; + const char *default_port = NULL, *path_search_start = NULL; char *original_host = NULL; /* service_suffix is optional */ @@ -594,22 +595,18 @@ int gitno_connection_data_from_url( gitno_connection_data_free_ptrs(data); if (!git__prefixcmp(url, prefix_http)) { - url = url + strlen(prefix_http); + path_search_start = url + strlen(prefix_http); default_port = "80"; if (data->use_ssl) { giterr_set(GITERR_NET, "Redirect from HTTPS to HTTP is not allowed"); goto cleanup; } - } - - if (!git__prefixcmp(url, prefix_https)) { - url += strlen(prefix_https); + } else if (!git__prefixcmp(url, prefix_https)) { + path_search_start = url + strlen(prefix_https); default_port = "443"; data->use_ssl = true; - } - - if (url[0] == '/') + } else if (url[0] == '/') default_port = data->use_ssl ? "443" : "80"; if (!default_port) { @@ -618,18 +615,19 @@ int gitno_connection_data_from_url( } error = gitno_extract_url_parts( - &data->host, &data->port, &data->user, &data->pass, + &data->host, &data->port, &data->path, &data->user, &data->pass, url, default_port); if (url[0] == '/') { /* Relative redirect; reuse original host name and port */ + path_search_start = url; git__free(data->host); data->host = original_host; original_host = NULL; } if (!error) { - const char *path = strchr(url, '/'); + const char *path = strchr(path_search_start, '/'); size_t pathlen = strlen(path); size_t suffixlen = service_suffix ? strlen(service_suffix) : 0; @@ -660,55 +658,74 @@ void gitno_connection_data_free_ptrs(gitno_connection_data *d) git__free(d->pass); d->pass = NULL; } +#define hex2c(c) ((c | 32) % 39 - 9) +static char* unescape(char *str) +{ + int x, y; + int len = (int)strlen(str); + + for (x=y=0; str[y]; ++x, ++y) { + if ((str[x] = str[y]) == '%') { + if (y < len-2 && isxdigit(str[y+1]) && isxdigit(str[y+2])) { + str[x] = (hex2c(str[y+1]) << 4) + hex2c(str[y+2]); + y += 2; + } + } + } + str[x] = '\0'; + return str; +} + int gitno_extract_url_parts( char **host, char **port, + char **path, char **username, char **password, const char *url, const char *default_port) { - char *colon, *slash, *at, *end; - const char *start; - - /* - * ==> [user[:pass]@]hostname.tld[:port]/resource - */ - - colon = strchr(url, ':'); - slash = strchr(url, '/'); - at = strchr(url, '@'); + struct http_parser_url u = {0}; + const char *_host, *_port, *_path, *_userinfo; - if (!slash || - (colon && (slash < colon))) { - giterr_set(GITERR_NET, "Malformed URL"); + if (http_parser_parse_url(url, strlen(url), false, &u)) { + giterr_set(GITERR_NET, "Malformed URL '%s'", url); return GIT_EINVALIDSPEC; } - start = url; - if (at && at < slash) { - start = at+1; - *username = git__substrdup(url, at - url); - } + _host = url+u.field_data[UF_HOST].off; + _port = url+u.field_data[UF_PORT].off; + _path = url+u.field_data[UF_PATH].off; + _userinfo = url+u.field_data[UF_USERINFO].off; - if (colon && colon < at) { - git__free(*username); - *username = git__substrdup(url, colon-url); - *password = git__substrdup(colon+1, at-colon-1); - colon = strchr(at, ':'); + if (u.field_set & (1 << UF_HOST)) { + *host = git__substrdup(_host, u.field_data[UF_HOST].len); + GITERR_CHECK_ALLOC(*host); } - if (colon == NULL) { + if (u.field_set & (1 << UF_PORT)) + *port = git__substrdup(_port, u.field_data[UF_PORT].len); + else *port = git__strdup(default_port); - } else { - *port = git__substrdup(colon + 1, slash - colon - 1); - } GITERR_CHECK_ALLOC(*port); - end = colon == NULL ? slash : colon; + if (u.field_set & (1 << UF_PATH)) { + *path = git__substrdup(_path, u.field_data[UF_PATH].len); + GITERR_CHECK_ALLOC(*path); + } - *host = git__substrdup(start, end - start); - GITERR_CHECK_ALLOC(*host); + if (u.field_set & (1 << UF_USERINFO)) { + const char *colon = memchr(_userinfo, ':', u.field_data[UF_USERINFO].len); + if (colon) { + *username = unescape(git__substrdup(_userinfo, colon - _userinfo)); + *password = unescape(git__substrdup(colon+1, u.field_data[UF_USERINFO].len - (colon+1-_userinfo))); + GITERR_CHECK_ALLOC(*password); + } else { + *username = git__substrdup(_userinfo, u.field_data[UF_USERINFO].len); + } + GITERR_CHECK_ALLOC(*username); + + } return 0; } diff --git a/src/netops.h b/src/netops.h index 5c105d6e3..666d66b12 100644 --- a/src/netops.h +++ b/src/netops.h @@ -92,6 +92,7 @@ void gitno_connection_data_free_ptrs(gitno_connection_data *data); int gitno_extract_url_parts( char **host, char **port, + char **path, char **username, char **password, const char *url, diff --git a/src/odb_pack.c b/src/odb_pack.c index 12f4591ec..fd2ca0fd8 100644 --- a/src/odb_pack.c +++ b/src/odb_pack.c @@ -558,7 +558,7 @@ static int pack_backend__writepack(struct git_odb_writepack **out, GITERR_CHECK_ALLOC(writepack); if (git_indexer_new(&writepack->indexer, - backend->pack_folder, odb, progress_cb, progress_payload) < 0) { + backend->pack_folder, 0, odb, progress_cb, progress_payload) < 0) { git__free(writepack); return -1; } diff --git a/src/pack-objects.c b/src/pack-objects.c index 9ea7c659a..2d62507f2 100644 --- a/src/pack-objects.c +++ b/src/pack-objects.c @@ -272,7 +272,7 @@ static int write_object(git_buf *buf, git_packbuilder *pb, git_pobject *po) git_buf zbuf = GIT_BUF_INIT; git_otype type; unsigned char hdr[10]; - unsigned int hdr_len; + size_t hdr_len; unsigned long size; void *data; @@ -560,6 +560,7 @@ static int write_pack(git_packbuilder *pb, git_buf buf = GIT_BUF_INIT; enum write_one_status status; struct git_pack_header ph; + git_oid entry_oid; unsigned int i = 0; int error = 0; @@ -596,10 +597,10 @@ static int write_pack(git_packbuilder *pb, } while (pb->nr_remaining && i < pb->nr_objects); - if ((error = git_hash_final(&pb->pack_oid, &pb->ctx)) < 0) + if ((error = git_hash_final(&entry_oid, &pb->ctx)) < 0) goto done; - error = cb(pb->pack_oid.id, GIT_OID_RAWSZ, data); + error = cb(entry_oid.id, GIT_OID_RAWSZ, data); done: git__free(write_order); @@ -1247,6 +1248,7 @@ static int write_cb(void *buf, size_t len, void *payload) int git_packbuilder_write( git_packbuilder *pb, const char *path, + unsigned int mode, git_transfer_progress_callback progress_cb, void *progress_cb_payload) { @@ -1257,7 +1259,7 @@ int git_packbuilder_write( PREPARE_PACK; if (git_indexer_new( - &indexer, path, pb->odb, progress_cb, progress_cb_payload) < 0) + &indexer, path, mode, pb->odb, progress_cb, progress_cb_payload) < 0) return -1; ctx.indexer = indexer; @@ -1269,12 +1271,19 @@ int git_packbuilder_write( return -1; } + git_oid_cpy(&pb->pack_oid, git_indexer_hash(indexer)); + git_indexer_free(indexer); return 0; } #undef PREPARE_PACK +const git_oid *git_packbuilder_hash(git_packbuilder *pb) +{ + return &pb->pack_oid; +} + static int cb_tree_walk(const char *root, const git_tree_entry *entry, void *payload) { struct tree_walk_context *ctx = payload; diff --git a/src/push.c b/src/push.c index a799db8d0..3c9d5bb35 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, (void *)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 b97cc9e79..c0f35e5ea 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; @@ -318,7 +320,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; } @@ -377,6 +380,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: @@ -643,11 +650,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) @@ -707,67 +714,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) @@ -778,6 +749,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; @@ -785,15 +775,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; @@ -1043,10 +1032,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) { @@ -1054,7 +1041,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; @@ -1063,8 +1050,8 @@ int git_remote_update_tips(git_remote *remote) } out: - git_refspec__free(&tagspec); git_vector_free(&refs); + git_refspec__free(&tagspec); return error; } @@ -1097,9 +1084,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; @@ -1112,12 +1096,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); @@ -1526,7 +1510,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; @@ -1542,7 +1526,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; @@ -1565,14 +1549,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) @@ -1630,18 +1668,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/signature.c b/src/signature.c index 52ca2b375..ec51a42e9 100644 --- a/src/signature.c +++ b/src/signature.c @@ -84,8 +84,12 @@ int git_signature_new(git_signature **sig_out, const char *name, const char *ema git_signature *git_signature_dup(const git_signature *sig) { - git_signature *new = git__calloc(1, sizeof(git_signature)); + git_signature *new; + if (sig == NULL) + return NULL; + + new = git__calloc(1, sizeof(git_signature)); if (new == NULL) return NULL; diff --git a/src/transports/git.c b/src/transports/git.c index 79a9e7dd4..5dcd4eff7 100644 --- a/src/transports/git.c +++ b/src/transports/git.c @@ -179,39 +179,33 @@ static int _git_uploadpack_ls( const char *url, git_smart_subtransport_stream **stream) { - char *host=NULL, *port=NULL, *user=NULL, *pass=NULL; + char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL; + const char *stream_url = url; git_stream *s; + int error = -1; *stream = NULL; - if (!git__prefixcmp(url, prefix_git)) - url += strlen(prefix_git); + stream_url += strlen(prefix_git); - if (git_stream_alloc(t, url, cmd_uploadpack, stream) < 0) + if (git_stream_alloc(t, stream_url, cmd_uploadpack, stream) < 0) return -1; s = (git_stream *)*stream; - if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0) - goto on_error; - - if (gitno_connect(&s->socket, host, port, 0) < 0) - goto on_error; - - t->current_stream = s; - git__free(host); - git__free(port); - git__free(user); - git__free(pass); - return 0; + if (!(error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) { + if (!(error = gitno_connect(&s->socket, host, port, 0))) + t->current_stream = s; -on_error: - if (*stream) + git__free(host); + git__free(port); + git__free(path); + git__free(user); + git__free(pass); + } else if (*stream) git_stream_free(*stream); - git__free(host); - git__free(port); - return -1; + return error; } static int _git_uploadpack( @@ -235,39 +229,33 @@ static int _git_receivepack_ls( const char *url, git_smart_subtransport_stream **stream) { - char *host=NULL, *port=NULL, *user=NULL, *pass=NULL; + char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL; + const char *stream_url = url; git_stream *s; + int error; *stream = NULL; - if (!git__prefixcmp(url, prefix_git)) - url += strlen(prefix_git); + stream_url += strlen(prefix_git); - if (git_stream_alloc(t, url, cmd_receivepack, stream) < 0) + if (git_stream_alloc(t, stream_url, cmd_receivepack, stream) < 0) return -1; s = (git_stream *)*stream; - if (gitno_extract_url_parts(&host, &port, &user, &pass, url, GIT_DEFAULT_PORT) < 0) - goto on_error; - - if (gitno_connect(&s->socket, host, port, 0) < 0) - goto on_error; - - t->current_stream = s; - git__free(host); - git__free(port); - git__free(user); - git__free(pass); - return 0; + if (!(error = gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, GIT_DEFAULT_PORT))) { + if (!(error = gitno_connect(&s->socket, host, port, 0))) + t->current_stream = s; -on_error: - if (*stream) + git__free(host); + git__free(port); + git__free(path); + git__free(user); + git__free(pass); + } else if (*stream) git_stream_free(*stream); - git__free(host); - git__free(port); - return -1; + return error; } static int _git_receivepack( 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/src/transports/ssh.c b/src/transports/ssh.c index 4e2834b49..4a905e3c9 100644 --- a/src/transports/ssh.c +++ b/src/transports/ssh.c @@ -317,7 +317,7 @@ static int _git_ssh_setup_conn( const char *cmd, git_smart_subtransport_stream **stream) { - char *host=NULL, *port=NULL, *user=NULL, *pass=NULL; + char *host=NULL, *port=NULL, *path=NULL, *user=NULL, *pass=NULL; const char *default_port="22"; ssh_stream *s; LIBSSH2_SESSION* session=NULL; @@ -330,8 +330,7 @@ static int _git_ssh_setup_conn( s = (ssh_stream *)*stream; if (!git__prefixcmp(url, prefix_ssh)) { - url = url + strlen(prefix_ssh); - if (gitno_extract_url_parts(&host, &port, &user, &pass, url, default_port) < 0) + if (gitno_extract_url_parts(&host, &port, &path, &user, &pass, url, default_port) < 0) goto on_error; } else { if (git_ssh_extract_url_parts(&host, &user, url) < 0) @@ -390,6 +389,7 @@ static int _git_ssh_setup_conn( t->current_stream = s; git__free(host); git__free(port); + git__free(path); git__free(user); git__free(pass); diff --git a/tests-clar/blame/buffer.c b/tests-clar/blame/buffer.c index 69b2d5440..912ee9846 100644 --- a/tests-clar/blame/buffer.c +++ b/tests-clar/blame/buffer.c @@ -19,6 +19,8 @@ void test_blame_buffer__cleanup(void) void test_blame_buffer__added_line(void) { + const git_blame_hunk *hunk; + const char *buffer = "\ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\ EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\ @@ -39,6 +41,10 @@ CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer))); cl_assert_equal_i(5, git_blame_get_hunk_count(g_bufferblame)); check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 1, 0, "000000", "b.txt"); + + hunk = git_blame_get_hunk_byline(g_bufferblame, 16); + cl_assert(hunk); + cl_assert_equal_s("Ben Straub", hunk->final_signature->name); } void test_blame_buffer__deleted_line(void) @@ -128,3 +134,33 @@ CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC check_blame_hunk_index(g_repo, g_bufferblame, 3, 7, 1, 0, "00000000", "b.txt"); check_blame_hunk_index(g_repo, g_bufferblame, 4, 8, 3, 0, "63d671eb", "b.txt"); } + +void test_blame_buffer__add_lines_at_end(void) +{ + const char *buffer = "\ +EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\ +EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\ +EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\ +EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE\n\ +\n\ +BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\ +BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\ +BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\ +BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB\n\ +\n\ +CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\ +CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\ +CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\ +CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\n\ +\n\ +abc\n\ +def\n"; + cl_git_pass(git_blame_buffer(&g_bufferblame, g_fileblame, buffer, strlen(buffer))); + + cl_assert_equal_i(5, git_blame_get_hunk_count(g_bufferblame)); + check_blame_hunk_index(g_repo, g_bufferblame, 0, 1, 4, 0, "da237394", "b.txt"); + check_blame_hunk_index(g_repo, g_bufferblame, 1, 5, 1, 1, "b99f7ac0", "b.txt"); + check_blame_hunk_index(g_repo, g_bufferblame, 2, 6, 5, 0, "63d671eb", "b.txt"); + check_blame_hunk_index(g_repo, g_bufferblame, 3, 11, 5, 0, "aa06ecca", "b.txt"); + check_blame_hunk_index(g_repo, g_bufferblame, 4, 16, 2, 0, "00000000", "b.txt"); +} diff --git a/tests-clar/config/write.c b/tests-clar/config/write.c index 309fef65a..15f750dc0 100644 --- a/tests-clar/config/write.c +++ b/tests-clar/config/write.c @@ -229,6 +229,37 @@ void test_config_write__add_value_at_file_with_no_clrf_at_the_end(void) git_config_free(cfg); } +void test_config_write__add_value_which_needs_quotes(void) +{ + git_config *cfg; + const char* str1; + const char* str2; + const char* str3; + const char* str4; + const char* str5; + + cl_git_pass(git_config_open_ondisk(&cfg, "config17")); + cl_git_pass(git_config_set_string(cfg, "core.startwithspace", " Something")); + cl_git_pass(git_config_set_string(cfg, "core.endwithspace", "Something ")); + cl_git_pass(git_config_set_string(cfg, "core.containscommentchar1", "some#thing")); + cl_git_pass(git_config_set_string(cfg, "core.containscommentchar2", "some;thing")); + cl_git_pass(git_config_set_string(cfg, "core.startwhithsapceandcontainsdoublequote", " some\"thing")); + git_config_free(cfg); + + cl_git_pass(git_config_open_ondisk(&cfg, "config17")); + cl_git_pass(git_config_get_string(&str1, cfg, "core.startwithspace")); + cl_assert_equal_s(" Something", str1); + cl_git_pass(git_config_get_string(&str2, cfg, "core.endwithspace")); + cl_assert_equal_s("Something ", str2); + cl_git_pass(git_config_get_string(&str3, cfg, "core.containscommentchar1")); + cl_assert_equal_s("some#thing", str3); + cl_git_pass(git_config_get_string(&str4, cfg, "core.containscommentchar2")); + cl_assert_equal_s("some;thing", str4); + cl_git_pass(git_config_get_string(&str5, cfg, "core.startwhithsapceandcontainsdoublequote")); + cl_assert_equal_s(" some\"thing", str5); + git_config_free(cfg); +} + void test_config_write__can_set_a_value_to_NULL(void) { git_repository *repository; diff --git a/tests-clar/merge/workdir/setup.c b/tests-clar/merge/workdir/setup.c index 463dee06e..870d55ef2 100644 --- a/tests-clar/merge/workdir/setup.c +++ b/tests-clar/merge/workdir/setup.c @@ -1041,7 +1041,8 @@ void test_merge_workdir_setup__removed_after_failure(void) cl_git_rewritefile("merge-resolve/new-in-octo1.txt", "Conflicting file!\n\nMerge will fail!\n"); - cl_git_fail(git_merge(&result, repo, &their_heads[0], 1, &opts)); + cl_git_fail(git_merge( + &result, repo, (const git_merge_head **)&their_heads[0], 1, &opts)); cl_assert(!git_path_exists("merge-resolve/" GIT_MERGE_HEAD_FILE)); cl_assert(!git_path_exists("merge-resolve/" GIT_ORIG_HEAD_FILE)); diff --git a/tests-clar/merge/workdir/trivial.c b/tests-clar/merge/workdir/trivial.c index d20d89879..9f9566243 100644 --- a/tests-clar/merge/workdir/trivial.c +++ b/tests-clar/merge/workdir/trivial.c @@ -62,7 +62,7 @@ static int merge_trivial(const char *ours, const char *theirs, bool automerge) return 0; } -static int merge_trivial_conflict_entrycount(void) +static size_t merge_trivial_conflict_entrycount(void) { const git_index_entry *entry; size_t count = 0; 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/network/urlparse.c b/tests-clar/network/urlparse.c index 15e841b35..2a9c2f69f 100644 --- a/tests-clar/network/urlparse.c +++ b/tests-clar/network/urlparse.c @@ -1,12 +1,12 @@ #include "clar_libgit2.h" #include "netops.h" -char *host, *port, *user, *pass; -gitno_connection_data conndata; +static char *host, *port, *path, *user, *pass; +static gitno_connection_data conndata; void test_network_urlparse__initialize(void) { - host = port = user = pass = NULL; + host = port = path = user = pass = NULL; memset(&conndata, 0, sizeof(conndata)); } @@ -15,6 +15,7 @@ void test_network_urlparse__cleanup(void) #define FREE_AND_NULL(x) if (x) { git__free(x); x = NULL; } FREE_AND_NULL(host); FREE_AND_NULL(port); + FREE_AND_NULL(path); FREE_AND_NULL(user); FREE_AND_NULL(pass); @@ -23,27 +24,33 @@ void test_network_urlparse__cleanup(void) void test_network_urlparse__trivial(void) { - cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, - "example.com/resource", "8080")); + cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, + "http://example.com/resource", "8080")); cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(port, "8080"); + cl_assert_equal_s(path, "/resource"); cl_assert_equal_p(user, NULL); cl_assert_equal_p(pass, NULL); } -void test_network_urlparse__bad_url(void) +void test_network_urlparse__encoded_password(void) { - cl_git_fail_with(gitno_extract_url_parts(&host, &port, &user, &pass, - "github.com/git://github.com/foo/bar.git.git", "443"), - GIT_EINVALIDSPEC); + cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, + "https://user:pass%2fis%40bad@hostname.com:1234/", "1")); + cl_assert_equal_s(host, "hostname.com"); + cl_assert_equal_s(port, "1234"); + cl_assert_equal_s(path, "/"); + cl_assert_equal_s(user, "user"); + cl_assert_equal_s(pass, "pass/is@bad"); } void test_network_urlparse__user(void) { - cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, - "user@example.com/resource", "8080")); + cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, + "https://user@example.com/resource", "8080")); cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(port, "8080"); + cl_assert_equal_s(path, "/resource"); cl_assert_equal_s(user, "user"); cl_assert_equal_p(pass, NULL); } @@ -51,10 +58,11 @@ void test_network_urlparse__user(void) void test_network_urlparse__user_pass(void) { /* user:pass@hostname.tld/resource */ - cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, - "user:pass@example.com/resource", "8080")); + cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, + "https://user:pass@example.com/resource", "8080")); cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(port, "8080"); + cl_assert_equal_s(path, "/resource"); cl_assert_equal_s(user, "user"); cl_assert_equal_s(pass, "pass"); } @@ -62,10 +70,11 @@ void test_network_urlparse__user_pass(void) void test_network_urlparse__port(void) { /* hostname.tld:port/resource */ - cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, - "example.com:9191/resource", "8080")); + cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, + "https://example.com:9191/resource", "8080")); cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(port, "9191"); + cl_assert_equal_s(path, "/resource"); cl_assert_equal_p(user, NULL); cl_assert_equal_p(pass, NULL); } @@ -73,10 +82,11 @@ void test_network_urlparse__port(void) void test_network_urlparse__user_port(void) { /* user@hostname.tld:port/resource */ - cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, - "user@example.com:9191/resource", "8080")); + cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, + "https://user@example.com:9191/resource", "8080")); cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(port, "9191"); + cl_assert_equal_s(path, "/resource"); cl_assert_equal_s(user, "user"); cl_assert_equal_p(pass, NULL); } @@ -84,10 +94,11 @@ void test_network_urlparse__user_port(void) void test_network_urlparse__user_pass_port(void) { /* user:pass@hostname.tld:port/resource */ - cl_git_pass(gitno_extract_url_parts(&host, &port, &user, &pass, - "user:pass@example.com:9191/resource", "8080")); + cl_git_pass(gitno_extract_url_parts(&host, &port, &path, &user, &pass, + "https://user:pass@example.com:9191/resource", "8080")); cl_assert_equal_s(host, "example.com"); cl_assert_equal_s(port, "9191"); + cl_assert_equal_s(path, "/resource"); cl_assert_equal_s(user, "user"); cl_assert_equal_s(pass, "pass"); } @@ -116,6 +127,18 @@ void test_network_urlparse__connection_data_ssl(void) cl_assert_equal_i(conndata.use_ssl, true); } +void test_network_urlparse__encoded_username_password(void) +{ + cl_git_pass(gitno_connection_data_from_url(&conndata, + "https://user%2fname:pass%40word%zyx%v@example.com/foo/bar/baz", "bar/baz")); + cl_assert_equal_s(conndata.host, "example.com"); + cl_assert_equal_s(conndata.port, "443"); + cl_assert_equal_s(conndata.path, "/foo/"); + cl_assert_equal_s(conndata.user, "user/name"); + cl_assert_equal_s(conndata.pass, "pass@word%zyx%v"); + cl_assert_equal_i(conndata.use_ssl, true); +} + void test_network_urlparse__connection_data_cross_host_redirect(void) { conndata.host = git__strdup("bar.com"); 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__ */ diff --git a/tests-clar/pack/indexer.c b/tests-clar/pack/indexer.c index d7953300f..07963a9e7 100644 --- a/tests-clar/pack/indexer.c +++ b/tests-clar/pack/indexer.c @@ -48,7 +48,7 @@ void test_pack_indexer__out_of_order(void) git_indexer *idx; git_transfer_progress stats; - cl_git_pass(git_indexer_new(&idx, ".", NULL, NULL, NULL)); + cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL)); cl_git_pass(git_indexer_append(idx, out_of_order_pack, out_of_order_pack_len, &stats)); cl_git_pass(git_indexer_commit(idx, &stats)); @@ -75,7 +75,7 @@ void test_pack_indexer__fix_thin(void) git_oid_fromstr(&should_id, "e68fe8129b546b101aee9510c5328e7f21ca1d18"); cl_assert(!git_oid_cmp(&id, &should_id)); - cl_git_pass(git_indexer_new(&idx, ".", odb, NULL, NULL)); + cl_git_pass(git_indexer_new(&idx, ".", 0, odb, NULL, NULL)); cl_git_pass(git_indexer_append(idx, thin_pack, thin_pack_len, &stats)); cl_git_pass(git_indexer_commit(idx, &stats)); @@ -108,7 +108,7 @@ void test_pack_indexer__fix_thin(void) cl_git_pass(p_stat(name, &st)); - cl_git_pass(git_indexer_new(&idx, ".", NULL, NULL, NULL)); + cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL)); read = p_read(fd, buffer, sizeof(buffer)); cl_assert(read != -1); p_close(fd); diff --git a/tests-clar/pack/packbuilder.c b/tests-clar/pack/packbuilder.c index ac6f731e1..54d1e8022 100644 --- a/tests-clar/pack/packbuilder.c +++ b/tests-clar/pack/packbuilder.c @@ -1,5 +1,6 @@ #include "clar_libgit2.h" #include "fileops.h" +#include "pack.h" #include "hash.h" #include "iterator.h" #include "vector.h" @@ -92,7 +93,7 @@ void test_pack_packbuilder__create_pack(void) seed_packbuilder(); - cl_git_pass(git_indexer_new(&_indexer, ".", NULL, NULL, NULL)); + cl_git_pass(git_indexer_new(&_indexer, ".", 0, NULL, NULL, NULL)); cl_git_pass(git_packbuilder_foreach(_packbuilder, feed_indexer, &stats)); cl_git_pass(git_indexer_commit(_indexer, &stats)); @@ -128,6 +129,61 @@ void test_pack_packbuilder__create_pack(void) cl_assert_equal_s(hex, "5d410bdf97cf896f9007681b92868471d636954b"); } +void test_pack_packbuilder__get_hash(void) +{ + char hex[41]; hex[40] = '\0'; + + seed_packbuilder(); + + git_packbuilder_write(_packbuilder, ".", 0, NULL, NULL); + git_oid_fmt(hex, git_packbuilder_hash(_packbuilder)); + + cl_assert_equal_s(hex, "80e61eb315239ef3c53033e37fee43b744d57122"); +} + +static void test_write_pack_permission(mode_t given, mode_t expected) +{ + struct stat statbuf; + mode_t mask, os_mask; + + seed_packbuilder(); + + git_packbuilder_write(_packbuilder, ".", given, NULL, NULL); + + /* Windows does not return group/user bits from stat, + * files are never executable. + */ +#ifdef GIT_WIN32 + os_mask = 0600; +#else + os_mask = 0777; +#endif + + mask = p_umask(0); + p_umask(mask); + + cl_git_pass(p_stat("pack-80e61eb315239ef3c53033e37fee43b744d57122.idx", &statbuf)); + cl_assert_equal_i(statbuf.st_mode & os_mask, (expected & ~mask) & os_mask); + + cl_git_pass(p_stat("pack-80e61eb315239ef3c53033e37fee43b744d57122.pack", &statbuf)); + cl_assert_equal_i(statbuf.st_mode & os_mask, (expected & ~mask) & os_mask); +} + +void test_pack_packbuilder__permissions_standard(void) +{ + test_write_pack_permission(0, GIT_PACK_FILE_MODE); +} + +void test_pack_packbuilder__permissions_readonly(void) +{ + test_write_pack_permission(0444, 0444); +} + +void test_pack_packbuilder__permissions_readwrite(void) +{ + test_write_pack_permission(0666, 0666); +} + static git_transfer_progress stats; static int foreach_cb(void *buf, size_t len, void *payload) { @@ -141,7 +197,7 @@ void test_pack_packbuilder__foreach(void) git_indexer *idx; seed_packbuilder(); - cl_git_pass(git_indexer_new(&idx, ".", NULL, NULL, NULL)); + cl_git_pass(git_indexer_new(&idx, ".", 0, NULL, NULL, NULL)); cl_git_pass(git_packbuilder_foreach(_packbuilder, foreach_cb, idx)); cl_git_pass(git_indexer_commit(idx, &stats)); git_indexer_free(idx); diff --git a/tests-clar/repo/config.c b/tests-clar/repo/config.c index 11abd42bc..e77acc8c5 100644 --- a/tests-clar/repo/config.c +++ b/tests-clar/repo/config.c @@ -2,7 +2,7 @@ #include "fileops.h" #include <ctype.h> -git_buf path = GIT_BUF_INIT; +static git_buf path = GIT_BUF_INIT; void test_repo_config__initialize(void) { |
