Commit 1d645aab by Jameson Miller

Update remote tips on push

parent 47fc2642
......@@ -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
......
......@@ -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;
......
......@@ -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);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment