summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEdward Thomson <ethomson@edwardthomson.com>2014-11-18 11:44:59 -0500
committerEdward Thomson <ethomson@edwardthomson.com>2014-11-18 11:44:59 -0500
commit45301cca309abf4b4c126890a6a7ae024714cfc9 (patch)
treec26ec02d5e126e514e9dd0ffe600f5e313281228
parent8b5b814e553093295085144976eef2df5bfbafa3 (diff)
parent64e3e6d43acbbf6cc8f8a4c612547c5a575c4031 (diff)
downloadlibgit2-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.h40
-rw-r--r--src/remote.c60
-rw-r--r--tests/online/push.c143
-rw-r--r--tests/online/push_util.c16
-rw-r--r--tests/online/push_util.h14
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(&note_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);