From 3dd998c061f769081e385ecc4065e05b1d362cd3 Mon Sep 17 00:00:00 2001 From: Bernardo Heynemann Date: Wed, 15 May 2013 18:15:00 -0300 Subject: [PATCH] Supporting clone in pygit2 --- pygit2/__init__.py | 19 ++++++++++++++ src/pygit2.c | 56 +++++++++++++++++++++++++++++++++++++++++ test/test_repository.py | 42 ++++++++++++++++++++++++++++++- 3 files changed, 116 insertions(+), 1 deletion(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index aeb7fe5..44b31e7 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -47,3 +47,22 @@ def init_repository(path, bare=False): """ _pygit2.init_repository(path, bare) return Repository(path) + + +def clone_repository( + url, path, bare=False, remote_name="origin", push_url=None, fetch_spec=None, + push_spec=None, checkout_branch=None): + """ + Clones a new Git repository from *url* in the given *path*. + + Parameters: + * If 'bare' is True, then a bare git repository will be created. + * 'remote_name' is the name given to the "origin" remote. The default is "origin". + * 'push_url' is a URL to be used for pushing. None means use the fetch url. + * 'fetch_spec' is the fetch specification to be used for fetching. None results in the same behavior as GIT_REMOTE_DEFAULT_FETCH. + * 'push_spec' is the fetch specification to be used for pushing. None means use the same spec as for 'fetch_spec'. + * 'checkout_branch' gives the name of the branch to checkout. None means use the remote's HEAD + """ + + _pygit2.clone_repository(url, path, bare, remote_name, push_url, fetch_spec, push_spec, checkout_branch) + return Repository(path) diff --git a/src/pygit2.c b/src/pygit2.c index c68e530..ed1ad9d 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -97,6 +97,61 @@ init_repository(PyObject *self, PyObject *args) { Py_RETURN_NONE; }; +PyDoc_STRVAR(clone_repository__doc__, + "clone_repository(url, path, bare, remote_name, push_url, fetch_spec, push_spec, checkout_branch)\n" + "\n" + "Clones a Git repository in the given url to the given path with the specified options.\n" + "\n" + "Arguments:\n" + "\n" + "url\n" + " Git repository remote url.\n" + "path\n" + " Path where to create the repository.\n" + "bare\n" + " If 'bare' is not 0, then a bare git repository will be created.\n" + "remote_name\n" + " The name given to the 'origin' remote. The default is 'origin'.\n" + "push_url\n" + " URL to be used for pushing.\n" + "fetch_spec\n" + " The fetch specification to be used for fetching. None results in the same behavior as GIT_REMOTE_DEFAULT_FETCH.\n" + "push_spec\n" + " The fetch specification to be used for pushing. None means use the same spec as for 'fetch_spec'\n" + "checkout_branch\n" + " The name of the branch to checkout. None means use the remote's HEAD.\n"); + + +PyObject * +clone_repository(PyObject *self, PyObject *args) { + git_repository *repo; + const char *url; + const char *path; + unsigned int bare; + const char *remote_name, *push_url, *fetch_spec, *push_spec, *checkout_branch; + int err; + + if (!PyArg_ParseTuple(args, "zzIzzzzz", &url, &path, &bare, &remote_name, &push_url, &fetch_spec, &push_spec, &checkout_branch)) + return NULL; + + git_clone_options opts = { + .version=1, + .bare=bare, + .remote_name=remote_name, + .pushurl=push_url, + .fetch_spec=fetch_spec, + .push_spec=push_spec, + .checkout_branch=checkout_branch + }; + + err = git_clone(&repo, url, path, &opts); + if (err < 0) + return Error_set_str(err, path); + + git_repository_free(repo); + Py_RETURN_NONE; +}; + PyDoc_STRVAR(discover_repository__doc__, "discover_repository(path[, across_fs[, ceiling_dirs]]) -> str\n" @@ -172,6 +227,7 @@ hash(PyObject *self, PyObject *args) PyMethodDef module_methods[] = { {"init_repository", init_repository, METH_VARARGS, init_repository__doc__}, + {"clone_repository", clone_repository, METH_VARARGS, clone_repository__doc__}, {"discover_repository", discover_repository, METH_VARARGS, discover_repository__doc__}, {"hashfile", hashfile, METH_VARARGS, hashfile__doc__}, diff --git a/test/test_repository.py b/test/test_repository.py index b11c804..f1771e9 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -40,7 +40,7 @@ from os.path import join, realpath # Import from pygit2 from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT -from pygit2 import init_repository, discover_repository, Reference, hashfile +from pygit2 import init_repository, clone_repository, discover_repository, Reference, hashfile from pygit2 import Oid import pygit2 from . import utils @@ -291,6 +291,46 @@ class EmptyRepositoryTest(utils.EmptyRepoTestCase): self.assertTrue(self.repo.head_is_orphaned) self.assertFalse(self.repo.head_is_detached) +class CloneRepositoryTest(utils.NoRepoTestCase): + def test_clone_repository(self): + repo = clone_repository("./test/data/testrepo.git/", self._temp_dir) + self.assertFalse(repo.is_empty) + self.assertFalse(repo.is_bare) + + def test_clone_bare_repository(self): + repo = clone_repository("./test/data/testrepo.git/", self._temp_dir, bare=True) + self.assertFalse(repo.is_empty) + self.assertTrue(repo.is_bare) + + def test_clone_remote_name(self): + repo = clone_repository("./test/data/testrepo.git/", self._temp_dir, remote_name="custom_remote") + self.assertFalse(repo.is_empty) + self.assertEqual(repo.remotes[0].name, "custom_remote") + + def test_clone_push_url(self): + repo = clone_repository("./test/data/testrepo.git/", self._temp_dir, push_url="custom_push_url") + self.assertFalse(repo.is_empty) + # not sure how to test this... couldn't find pushurl + # self.assertEqual(repo.remotes[0].pushurl, "custom_push_url") + + def test_clone_fetch_spec(self): + repo = clone_repository("./test/data/testrepo.git/", self._temp_dir, fetch_spec="refs/heads/test") + self.assertFalse(repo.is_empty) + # not sure how to test this either... fetchspec seems to be going through, but repo is not getting it. + # self.assertEqual(repo.remotes[0].fetchspec, "refs/heads/test") + + def test_clone_push_spec(self): + repo = clone_repository("./test/data/testrepo.git/", self._temp_dir, push_spec="refs/heads/test") + self.assertFalse(repo.is_empty) + # not sure how to test this either... couldn't find pushspec + # self.assertEqual(repo.remotes[0].fetchspec, "refs/heads/test") + + def test_clone_checkout_branch(self): + repo = clone_repository("./test/data/testrepo.git/", self._temp_dir, checkout_branch="test") + self.assertFalse(repo.is_empty) + # not sure how to test this either... couldn't find current branch + # self.assertEqual(repo.remotes[0].current_branch, "test") + if __name__ == '__main__': unittest.main()