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:
@@ -15,4 +15,5 @@ The Remote type
|
|||||||
.. autoattribute:: pygit2.Remote.refspec_count
|
.. autoattribute:: pygit2.Remote.refspec_count
|
||||||
.. automethod:: pygit2.Remote.get_refspec
|
.. automethod:: pygit2.Remote.get_refspec
|
||||||
.. automethod:: pygit2.Remote.fetch
|
.. automethod:: pygit2.Remote.fetch
|
||||||
|
.. automethod:: pygit2.Remote.push
|
||||||
.. automethod:: pygit2.Remote.save
|
.. automethod:: pygit2.Remote.save
|
||||||
|
66
src/remote.c
66
src/remote.c
@@ -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[] = {
|
PyMethodDef Remote_methods[] = {
|
||||||
METHOD(Remote, fetch, METH_NOARGS),
|
METHOD(Remote, fetch, METH_NOARGS),
|
||||||
METHOD(Remote, save, METH_NOARGS),
|
METHOD(Remote, save, METH_NOARGS),
|
||||||
METHOD(Remote, get_refspec, METH_O),
|
METHOD(Remote, get_refspec, METH_O),
|
||||||
|
METHOD(Remote, push, METH_VARARGS),
|
||||||
{NULL}
|
{NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -124,5 +124,45 @@ class EmptyRepositoryTest(utils.EmptyRepoTestCase):
|
|||||||
self.assertEqual(stats['received_objects'], REMOTE_REPO_OBJECTS)
|
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__':
|
if __name__ == '__main__':
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
Reference in New Issue
Block a user