implement push support

Implement push support via Remote.push which is called with a single
refspec and raises GitError (with an appropriate message where
possible) if the push fails.

Note that local push to non-bare repository is currently not
supported by libgit2.
This commit is contained in:
Fraser Tweedale 2013-08-25 19:51:20 +10:00
parent 74b1628e51
commit 134d87ab2a
3 changed files with 107 additions and 0 deletions

View File

@ -15,4 +15,5 @@ The Remote type
.. autoattribute:: pygit2.Remote.refspec_count
.. automethod:: pygit2.Remote.get_refspec
.. automethod:: pygit2.Remote.fetch
.. automethod:: pygit2.Remote.push
.. automethod:: pygit2.Remote.save

View File

@ -218,10 +218,76 @@ Remote_save(Remote *self, PyObject *args)
}
int
push_status_foreach_callback(const char *ref, const char *msg, void *data)
{
const char **msg_dst = (const char **)data;
if (msg != NULL && *msg_dst == NULL)
*msg_dst = msg;
return 0;
}
PyDoc_STRVAR(Remote_push__doc__,
"push(refspec)\n"
"\n"
"Push the given refspec to the remote. Raises ``GitError`` on error.");
PyObject *
Remote_push(Remote *self, PyObject *args)
{
git_push *push = NULL;
const char *refspec = NULL;
const char *msg = NULL;
int err;
if (!PyArg_ParseTuple(args, "s", &refspec))
return NULL;
err = git_push_new(&push, self->remote);
if (err < 0)
return Error_set(err);
err = git_push_add_refspec(push, refspec);
if (err < 0)
goto error;
err = git_push_finish(push);
if (err < 0)
goto error;
if (!git_push_unpack_ok(push)) {
git_push_free(push);
PyErr_SetString(GitError, "Remote failed to unpack objects");
return NULL;
}
err = git_push_status_foreach(push, push_status_foreach_callback, &msg);
if (err < 0)
goto error;
if (msg != NULL) {
git_push_free(push);
PyErr_SetString(GitError, msg);
return NULL;
}
err = git_push_update_tips(push);
if (err < 0)
goto error;
git_push_free(push);
Py_RETURN_NONE;
error:
git_push_free(push);
return Error_set(err);
}
PyMethodDef Remote_methods[] = {
METHOD(Remote, fetch, METH_NOARGS),
METHOD(Remote, save, METH_NOARGS),
METHOD(Remote, get_refspec, METH_O),
METHOD(Remote, push, METH_VARARGS),
{NULL}
};

View File

@ -124,5 +124,45 @@ class EmptyRepositoryTest(utils.EmptyRepoTestCase):
self.assertEqual(stats['received_objects'], REMOTE_REPO_OBJECTS)
class PushTestCase(unittest.TestCase):
def setUp(self):
self.origin_ctxtmgr = utils.TemporaryRepository(('git', 'testrepo.git'))
self.clone_ctxtmgr = utils.TemporaryRepository(('git', 'testrepo.git'))
self.origin = pygit2.Repository(self.origin_ctxtmgr.__enter__())
self.clone = pygit2.Repository(self.clone_ctxtmgr.__enter__())
self.remote = self.clone.create_remote('origin', self.origin.path)
def tearDown(self):
self.origin_ctxtmgr.__exit__(None, None, None)
self.clone_ctxtmgr.__exit__(None, None, None)
def test_push_fast_forward_commits_to_remote_succeeds(self):
tip = self.clone[self.clone.head.target]
oid = self.clone.create_commit(
'refs/heads/master', tip.author, tip.author, 'empty commit',
tip.tree.oid, [tip.oid]
)
self.remote.push('refs/heads/master')
self.assertEqual(self.origin[self.origin.head.target].oid, oid)
def test_push_when_up_to_date_succeeds(self):
self.remote.push('refs/heads/master')
origin_tip = self.origin[self.origin.head.target].oid
clone_tip = self.clone[self.clone.head.target].oid
self.assertEqual(origin_tip, clone_tip)
def test_push_non_fast_forward_commits_to_remote_fails(self):
tip = self.origin[self.origin.head.target]
oid = self.origin.create_commit(
'refs/heads/master', tip.author, tip.author, 'some commit',
tip.tree.oid, [tip.oid]
)
tip = self.clone[self.clone.head.target]
oid = self.clone.create_commit(
'refs/heads/master', tip.author, tip.author, 'other commit',
tip.tree.oid, [tip.oid]
)
self.assertRaises(pygit2.GitError, self.remote.push, 'refs/heads/master')
if __name__ == '__main__':
unittest.main()