Merge remote-tracking branch 'fraser/feature/push'
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}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@@ -93,13 +93,12 @@ class BlobTest(utils.RepoTestCase):
|
|||||||
|
|
||||||
|
|
||||||
def test_create_blob_outside_workdir(self):
|
def test_create_blob_outside_workdir(self):
|
||||||
path = join(dirname(__file__), 'data', self.repo_dir + '.tar')
|
path = __file__
|
||||||
self.assertRaises(KeyError, self.repo.create_blob_fromworkdir, path)
|
self.assertRaises(KeyError, self.repo.create_blob_fromworkdir, path)
|
||||||
|
|
||||||
|
|
||||||
def test_create_blob_fromdisk(self):
|
def test_create_blob_fromdisk(self):
|
||||||
path = join(dirname(__file__), 'data', self.repo_dir + '.tar')
|
blob_oid = self.repo.create_blob_fromdisk(__file__)
|
||||||
blob_oid = self.repo.create_blob_fromdisk(path)
|
|
||||||
blob = self.repo[blob_oid]
|
blob = self.repo[blob_oid]
|
||||||
|
|
||||||
self.assertTrue(isinstance(blob, pygit2.Blob))
|
self.assertTrue(isinstance(blob, pygit2.Blob))
|
||||||
|
@@ -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()
|
||||||
|
@@ -143,7 +143,7 @@ class RepositoryTest(utils.BareRepoTestCase):
|
|||||||
|
|
||||||
def test_get_path(self):
|
def test_get_path(self):
|
||||||
directory = realpath(self.repo.path)
|
directory = realpath(self.repo.path)
|
||||||
expected = realpath(join(self._temp_dir, 'testrepo.git'))
|
expected = realpath(self.repo_path)
|
||||||
self.assertEqual(directory, expected)
|
self.assertEqual(directory, expected)
|
||||||
|
|
||||||
def test_get_workdir(self):
|
def test_get_workdir(self):
|
||||||
@@ -179,12 +179,12 @@ class RepositoryTest_II(utils.RepoTestCase):
|
|||||||
|
|
||||||
def test_get_path(self):
|
def test_get_path(self):
|
||||||
directory = realpath(self.repo.path)
|
directory = realpath(self.repo.path)
|
||||||
expected = realpath(join(self._temp_dir, 'testrepo', '.git'))
|
expected = realpath(join(self.repo_path, '.git'))
|
||||||
self.assertEqual(directory, expected)
|
self.assertEqual(directory, expected)
|
||||||
|
|
||||||
def test_get_workdir(self):
|
def test_get_workdir(self):
|
||||||
directory = realpath(self.repo.workdir)
|
directory = realpath(self.repo.workdir)
|
||||||
expected = realpath(join(self._temp_dir, 'testrepo'))
|
expected = realpath(self.repo_path)
|
||||||
self.assertEqual(directory, expected)
|
self.assertEqual(directory, expected)
|
||||||
|
|
||||||
def test_checkout_ref(self):
|
def test_checkout_ref(self):
|
||||||
|
@@ -65,6 +65,27 @@ def rmtree(path):
|
|||||||
shutil.rmtree(path, onerror=onerror)
|
shutil.rmtree(path, onerror=onerror)
|
||||||
|
|
||||||
|
|
||||||
|
class TemporaryRepository(object):
|
||||||
|
def __init__(self, repo_spec):
|
||||||
|
self.repo_spec = repo_spec
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
container, name = self.repo_spec
|
||||||
|
repo_path = os.path.join(os.path.dirname(__file__), 'data', name)
|
||||||
|
self.temp_dir = tempfile.mkdtemp()
|
||||||
|
temp_repo_path = os.path.join(self.temp_dir, name)
|
||||||
|
if container == 'tar':
|
||||||
|
tar = tarfile.open('.'.join((repo_path, 'tar')))
|
||||||
|
tar.extractall(self.temp_dir)
|
||||||
|
tar.close()
|
||||||
|
else:
|
||||||
|
shutil.copytree(repo_path, temp_repo_path)
|
||||||
|
return temp_repo_path
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_value, traceback):
|
||||||
|
rmtree(self.temp_dir)
|
||||||
|
|
||||||
|
|
||||||
class NoRepoTestCase(unittest.TestCase):
|
class NoRepoTestCase(unittest.TestCase):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@@ -103,45 +124,33 @@ class NoRepoTestCase(unittest.TestCase):
|
|||||||
self.assertEqual(a.offset, b.offset)
|
self.assertEqual(a.offset, b.offset)
|
||||||
|
|
||||||
|
|
||||||
class BareRepoTestCase(NoRepoTestCase):
|
class AutoRepoTestCase(NoRepoTestCase):
|
||||||
|
|
||||||
repo_dir = 'testrepo.git'
|
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(BareRepoTestCase, self).setUp()
|
super(AutoRepoTestCase, self).setUp()
|
||||||
|
self.repo_ctxtmgr = TemporaryRepository(self.repo_spec)
|
||||||
|
self.repo_path = self.repo_ctxtmgr.__enter__()
|
||||||
|
self.repo = pygit2.Repository(self.repo_path)
|
||||||
|
|
||||||
repo_dir = self.repo_dir
|
def tearDown(self):
|
||||||
repo_path = os.path.join(os.path.dirname(__file__), 'data', repo_dir)
|
self.repo_ctxtmgr.__exit__(None, None, None)
|
||||||
temp_repo_path = os.path.join(self._temp_dir, repo_dir)
|
super(AutoRepoTestCase, self).tearDown()
|
||||||
|
|
||||||
shutil.copytree(repo_path, temp_repo_path)
|
|
||||||
|
|
||||||
self.repo = pygit2.Repository(temp_repo_path)
|
|
||||||
|
|
||||||
|
|
||||||
class RepoTestCase(NoRepoTestCase):
|
class BareRepoTestCase(AutoRepoTestCase):
|
||||||
|
|
||||||
repo_dir = 'testrepo'
|
repo_spec = 'git', 'testrepo.git'
|
||||||
|
|
||||||
def setUp(self):
|
|
||||||
super(RepoTestCase, self).setUp()
|
|
||||||
|
|
||||||
repo_dir = self.repo_dir
|
|
||||||
repo_path = os.path.join(os.path.dirname(__file__), 'data', repo_dir)
|
|
||||||
temp_repo_path = os.path.join(self._temp_dir, repo_dir, '.git')
|
|
||||||
|
|
||||||
tar = tarfile.open(repo_path + '.tar')
|
|
||||||
tar.extractall(self._temp_dir)
|
|
||||||
tar.close()
|
|
||||||
|
|
||||||
self.repo = pygit2.Repository(temp_repo_path)
|
|
||||||
|
|
||||||
|
|
||||||
class DirtyRepoTestCase(RepoTestCase):
|
class RepoTestCase(AutoRepoTestCase):
|
||||||
|
|
||||||
repo_dir = 'dirtyrepo'
|
repo_spec = 'tar', 'testrepo'
|
||||||
|
|
||||||
|
|
||||||
class EmptyRepoTestCase(RepoTestCase):
|
class DirtyRepoTestCase(AutoRepoTestCase):
|
||||||
|
|
||||||
repo_dir = 'emptyrepo'
|
repo_spec = 'tar', 'dirtyrepo'
|
||||||
|
|
||||||
|
|
||||||
|
class EmptyRepoTestCase(AutoRepoTestCase):
|
||||||
|
|
||||||
|
repo_spec = 'tar', 'emptyrepo'
|
||||||
|
Reference in New Issue
Block a user