summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJameson Miller <jamill@microsoft.com>2013-01-17 10:20:33 -0500
committerJameson Miller <jamill@microsoft.com>2013-01-22 10:01:43 -0500
commit1d645aabefa6ac9f3f617acb7e3b9025aafcd750 (patch)
treed70abce426d026f872ea79745817afcdb9157f98
parent47fc264203b2bae9e8a674505ac3502c3e9e71e7 (diff)
downloadlibgit2-1d645aabefa6ac9f3f617acb7e3b9025aafcd750.tar.gz
Update remote tips on push
-rw-r--r--include/git2/push.h9
-rw-r--r--src/push.c54
-rw-r--r--tests-clar/online/push.c103
3 files changed, 163 insertions, 3 deletions
diff --git a/include/git2/push.h b/include/git2/push.h
index 51f059ac4..6e07f368e 100644
--- a/include/git2/push.h
+++ b/include/git2/push.h
@@ -39,6 +39,15 @@ GIT_EXTERN(int) git_push_new(git_push **out, git_remote *remote);
GIT_EXTERN(int) git_push_add_refspec(git_push *push, const char *refspec);
/**
+ * Update remote tips after a push
+ *
+ * @param push The push object
+ *
+ * @return 0 or an error code
+ */
+GIT_EXTERN(int) git_push_update_tips(git_push *push);
+
+/**
* Actually push all given refspecs
*
* @param push The push object
diff --git a/src/push.c b/src/push.c
index 452ead405..ddfe5ec07 100644
--- a/src/push.c
+++ b/src/push.c
@@ -161,6 +161,60 @@ int git_push_add_refspec(git_push *push, const char *refspec)
return 0;
}
+int git_push_update_tips(git_push *push)
+{
+ git_refspec *fetch_spec = &push->remote->fetch;
+ git_buf remote_ref_name = GIT_BUF_INIT;
+ size_t i, j;
+ push_spec *push_spec;
+ git_reference *remote_ref;
+ push_status *status;
+ int error = 0;
+
+ git_vector_foreach(&push->status, i, status) {
+ /* If this ref update was successful (ok, not ng), it will have an empty message */
+ if (status->msg)
+ continue;
+
+ /* Find the corresponding remote ref */
+ if (!git_refspec_src_matches(fetch_spec, status->ref))
+ continue;
+
+ if ((error = git_refspec_transform_r(&remote_ref_name, fetch_spec, status->ref)) < 0)
+ goto on_error;
+
+ /* Find matching push ref spec */
+ git_vector_foreach(&push->specs, j, push_spec) {
+ if (!strcmp(push_spec->rref, status->ref))
+ break;
+ }
+
+ /* Could not find the corresponding push ref spec for this push update */
+ if (j == push->specs.length)
+ continue;
+
+ /* Update the remote ref */
+ if (git_oid_iszero(&push_spec->loid)) {
+ error = git_reference_lookup(&remote_ref, push->remote->repo, git_buf_cstr(&remote_ref_name));
+
+ if (!error) {
+ if ((error = git_reference_delete(remote_ref)) < 0)
+ goto on_error;
+ } else if (error == GIT_ENOTFOUND)
+ giterr_clear();
+ else
+ goto on_error;
+ } else if ((error = git_reference_create(NULL, push->remote->repo, git_buf_cstr(&remote_ref_name), &push_spec->loid, 1)) < 0)
+ goto on_error;
+ }
+
+ error = 0;
+
+on_error:
+ git_buf_free(&remote_ref_name);
+ return error;
+}
+
static int revwalk(git_vector *commits, git_push *push)
{
git_remote_head *head;
diff --git a/tests-clar/online/push.c b/tests-clar/online/push.c
index 15351ae08..a065e4b78 100644
--- a/tests-clar/online/push.c
+++ b/tests-clar/online/push.c
@@ -4,6 +4,8 @@
#include "vector.h"
#include "../submodule/submodule_helpers.h"
#include "push_util.h"
+#include "refspec.h"
+#include "remote.h"
static git_repository *_repo;
@@ -127,6 +129,100 @@ static void verify_refs(git_remote *remote, expected_ref expected_refs[], size_t
git_vector_free(&actual_refs);
}
+static int tracking_branch_list_cb(const char *branch_name, git_branch_t branch_type, void *payload)
+{
+ git_vector *tracking = (git_vector *)payload;
+
+ if (branch_type == GIT_BRANCH_REMOTE)
+ git_vector_insert(tracking, git__strdup(branch_name));
+ else
+ GIT_UNUSED(branch_name);
+
+ return 0;
+}
+
+/**
+ * Verifies that after git_push_update_tips(), remote tracking branches have the expected
+ * names and oids.
+ *
+ * @param remote remote to verify
+ * @param expected_refs expected remote refs after push
+ * @param expected_refs_len length of expected_refs
+ */
+static void verify_tracking_branches(git_remote *remote, expected_ref expected_refs[], size_t expected_refs_len)
+{
+ git_refspec *fetch_spec = &remote->fetch;
+ size_t i, j;
+ git_buf msg = GIT_BUF_INIT;
+ git_buf ref_name = GIT_BUF_INIT;
+ git_buf canonical_ref_name = GIT_BUF_INIT;
+ git_vector actual_refs = GIT_VECTOR_INIT;
+ char *actual_ref;
+ git_oid oid;
+ int failed = 0;
+
+ /* Get current remote branches */
+ cl_git_pass(git_branch_foreach(remote->repo, GIT_BRANCH_REMOTE, tracking_branch_list_cb, &actual_refs));
+
+ /* Loop through expected refs, make sure they exist */
+ for (i = 0; i < expected_refs_len; i++) {
+
+ /* Convert remote reference name into tracking branch name.
+ * If the spec is not under refs/heads/, then skip.
+ */
+ if (!git_refspec_src_matches(fetch_spec, expected_refs[i].name))
+ continue;
+
+ cl_git_pass(git_refspec_transform_r(&ref_name, fetch_spec, expected_refs[i].name));
+
+ /* Find matching remote branch */
+ git_vector_foreach(&actual_refs, j, actual_ref) {
+
+ /* Construct canonical ref name from the actual_ref name */
+ git_buf_clear(&canonical_ref_name);
+ cl_git_pass(git_buf_printf(&canonical_ref_name, "refs/remotes/%s", actual_ref));
+ if (!strcmp(git_buf_cstr(&ref_name), git_buf_cstr(&canonical_ref_name)))
+ break;
+ }
+
+ if (j == actual_refs.length) {
+ git_buf_printf(&msg, "Did not find expected tracking branch '%s'.", git_buf_cstr(&ref_name));
+ failed = 1;
+ goto failed;
+ }
+
+ /* Make sure tracking branch is at expected commit ID */
+ cl_git_pass(git_reference_name_to_id(&oid, remote->repo, git_buf_cstr(&canonical_ref_name)));
+
+ if (git_oid_cmp(expected_refs[i].oid, &oid) != 0) {
+ git_buf_puts(&msg, "Tracking branch commit does not match expected ID.");
+ failed = 1;
+ goto failed;
+ }
+
+ cl_git_pass(git_vector_remove(&actual_refs, j));
+ }
+
+ /* Make sure there are no extra branches */
+ if (actual_refs.length > 0) {
+ git_buf_puts(&msg, "Unexpected remote tracking branches exist.");
+ failed = 1;
+ goto failed;
+ }
+
+failed:
+
+ if(failed)
+ cl_fail(git_buf_cstr(&msg));
+
+ git_vector_foreach(&actual_refs, i, actual_ref)
+ git__free(actual_ref);
+
+ git_vector_free(&actual_refs);
+ git_buf_free(&msg);
+ return;
+}
+
void test_online_push__initialize(void)
{
git_vector delete_specs = GIT_VECTOR_INIT;
@@ -265,11 +361,12 @@ static void do_push(const char *refspecs[], size_t refspecs_len,
cl_assert_equal_i(expected_ret, ret);
- git_push_free(push);
-
verify_refs(_remote, expected_refs, expected_refs_len);
- cl_git_pass(git_remote_update_tips(_remote));
+ cl_git_pass(git_push_update_tips(push));
+ verify_tracking_branches(_remote, expected_refs, expected_refs_len);
+
+ git_push_free(push);
git_remote_disconnect(_remote);
}