diff options
author | Edward Thomson <ethomson@edwardthomson.com> | 2014-11-18 11:44:59 -0500 |
---|---|---|
committer | Edward Thomson <ethomson@edwardthomson.com> | 2014-11-18 11:44:59 -0500 |
commit | 45301cca309abf4b4c126890a6a7ae024714cfc9 (patch) | |
tree | c26ec02d5e126e514e9dd0ffe600f5e313281228 | |
parent | 8b5b814e553093295085144976eef2df5bfbafa3 (diff) | |
parent | 64e3e6d43acbbf6cc8f8a4c612547c5a575c4031 (diff) | |
download | libgit2-45301cca309abf4b4c126890a6a7ae024714cfc9.tar.gz |
Merge pull request #2608 from libgit2/cmn/remote-push
Provide a convenience function `git_remote_push()`
-rw-r--r-- | include/git2/remote.h | 40 | ||||
-rw-r--r-- | src/remote.c | 60 | ||||
-rw-r--r-- | tests/online/push.c | 143 | ||||
-rw-r--r-- | tests/online/push_util.c | 16 | ||||
-rw-r--r-- | tests/online/push_util.h | 14 |
5 files changed, 214 insertions, 59 deletions
diff --git a/include/git2/remote.h b/include/git2/remote.h index 0d7fd230f..ef242293c 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 @@ -390,6 +391,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 +480,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/src/remote.c b/src/remote.c index 2897b456d..3715e43ea 100644 --- a/src/remote.c +++ b/src/remote.c @@ -2115,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/tests/online/push.c b/tests/online/push.c index 1d4f9bc60..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); } @@ -882,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); |