diff options
31 files changed, 392 insertions, 158 deletions
diff --git a/PROJECTS.md b/PROJECTS.md index 5a09adae1..4f200b7f9 100644 --- a/PROJECTS.md +++ b/PROJECTS.md @@ -89,14 +89,11 @@ might make good smaller projects by themselves. using binary search every time * Tree builder improvements: * Extend to allow building a tree hierarchy -* Move the tagopt mechanism to the newer git 1.9 interpretation of - --tags [#2120](https://github.com/libgit2/libgit2/issues/2120) * Apply-patch API * Add a patch editing API to enable "git add -p" type operations * Textconv API to filter binary data before generating diffs (something like the current Filter API, probably). * Performance profiling and improvement -* Build in handling of "empty tree" and "empty blob" SHAs * Support "git replace" ref replacements * Include conflicts in diff results and in status * GIT_DELTA_CONFLICT for items in conflict (with multiple files) diff --git a/include/git2/remote.h b/include/git2/remote.h index 0d7fd230f..4aabd94b6 100644 --- a/include/git2/remote.h +++ b/include/git2/remote.h @@ -14,6 +14,7 @@ #include "indexer.h" #include "strarray.h" #include "transport.h" +#include "push.h" /** * @file git2/remote.h @@ -280,14 +281,19 @@ GIT_EXTERN(const git_refspec *)git_remote_get_refspec(const git_remote *remote, GIT_EXTERN(int) git_remote_connect(git_remote *remote, git_direction direction); /** - * Get a list of refs at the remote + * Get the remote repository's reference advertisement list * - * The remote (or more exactly its transport) must be connected. The - * memory belongs to the remote. + * Get the list of references with which the server responds to a new + * connection. * - * 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. + * The remote (or more exactly its transport) must have connected to + * the remote repository. This list is available as soon as the + * connection to the remote is initiated and it remains available + * after disconnecting. + * + * The memory belongs to the remote. The pointer will be valid as long + * as a new connection is not initiated, but it is recommended that + * you make a copy in order to make use of the data. * * @param out pointer to the array * @param size the number of remote heads @@ -337,8 +343,7 @@ GIT_EXTERN(void) git_remote_stop(git_remote *remote); /** * Disconnect from the remote * - * Close the connection to the remote and free the underlying - * transport. + * Close the connection to the remote. * * @param remote the remote to disconnect from */ @@ -390,6 +395,23 @@ GIT_EXTERN(int) git_remote_fetch( const char *reflog_message); /** + * Perform a push + * + * Peform all the steps from a push. + * + * @param remote the remote to push to + * @param refspecs the refspecs to use for pushing. If none are + * passed, the configured refspecs will be used + * @param opts the options + * @param signature signature to use for the reflog of updated references + * @param reflog_message message to use for the reflog of upated references + */ +GIT_EXTERN(int) git_remote_push(git_remote *remote, + git_strarray *refspecs, + const git_push_options *opts, + const git_signature *signature, const char *reflog_message); + +/** * Get a list of the configured remotes for a repo * * The string array must be freed by the user. @@ -462,6 +484,28 @@ struct git_remote_callbacks { int (*update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data); /** + * Function to call with progress information during pack + * building. Be aware that this is called inline with pack + * building operations, so performance may be affected. + */ + git_packbuilder_progress pack_progress; + + /** + * Function to call with progress information during the + * upload portion of a push. Be aware that this is called + * inline with pack building operations, so performance may be + * affected. + */ + git_push_transfer_progress push_transfer_progress; + + /** + * Called for each updated reference on push. If `status` is + * not `NULL`, the update was rejected by the remote server + * and `status` contains the reason given. + */ + int (*push_update_reference)(const char *refname, const char *status, void *data); + + /** * This will be passed to each of the callbacks in this struct * as the last parameter. */ diff --git a/script/cibuild.sh b/script/cibuild.sh index 360c28663..8983dcce0 100755 --- a/script/cibuild.sh +++ b/script/cibuild.sh @@ -44,6 +44,9 @@ export GITTEST_REMOTE_SSH_PUBKEY="$HOME/.ssh/id_rsa.pub" export GITTEST_REMOTE_SSH_PASSPHRASE="" if [ -e ./libgit2_clar ]; then - ./libgit2_clar -sonline::push -sonline::clone::cred_callback -sonline::clone::ssh_cert && + ./libgit2_clar -sonline::push -sonline::clone::ssh_cert && ./libgit2_clar -sonline::clone::ssh_with_paths + if [ "$TRAVIS_OS_NAME" = "linux" ]; then + ./libgit2_clar -sonline::clone::cred_callback + fi fi diff --git a/src/branch.c b/src/branch.c index 01402a2e9..b4e4b0564 100644 --- a/src/branch.c +++ b/src/branch.c @@ -400,6 +400,12 @@ int git_branch_upstream_remote(git_buf *buf, git_repository *repo, const char *r if ((error = retrieve_upstream_configuration(&str, cfg, refname, "branch.%s.remote")) < 0) goto cleanup; + if (!*str) { + giterr_set(GITERR_REFERENCE, "branch '%s' does not have an upstream remote", refname); + error = GIT_ENOTFOUND; + goto cleanup; + } + error = git_buf_puts(buf, str); cleanup: diff --git a/src/checkout.c b/src/checkout.c index 44e2f3b27..4e879e36f 100644 --- a/src/checkout.c +++ b/src/checkout.c @@ -1145,12 +1145,16 @@ static int checkout_conflict_append_remove( checkout_data *data = payload; const char *name; + assert(ancestor || ours || theirs); + if (ancestor) name = git__strdup(ancestor->path); else if (ours) name = git__strdup(ours->path); else if (theirs) name = git__strdup(theirs->path); + else + abort(); GITERR_CHECK_ALLOC(name); diff --git a/src/global.c b/src/global.c index da903cb94..006202a2c 100644 --- a/src/global.c +++ b/src/global.c @@ -67,7 +67,13 @@ void openssl_locking_function(int mode, int n, const char *file, int line) static void shutdown_ssl_locking(void) { + int num_locks, i; + + num_locks = CRYPTO_num_locks(); CRYPTO_set_locking_callback(NULL); + + for (i = 0; i < num_locks; ++i) + git_mutex_free(openssl_locks); git__free(openssl_locks); } #endif diff --git a/src/indexer.c b/src/indexer.c index 620e8186b..0e682dd6e 100644 --- a/src/indexer.c +++ b/src/indexer.c @@ -120,6 +120,7 @@ int git_indexer_new( idx->progress_cb = progress_cb; idx->progress_payload = progress_payload; idx->mode = mode ? mode : GIT_PACK_FILE_MODE; + git_hash_ctx_init(&idx->hash_ctx); git_hash_ctx_init(&idx->trailer); error = git_buf_joinpath(&path, prefix, suff); @@ -265,7 +266,6 @@ static int store_object(git_indexer *idx) struct entry *entry; git_off_t entry_size; struct git_pack_entry *pentry; - git_hash_ctx *ctx = &idx->hash_ctx; git_off_t entry_start = idx->entry_start; entry = git__calloc(1, sizeof(*entry)); @@ -274,7 +274,7 @@ static int store_object(git_indexer *idx) pentry = git__calloc(1, sizeof(struct git_pack_entry)); GITERR_CHECK_ALLOC(pentry); - git_hash_final(&oid, ctx); + git_hash_final(&oid, &idx->hash_ctx); entry_size = idx->off - entry_start; if (entry_start > UINT31_MAX) { entry->offset = UINT32_MAX; @@ -557,7 +557,7 @@ int git_indexer_append(git_indexer *idx, const void *data, size_t size, git_tran git_mwindow_close(&w); idx->entry_start = entry_start; - git_hash_ctx_init(&idx->hash_ctx); + git_hash_init(&idx->hash_ctx); if (type == GIT_OBJ_REF_DELTA || type == GIT_OBJ_OFS_DELTA) { error = advance_delta_offset(idx, type); @@ -843,12 +843,10 @@ static int update_header_and_rehash(git_indexer *idx, git_transfer_progress *sta git_mwindow *w = NULL; git_mwindow_file *mwf; unsigned int left; - git_hash_ctx *ctx; mwf = &idx->pack->mwf; - ctx = &idx->trailer; - git_hash_ctx_init(ctx); + git_hash_init(&idx->trailer); /* Update the header to include the numer of local objects we injected */ @@ -1061,5 +1059,7 @@ void git_indexer_free(git_indexer *idx) git_mutex_unlock(&git__mwindow_mutex); } + git_hash_ctx_cleanup(&idx->trailer); + git_hash_ctx_cleanup(&idx->hash_ctx); git__free(idx); } @@ -762,12 +762,12 @@ static int hardcoded_objects(git_rawobj *raw, const git_oid *id) if (!git_oid_cmp(id, &empty_blob)) { raw->type = GIT_OBJ_BLOB; raw->len = 0; - raw->data = NULL; + raw->data = git__calloc(1, sizeof(uint8_t)); return 0; } else if (!git_oid_cmp(id, &empty_tree)) { raw->type = GIT_OBJ_TREE; raw->len = 0; - raw->data = NULL; + raw->data = git__calloc(1, sizeof(uint8_t)); return 0; } else { return GIT_ENOTFOUND; diff --git a/src/push.c b/src/push.c index be5ec1c0e..6671da465 100644 --- a/src/push.c +++ b/src/push.c @@ -19,7 +19,7 @@ static int push_spec_rref_cmp(const void *a, const void *b) { const push_spec *push_spec_a = a, *push_spec_b = b; - return strcmp(push_spec_a->rref, push_spec_b->rref); + return strcmp(push_spec_a->refspec.dst, push_spec_b->refspec.dst); } static int push_status_ref_cmp(const void *a, const void *b) @@ -94,12 +94,7 @@ static void free_refspec(push_spec *spec) if (spec == NULL) return; - if (spec->lref) - git__free(spec->lref); - - if (spec->rref) - git__free(spec->rref); - + git_refspec__free(&spec->refspec); git__free(spec); } @@ -134,47 +129,25 @@ static int check_lref(git_push *push, char *ref) static int parse_refspec(git_push *push, push_spec **spec, const char *str) { push_spec *s; - char *delim; *spec = NULL; s = git__calloc(1, sizeof(*s)); GITERR_CHECK_ALLOC(s); - if (str[0] == '+') { - s->force = true; - str++; + if (git_refspec__parse(&s->refspec, str, false) < 0) { + giterr_set(GITERR_INVALID, "invalid refspec %s", str); + goto on_error; } - delim = strchr(str, ':'); - if (delim == NULL) { - s->lref = git__strdup(str); - if (!s->lref || check_lref(push, s->lref) < 0) - goto on_error; - } else { - if (delim - str) { - s->lref = git__strndup(str, delim - str); - if (!s->lref || check_lref(push, s->lref) < 0) - goto on_error; - } - - if (strlen(delim + 1)) { - s->rref = git__strdup(delim + 1); - if (!s->rref || check_rref(s->rref) < 0) - goto on_error; - } + if (s->refspec.src && s->refspec.src[0] != '\0' && + check_lref(push, s->refspec.src) < 0) { + goto on_error; } - if (!s->lref && !s->rref) + if (check_rref(s->refspec.dst) < 0) goto on_error; - /* If rref is ommitted, use the same ref name as lref */ - if (!s->rref) { - s->rref = git__strdup(s->lref); - if (!s->rref || check_rref(s->rref) < 0) - goto on_error; - } - *spec = s; return 0; @@ -220,7 +193,7 @@ int git_push_update_tips( /* Find matching push ref spec */ git_vector_foreach(&push->specs, j, push_spec) { - if (!strcmp(push_spec->rref, status->ref)) + if (!strcmp(push_spec->refspec.dst, status->ref)) break; } @@ -353,14 +326,15 @@ static int revwalk(git_vector *commits, git_push *push) } else if (git_revwalk_push(rw, &spec->loid) < 0) goto on_error; - if (!spec->force) { + if (!spec->refspec.force) { git_oid base; if (git_oid_iszero(&spec->roid)) continue; if (!git_odb_exists(push->repo->_odb, &spec->roid)) { - giterr_set(GITERR_REFERENCE, "Cannot push missing reference"); + giterr_set(GITERR_REFERENCE, + "Cannot push because a reference that you are trying to update on the remote contains commits that are not present locally."); error = GIT_ENONFASTFORWARD; goto on_error; } @@ -571,22 +545,20 @@ static int calculate_work(git_push *push) /* Update local and remote oids*/ git_vector_foreach(&push->specs, i, spec) { - if (spec->lref) { + if (spec->refspec.src && spec->refspec.src[0]!= '\0') { /* This is a create or update. Local ref must exist. */ if (git_reference_name_to_id( - &spec->loid, push->repo, spec->lref) < 0) { - giterr_set(GITERR_REFERENCE, "No such reference '%s'", spec->lref); + &spec->loid, push->repo, spec->refspec.src) < 0) { + giterr_set(GITERR_REFERENCE, "No such reference '%s'", spec->refspec.src); return -1; } } - if (spec->rref) { - /* Remote ref may or may not (e.g. during create) already exist. */ - git_vector_foreach(&push->remote->refs, j, head) { - if (!strcmp(spec->rref, head->name)) { - git_oid_cpy(&spec->roid, &head->oid); - break; - } + /* Remote ref may or may not (e.g. during create) already exist. */ + git_vector_foreach(&push->remote->refs, j, head) { + if (!strcmp(spec->refspec.dst, head->name)) { + git_oid_cpy(&spec->roid, &head->oid); + break; } } } diff --git a/src/push.h b/src/push.h index 6c8bf7229..68fa868dd 100644 --- a/src/push.h +++ b/src/push.h @@ -8,15 +8,13 @@ #define INCLUDE_push_h__ #include "git2.h" +#include "refspec.h" typedef struct push_spec { - char *lref; - char *rref; + struct git_refspec refspec; git_oid loid; git_oid roid; - - bool force; } push_spec; typedef struct push_status { diff --git a/src/refspec.c b/src/refspec.c index 9f0df35a7..a56c44cc0 100644 --- a/src/refspec.c +++ b/src/refspec.c @@ -119,6 +119,12 @@ int git_refspec__parse(git_refspec *refspec, const char *input, bool is_fetch) if (!git_reference__is_valid_name(refspec->dst, flags)) goto invalid; } + + /* if the RHS is empty, then it's a copy of the LHS */ + if (!refspec->dst) { + refspec->dst = git__strdup(refspec->src); + GITERR_CHECK_ALLOC(refspec->dst); + } } refspec->string = git__strdup(input); diff --git a/src/remote.c b/src/remote.c index cc9f85cd1..b1a84075e 100644 --- a/src/remote.c +++ b/src/remote.c @@ -163,6 +163,10 @@ static int create_internal(git_remote **out, git_repository *repo, const char *n if (fetch != NULL) { if (add_refspec(remote, fetch, true) < 0) goto on_error; + + /* Move the data over to where the matching functions can find them */ + if (dwim_refspecs(&remote->active_refspecs, &remote->refspecs, &remote->refs) < 0) + goto on_error; } if (!name) @@ -702,7 +706,7 @@ int git_remote_ls(const git_remote_head ***out, size_t *size, git_remote *remote assert(remote); if (!remote->transport) { - giterr_set(GITERR_NET, "No transport bound to this remote"); + giterr_set(GITERR_NET, "this remote has never connected"); return -1; } @@ -2111,3 +2115,63 @@ int git_remote_default_branch(git_buf *out, git_remote *remote) return git_buf_puts(out, guess->name); } + +int git_remote_push(git_remote *remote, git_strarray *refspecs, const git_push_options *opts, + const git_signature *signature, const char *reflog_message) +{ + int error; + size_t i; + git_push *push = NULL; + git_remote_callbacks *cbs; + git_refspec *spec; + + assert(remote && refspecs); + + if ((error = git_remote_connect(remote, GIT_DIRECTION_PUSH)) < 0) + return error; + + if ((error = git_push_new(&push, remote)) < 0) + goto cleanup; + + if (opts && (error = git_push_set_options(push, opts)) < 0) + goto cleanup; + + if (refspecs && refspecs->count > 0) { + for (i = 0; i < refspecs->count; i++) { + if ((error = git_push_add_refspec(push, refspecs->strings[i])) < 0) + goto cleanup; + } + } else { + git_vector_foreach(&remote->refspecs, i, spec) { + if (!spec->push) + continue; + if ((error = git_push_add_refspec(push, spec->string)) < 0) + goto cleanup; + } + } + + cbs = &remote->callbacks; + if ((error = git_push_set_callbacks(push, + cbs->pack_progress, cbs->payload, + cbs->push_transfer_progress, cbs->payload)) < 0) + goto cleanup; + + if ((error = git_push_finish(push)) < 0) + goto cleanup; + + if (!git_push_unpack_ok(push)) { + error = -1; + giterr_set(GITERR_NET, "error in the remote while trying to unpack"); + goto cleanup; + } + + if ((error = git_push_status_foreach(push, cbs->push_update_reference, cbs->payload)) < 0) + goto cleanup; + + error = git_push_update_tips(push, signature, reflog_message); + +cleanup: + git_remote_disconnect(remote); + git_push_free(push); + return error; +} diff --git a/src/transports/http.c b/src/transports/http.c index 4070b683a..234ee229f 100644 --- a/src/transports/http.c +++ b/src/transports/http.c @@ -1009,6 +1009,7 @@ static int http_close(git_smart_subtransport *subtransport) git_vector_clear(&t->auth_contexts); gitno_connection_data_free_ptrs(&t->connection_data); + memset(&t->connection_data, 0x0, sizeof(gitno_connection_data)); return 0; } diff --git a/src/transports/local.c b/src/transports/local.c index f859f0b70..05302a13f 100644 --- a/src/transports/local.c +++ b/src/transports/local.c @@ -405,7 +405,7 @@ static int local_push( git_vector_foreach(&push->specs, j, spec) { push_status *status; const git_error *last; - char *ref = spec->rref ? spec->rref : spec->lref; + char *ref = spec->refspec.dst; status = git__calloc(sizeof(push_status), 1); if (!status) @@ -417,7 +417,7 @@ static int local_push( goto on_error; } - error = local_push_update_remote_ref(remote_repo, spec->lref, spec->rref, + error = local_push_update_remote_ref(remote_repo, spec->refspec.src, spec->refspec.dst, &spec->loid, &spec->roid); switch (error) { diff --git a/src/transports/smart_protocol.c b/src/transports/smart_protocol.c index 7c20382dc..e110da07e 100644 --- a/src/transports/smart_protocol.c +++ b/src/transports/smart_protocol.c @@ -645,7 +645,7 @@ static int gen_pktline(git_buf *buf, git_push *push) old_id[GIT_OID_HEXSZ] = '\0'; new_id[GIT_OID_HEXSZ] = '\0'; git_vector_foreach(&push->specs, i, spec) { - len = 2*GIT_OID_HEXSZ + 7 + strlen(spec->rref); + len = 2*GIT_OID_HEXSZ + 7 + strlen(spec->refspec.dst); if (i == 0) { ++len; /* '\0' */ @@ -657,7 +657,7 @@ static int gen_pktline(git_buf *buf, git_push *push) git_oid_fmt(old_id, &spec->roid); git_oid_fmt(new_id, &spec->loid); - git_buf_printf(buf, "%04"PRIxZ"%s %s %s", len, old_id, new_id, spec->rref); + git_buf_printf(buf, "%04"PRIxZ"%s %s %s", len, old_id, new_id, spec->refspec.dst); if (i == 0) { git_buf_putc(buf, '\0'); @@ -816,7 +816,7 @@ static int add_ref_from_push_spec(git_vector *refs, push_spec *push_spec) added->type = GIT_PKT_REF; git_oid_cpy(&added->head.oid, &push_spec->loid); - added->head.name = git__strdup(push_spec->rref); + added->head.name = git__strdup(push_spec->refspec.dst); if (!added->head.name || git_vector_insert(refs, added) < 0) { @@ -855,7 +855,7 @@ static int update_refs_from_report( /* For each push spec we sent to the server, we should have * gotten back a status packet in the push report which matches */ - if (strcmp(push_spec->rref, push_status->ref)) { + if (strcmp(push_spec->refspec.dst, push_status->ref)) { giterr_set(GITERR_NET, "report-status: protocol error"); return -1; } @@ -872,7 +872,7 @@ static int update_refs_from_report( push_status = git_vector_get(push_report, i); ref = git_vector_get(refs, j); - cmp = strcmp(push_spec->rref, ref->head.name); + cmp = strcmp(push_spec->refspec.dst, ref->head.name); /* Iterate appropriately */ if (cmp <= 0) i++; @@ -985,7 +985,7 @@ int git_smart__push(git_transport *transport, git_push *push) * cases except when we only send delete commands */ git_vector_foreach(&push->specs, i, spec) { - if (spec->lref) { + if (spec->refspec.src && spec->refspec.src[0] != '\0') { need_pack = 1; break; } diff --git a/tests/network/remote/local.c b/tests/network/remote/local.c index a69af645d..b4a68e59e 100644 --- a/tests/network/remote/local.c +++ b/tests/network/remote/local.c @@ -212,7 +212,7 @@ void test_network_remote_local__push_to_bare_remote(void) /* Try to push */ cl_git_pass(git_push_new(&push, localremote)); - cl_git_pass(git_push_add_refspec(push, "refs/heads/master:")); + cl_git_pass(git_push_add_refspec(push, "refs/heads/master")); cl_git_pass(git_push_finish(push)); cl_assert(git_push_unpack_ok(push)); @@ -258,7 +258,7 @@ void test_network_remote_local__push_to_bare_remote_with_file_url(void) /* Try to push */ cl_git_pass(git_push_new(&push, localremote)); - cl_git_pass(git_push_add_refspec(push, "refs/heads/master:")); + cl_git_pass(git_push_add_refspec(push, "refs/heads/master")); cl_git_pass(git_push_finish(push)); cl_assert(git_push_unpack_ok(push)); @@ -301,7 +301,7 @@ void test_network_remote_local__push_to_non_bare_remote(void) /* Try to push */ cl_git_pass(git_push_new(&push, localremote)); - cl_git_pass(git_push_add_refspec(push, "refs/heads/master:")); + cl_git_pass(git_push_add_refspec(push, "refs/heads/master")); cl_git_fail_with(git_push_finish(push), GIT_EBAREREPO); cl_assert_equal_i(0, git_push_unpack_ok(push)); @@ -429,3 +429,38 @@ void test_network_remote_local__opportunistic_update(void) cl_git_pass(git_reference_lookup(&ref, repo, "refs/remotes/origin/master")); git_reference_free(ref); } + +void test_network_remote_local__update_tips_for_new_remote(void) { + git_repository *src_repo; + git_repository *dst_repo; + git_remote *new_remote; + git_push *push; + git_reference* branch; + + /* Copy test repo */ + cl_fixture_sandbox("testrepo.git"); + cl_git_pass(git_repository_open(&src_repo, "testrepo.git")); + + /* Set up an empty bare repo to push into */ + cl_git_pass(git_repository_init(&dst_repo, "./localbare.git", 1)); + + /* Push to bare repo */ + cl_git_pass(git_remote_create(&new_remote, src_repo, "bare", "./localbare.git")); + cl_git_pass(git_remote_connect(new_remote, GIT_DIRECTION_PUSH)); + cl_git_pass(git_push_new(&push, new_remote)); + cl_git_pass(git_push_add_refspec(push, "refs/heads/master")); + cl_git_pass(git_push_finish(push)); + cl_assert(git_push_unpack_ok(push)); + + /* Update tips and make sure remote branch has been created */ + cl_git_pass(git_push_update_tips(push, NULL, NULL)); + cl_git_pass(git_branch_lookup(&branch, src_repo, "bare/master", GIT_BRANCH_REMOTE)); + + git_reference_free(branch); + git_push_free(push); + git_remote_free(new_remote); + git_repository_free(dst_repo); + cl_fixture_cleanup("localbare.git"); + git_repository_free(src_repo); + cl_fixture_cleanup("testrepo.git"); +} diff --git a/tests/odb/emptyobjects.c b/tests/odb/emptyobjects.c index d6d832cdf..783d05197 100644 --- a/tests/odb/emptyobjects.c +++ b/tests/odb/emptyobjects.c @@ -21,6 +21,8 @@ void test_odb_emptyobjects__read(void) cl_git_pass(git_oid_fromstr(&id, "e69de29bb2d1d6434b8b29ae775ad8c2e48c5391")); cl_git_pass(git_blob_lookup(&blob, g_repo, &id)); cl_assert_equal_i(GIT_OBJ_BLOB, git_object_type((git_object *) blob)); + cl_assert(git_blob_rawcontent(blob)); + cl_assert_equal_s("", git_blob_rawcontent(blob)); cl_assert_equal_i(0, git_blob_rawsize(blob)); git_blob_free(blob); } @@ -37,3 +39,19 @@ void test_odb_emptyobjects__read_tree(void) cl_assert_equal_p(NULL, git_tree_entry_byname(tree, "foo")); git_tree_free(tree); } + +void test_odb_emptyobjects__read_tree_odb(void) +{ + git_oid id; + git_odb *odb; + git_odb_object *tree_odb; + + cl_git_pass(git_oid_fromstr(&id, "4b825dc642cb6eb9a060e54bf8d69288fbee4904")); + cl_git_pass(git_repository_odb(&odb, g_repo)); + cl_git_pass(git_odb_read(&tree_odb, odb, &id)); + cl_assert(git_odb_object_data(tree_odb)); + cl_assert_equal_s("", git_odb_object_data(tree_odb)); + cl_assert_equal_i(0, git_odb_object_size(tree_odb)); + git_odb_object_free(tree_odb); + git_odb_free(odb); +} diff --git a/tests/online/fetch.c b/tests/online/fetch.c index ec16dd2fd..848b87410 100644 --- a/tests/online/fetch.c +++ b/tests/online/fetch.c @@ -202,3 +202,14 @@ void test_online_fetch__remote_symrefs(void) git_remote_free(remote); } + +void test_online_fetch__twice(void) +{ + git_remote *remote; + + cl_git_pass(git_remote_create(&remote, _repo, "test", "http://github.com/libgit2/TestGitRepository.git")); + cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL)); + cl_git_pass(git_remote_fetch(remote, NULL, NULL, NULL)); + + git_remote_free(remote); +} diff --git a/tests/online/push.c b/tests/online/push.c index b09c7ad1f..6424b5ba6 100644 --- a/tests/online/push.c +++ b/tests/online/push.c @@ -89,46 +89,38 @@ static int cred_acquire_cb( return -1; } -/* the results of a push status. when used for expected values, msg may be NULL - * to indicate that it should not be matched. */ -typedef struct { - const char *ref; - int success; - const char *msg; -} push_status; - /** * git_push_status_foreach callback that records status entries. * @param data (git_vector *) of push_status instances */ -static int record_push_status_cb(const char *ref, const char *msg, void *data) +static int record_push_status_cb(const char *ref, const char *msg, void *payload) { - git_vector *statuses = (git_vector *)data; + record_callbacks_data *data = (record_callbacks_data *) payload; push_status *s; - cl_assert(s = git__malloc(sizeof(*s))); - s->ref = ref; + cl_assert(s = git__calloc(1, sizeof(*s))); + if (ref) + cl_assert(s->ref = git__strdup(ref)); s->success = (msg == NULL); - s->msg = msg; + if (msg) + cl_assert(s->msg = git__strdup(msg)); - git_vector_insert(statuses, s); + git_vector_insert(&data->statuses, s); return 0; } -static void do_verify_push_status(git_push *push, const push_status expected[], const size_t expected_len) +static void do_verify_push_status(record_callbacks_data *data, const push_status expected[], const size_t expected_len) { - git_vector actual = GIT_VECTOR_INIT; + git_vector *actual = &data->statuses; push_status *iter; bool failed = false; size_t i; - git_push_status_foreach(push, record_push_status_cb, &actual); - - if (expected_len != actual.length) + if (expected_len != actual->length) failed = true; else - git_vector_foreach(&actual, i, iter) + git_vector_foreach(actual, i, iter) if (strcmp(expected[i].ref, iter->ref) || (expected[i].success != iter->success) || (expected[i].msg && (!iter->msg || strcmp(expected[i].msg, iter->msg)))) { @@ -149,7 +141,7 @@ static void do_verify_push_status(git_push *push, const push_status expected[], git_buf_puts(&msg, "\nACTUAL:\n"); - git_vector_foreach(&actual, i, iter) { + git_vector_foreach(actual, i, iter) { if (iter->success) git_buf_printf(&msg, "%s: success\n", iter->ref); else @@ -161,10 +153,10 @@ static void do_verify_push_status(git_push *push, const push_status expected[], git_buf_free(&msg); } - git_vector_foreach(&actual, i, iter) + git_vector_foreach(actual, i, iter) git__free(iter); - git_vector_free(&actual); + git_vector_free(actual); } /** @@ -431,22 +423,24 @@ void test_online_push__cleanup(void) static int push_pack_progress_cb( int stage, unsigned int current, unsigned int total, void* payload) { - int *calls = (int *)payload; + record_callbacks_data *data = (record_callbacks_data *) payload; GIT_UNUSED(stage); GIT_UNUSED(current); GIT_UNUSED(total); - if (*calls < 0) - return *calls; - (*calls)++; + if (data->pack_progress_calls < 0) + return data->pack_progress_calls; + + data->pack_progress_calls++; return 0; } static int push_transfer_progress_cb( unsigned int current, unsigned int total, size_t bytes, void* payload) { - int *calls = (int *)payload; + record_callbacks_data *data = (record_callbacks_data *) payload; GIT_UNUSED(current); GIT_UNUSED(total); GIT_UNUSED(bytes); - if (*calls < 0) - return *calls; - (*calls)++; + if (data->transfer_progress_calls < 0) + return data->transfer_progress_calls; + + data->transfer_progress_calls++; return 0; } @@ -466,62 +460,63 @@ static void do_push( expected_ref expected_refs[], size_t expected_refs_len, int expected_ret, int check_progress_cb, int check_update_tips_cb) { - git_push *push; git_push_options opts = GIT_PUSH_OPTIONS_INIT; size_t i; - int pack_progress_calls = 0, transfer_progress_calls = 0; + int error; + git_strarray specs = {0}; git_signature *pusher; + git_remote_callbacks callbacks; + record_callbacks_data *data; if (_remote) { /* Auto-detect the number of threads to use */ opts.pb_parallelism = 0; cl_git_pass(git_signature_now(&pusher, "Foo Bar", "foo@example.com")); - cl_git_pass(git_remote_connect(_remote, GIT_DIRECTION_PUSH)); - cl_git_pass(git_push_new(&push, _remote)); - cl_git_pass(git_push_set_options(push, &opts)); + memcpy(&callbacks, git_remote_get_callbacks(_remote), sizeof(callbacks)); + data = callbacks.payload; - if (check_progress_cb) { - /* if EUSER, then abort in transfer */ - if (expected_ret == GIT_EUSER) - transfer_progress_calls = GIT_EUSER; + callbacks.pack_progress = push_pack_progress_cb; + callbacks.push_transfer_progress = push_transfer_progress_cb; + callbacks.push_update_reference = record_push_status_cb; + cl_git_pass(git_remote_set_callbacks(_remote, &callbacks)); - cl_git_pass( - git_push_set_callbacks( - push, push_pack_progress_cb, &pack_progress_calls, - push_transfer_progress_cb, &transfer_progress_calls)); + if (refspecs_len) { + specs.count = refspecs_len; + specs.strings = git__calloc(refspecs_len, sizeof(char *)); + cl_assert(specs.strings); } for (i = 0; i < refspecs_len; i++) - cl_git_pass(git_push_add_refspec(push, refspecs[i])); + specs.strings[i] = (char *) refspecs[i]; + + /* if EUSER, then abort in transfer */ + if (check_progress_cb && expected_ret == GIT_EUSER) + data->transfer_progress_calls = GIT_EUSER; + + error = git_remote_push(_remote, &specs, &opts, pusher, "test push"); + git__free(specs.strings); if (expected_ret < 0) { - cl_git_fail_with(git_push_finish(push), expected_ret); - cl_assert_equal_i(0, git_push_unpack_ok(push)); + cl_git_fail_with(expected_ret, error); } else { - cl_git_pass(git_push_finish(push)); - cl_assert_equal_i(1, git_push_unpack_ok(push)); + cl_git_pass(error); } - if (check_progress_cb && !expected_ret) { - cl_assert(pack_progress_calls > 0); - cl_assert(transfer_progress_calls > 0); + if (check_progress_cb && expected_ret == 0) { + cl_assert(data->pack_progress_calls > 0); + cl_assert(data->transfer_progress_calls > 0); } - do_verify_push_status(push, expected_statuses, expected_statuses_len); + do_verify_push_status(data, expected_statuses, expected_statuses_len); verify_refs(_remote, expected_refs, expected_refs_len); - - cl_git_pass(git_push_update_tips(push, pusher, "test push")); verify_tracking_branches(_remote, expected_refs, expected_refs_len); if (check_update_tips_cb) verify_update_tips_callback(_remote, expected_refs, expected_refs_len); - git_push_free(push); - - git_remote_disconnect(_remote); git_signature_free(pusher); } @@ -631,11 +626,11 @@ void test_online_push__multi(void) void test_online_push__implicit_tgt(void) { - const char *specs1[] = { "refs/heads/b1:" }; + const char *specs1[] = { "refs/heads/b1" }; push_status exp_stats1[] = { { "refs/heads/b1", 1 } }; expected_ref exp_refs1[] = { { "refs/heads/b1", &_oid_b1 } }; - const char *specs2[] = { "refs/heads/b2:" }; + const char *specs2[] = { "refs/heads/b2" }; push_status exp_stats2[] = { { "refs/heads/b2", 1 } }; expected_ref exp_refs2[] = { { "refs/heads/b1", &_oid_b1 }, @@ -838,22 +833,19 @@ void test_online_push__bad_refspecs(void) void test_online_push__expressions(void) { + git_push *push; + /* TODO: Expressions in refspecs doesn't actually work yet */ const char *specs_left_expr[] = { "refs/heads/b2~1:refs/heads/b2" }; - /* expect not NULL to indicate failure (core git replies "funny refname", - * other servers may be less pithy. */ - const char *specs_right_expr[] = { "refs/heads/b2:refs/heads/b2~1" }; - push_status exp_stats_right_expr[] = { { "refs/heads/b2~1", 0 } }; + cl_git_pass(git_push_new(&push, _remote)); + cl_git_fail(git_push_add_refspec(push, "refs/heads/b2:refs/heads/b2~1")); + git_push_free(push); /* TODO: Find a more precise way of checking errors than a exit code of -1. */ do_push(specs_left_expr, ARRAY_SIZE(specs_left_expr), NULL, 0, NULL, 0, -1, 0, 0); - - do_push(specs_right_expr, ARRAY_SIZE(specs_right_expr), - exp_stats_right_expr, ARRAY_SIZE(exp_stats_right_expr), - NULL, 0, 0, 1, 1); } void test_online_push__notes(void) @@ -885,3 +877,35 @@ void test_online_push__notes(void) git_signature_free(signature); } + +void test_online_push__configured(void) +{ + git_oid note_oid, *target_oid, expected_oid; + git_signature *signature; + const char *specs[] = { "refs/notes/commits:refs/notes/commits" }; + push_status exp_stats[] = { { "refs/notes/commits", 1 } }; + expected_ref exp_refs[] = { { "refs/notes/commits", &expected_oid } }; + const char *specs_del[] = { ":refs/notes/commits" }; + + git_oid_fromstr(&expected_oid, "8461a99b27b7043e58ff6e1f5d2cf07d282534fb"); + + target_oid = &_oid_b6; + + cl_git_pass(git_remote_add_push(_remote, specs[0])); + + /* Create note to push */ + cl_git_pass(git_signature_new(&signature, "nulltoken", "emeric.fermas@gmail.com", 1323847743, 60)); /* Wed Dec 14 08:29:03 2011 +0100 */ + cl_git_pass(git_note_create(¬e_oid, _repo, signature, signature, NULL, target_oid, "hello world\n", 0)); + + do_push(NULL, 0, + exp_stats, ARRAY_SIZE(exp_stats), + exp_refs, ARRAY_SIZE(exp_refs), 0, 1, 1); + + /* And make sure to delete the note */ + + do_push(specs_del, ARRAY_SIZE(specs_del), + exp_stats, 1, + NULL, 0, 0, 0, 0); + + git_signature_free(signature); +} diff --git a/tests/online/push_util.c b/tests/online/push_util.c index 68e71eacc..cd483c7c0 100644 --- a/tests/online/push_util.c +++ b/tests/online/push_util.c @@ -14,15 +14,31 @@ void updated_tip_free(updated_tip *t) git__free(t); } +void push_status_free(push_status *s) +{ + git__free(s->ref); + git__free(s->msg); + git__free(s); +} + void record_callbacks_data_clear(record_callbacks_data *data) { size_t i; updated_tip *tip; + push_status *status; git_vector_foreach(&data->updated_tips, i, tip) updated_tip_free(tip); git_vector_free(&data->updated_tips); + + git_vector_foreach(&data->statuses, i, status) + push_status_free(status); + + git_vector_free(&data->statuses); + + data->pack_progress_calls = 0; + data->transfer_progress_calls = 0; } int record_update_tips_cb(const char *refname, const git_oid *a, const git_oid *b, void *data) diff --git a/tests/online/push_util.h b/tests/online/push_util.h index 7736912d6..2e05c4ed7 100644 --- a/tests/online/push_util.h +++ b/tests/online/push_util.h @@ -12,7 +12,7 @@ extern const git_oid OID_ZERO; * @param data pointer to a record_callbacks_data instance */ #define RECORD_CALLBACKS_INIT(data) \ - { GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, cred_acquire_cb, NULL, NULL, record_update_tips_cb, data } + { GIT_REMOTE_CALLBACKS_VERSION, NULL, NULL, cred_acquire_cb, NULL, NULL, record_update_tips_cb, NULL, NULL, NULL, data } typedef struct { char *name; @@ -22,6 +22,9 @@ typedef struct { typedef struct { git_vector updated_tips; + git_vector statuses; + int pack_progress_calls; + int transfer_progress_calls; } record_callbacks_data; typedef struct { @@ -29,6 +32,15 @@ typedef struct { const git_oid *oid; } expected_ref; +/* the results of a push status. when used for expected values, msg may be NULL + * to indicate that it should not be matched. */ +typedef struct { + char *ref; + int success; + char *msg; +} push_status; + + void updated_tip_free(updated_tip *t); void record_callbacks_data_clear(record_callbacks_data *data); diff --git a/tests/refs/branches/upstream.c b/tests/refs/branches/upstream.c index abf7933d3..b20b93753 100644 --- a/tests/refs/branches/upstream.c +++ b/tests/refs/branches/upstream.c @@ -70,6 +70,22 @@ void test_refs_branches_upstream__upstream_remote(void) git_buf_free(&buf); } +void test_refs_branches_upstream__upstream_remote_empty_value(void) +{ + git_repository *repository; + git_config *cfg; + git_buf buf = GIT_BUF_INIT; + + repository = cl_git_sandbox_init("testrepo.git"); + cl_git_pass(git_repository_config(&cfg, repository)); + cl_git_pass(git_config_set_string(cfg, "branch.master.remote", "")); + cl_git_fail_with(GIT_ENOTFOUND, git_branch_upstream_remote(&buf, repository, "refs/heads/master")); + + cl_git_pass(git_config_delete_entry(cfg, "branch.master.remote")); + cl_git_fail_with(GIT_ENOTFOUND, git_branch_upstream_remote(&buf, repository, "refs/heads/master")); + cl_git_sandbox_cleanup(); +} + static void assert_merge_and_or_remote_key_missing(git_repository *repository, const git_commit *target, const char *entry_name) { git_reference *branch; diff --git a/tests/resources/crlf/.gitted/objects/04/4bcd5c9bf5ebdd51e514a9a36457018f06f6e1 b/tests/resources/crlf/.gitted/objects/04/4bcd5c9bf5ebdd51e514a9a36457018f06f6e1 new file mode 100644 index 000000000..a32a9b282 --- /dev/null +++ b/tests/resources/crlf/.gitted/objects/04/4bcd5c9bf5ebdd51e514a9a36457018f06f6e1 @@ -0,0 +1 @@ +x-j0D{W4H++C(ɡ]aJU|}̃ʺ.
кVE@vȔvBx=%l
sDxH!x3E9AhPdUTk{k+Av`C2|h괟lR{~/]`z-̥<]M5?]udr&K!
\ No newline at end of file diff --git a/tests/resources/crlf/.gitted/objects/12/faf3c1ea55f572473cec9052fca468c3584ccb b/tests/resources/crlf/.gitted/objects/12/faf3c1ea55f572473cec9052fca468c3584ccb deleted file mode 100644 index 96d5b2f91..000000000 --- a/tests/resources/crlf/.gitted/objects/12/faf3c1ea55f572473cec9052fca468c3584ccb +++ /dev/null @@ -1 +0,0 @@ -x 1}Nۀ,b6K6`.ؾQoab-A0dXbtnr:0cy(*Y<B0q m-y|TSα6Gsٗ^%.lu#Qg?w@j뵔DŠl?gDl7kP
\ No newline at end of file diff --git a/tests/resources/crlf/.gitted/objects/2b/55b4b94f655c857635b6a9005c056aa7de3532 b/tests/resources/crlf/.gitted/objects/2b/55b4b94f655c857635b6a9005c056aa7de3532 new file mode 100644 index 000000000..031fd6681 --- /dev/null +++ b/tests/resources/crlf/.gitted/objects/2b/55b4b94f655c857635b6a9005c056aa7de3532 @@ -0,0 +1,2 @@ +x-Kj0D)z2tca].OVȄ zRݶԛAvyIfLi.υPɘL0d<=&b{O.09o4ŜI˔ +G_jk -Yھ~XTW ذNjqKu:_ǣ]na.ZZk7!<#WF
\ No newline at end of file diff --git a/tests/resources/crlf/.gitted/objects/2c/9a868cfdf8e270d0ec68164433376c68fb1789 b/tests/resources/crlf/.gitted/objects/2c/9a868cfdf8e270d0ec68164433376c68fb1789 Binary files differdeleted file mode 100644 index 218e9d192..000000000 --- a/tests/resources/crlf/.gitted/objects/2c/9a868cfdf8e270d0ec68164433376c68fb1789 +++ /dev/null diff --git a/tests/resources/crlf/.gitted/objects/78/db270c1841841f75a8157321bdcb50ab12e6c3 b/tests/resources/crlf/.gitted/objects/78/db270c1841841f75a8157321bdcb50ab12e6c3 Binary files differnew file mode 100644 index 000000000..8a55bb082 --- /dev/null +++ b/tests/resources/crlf/.gitted/objects/78/db270c1841841f75a8157321bdcb50ab12e6c3 diff --git a/tests/resources/crlf/.gitted/objects/ba/aa042ab2976f8264e467988e6112ee518ec62e b/tests/resources/crlf/.gitted/objects/ba/aa042ab2976f8264e467988e6112ee518ec62e Binary files differdeleted file mode 100644 index 4c544d5ef..000000000 --- a/tests/resources/crlf/.gitted/objects/ba/aa042ab2976f8264e467988e6112ee518ec62e +++ /dev/null diff --git a/tests/resources/crlf/.gitted/refs/heads/master b/tests/resources/crlf/.gitted/refs/heads/master index cfdaaf37b..c150a9776 100644 --- a/tests/resources/crlf/.gitted/refs/heads/master +++ b/tests/resources/crlf/.gitted/refs/heads/master @@ -1 +1 @@ -2c9a868cfdf8e270d0ec68164433376c68fb1789 +044bcd5c9bf5ebdd51e514a9a36457018f06f6e1
diff --git a/tests/resources/crlf/.gitted/refs/heads/utf8 b/tests/resources/crlf/.gitted/refs/heads/utf8 index 4b32f7f91..f8e6cf51c 100644 --- a/tests/resources/crlf/.gitted/refs/heads/utf8 +++ b/tests/resources/crlf/.gitted/refs/heads/utf8 @@ -1 +1 @@ -baaa042ab2976f8264e467988e6112ee518ec62e +2b55b4b94f655c857635b6a9005c056aa7de3532 diff --git a/tests/status/worktree_init.c b/tests/status/worktree_init.c index 3e43c8c20..cc7e126f1 100644 --- a/tests/status/worktree_init.c +++ b/tests/status/worktree_init.c @@ -127,7 +127,6 @@ void test_status_worktree_init__bracket_in_filename(void) git_index *index; status_entry_single result; unsigned int status_flags; - int error; #define FILE_WITH_BRACKET "LICENSE[1].md" #define FILE_WITHOUT_BRACKET "LICENSE1.md" |
