From af38211d0cb0123db35dd85ffb976c082ae33520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 11 Jul 2014 13:47:24 +0200 Subject: [PATCH 1/2] checkout: move the code to cffi As part of this, make the strategy part of **kwargs, in preparation for supporting more options. --- pygit2/decl.h | 6 ++++ pygit2/repository.py | 61 ++++++++++++++++++++++++++++++--- src/repository.c | 76 ----------------------------------------- test/test_repository.py | 4 +-- 4 files changed, 64 insertions(+), 83 deletions(-) diff --git a/pygit2/decl.h b/pygit2/decl.h index 09a8ad94b..5573ecd83 100644 --- a/pygit2/decl.h +++ b/pygit2/decl.h @@ -3,6 +3,7 @@ typedef ... git_remote; typedef ... git_refspec; typedef ... git_push; typedef ... git_cred; +typedef ... git_object; typedef ... git_tree; typedef ... git_signature; typedef ... git_index; @@ -309,6 +310,11 @@ typedef enum { GIT_CLONE_LOCAL_NO_LINKS, } git_clone_local_t; +int git_checkout_init_options(git_checkout_options *opts, unsigned int version); +int git_checkout_tree(git_repository *repo, const git_object *treeish, const git_checkout_options *opts); +int git_checkout_head(git_repository *repo, const git_checkout_options *opts); +int git_checkout_index(git_repository *repo, git_index *index, const git_checkout_options *opts); + /* * git_clone */ diff --git a/pygit2/repository.py b/pygit2/repository.py index 1be5bbb98..bffc2d8b2 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -176,11 +176,52 @@ def create_reference(self, name, target, force=False): return self.create_reference_symbolic(name, target, force) - # # Checkout # - def checkout(self, refname=None, strategy=GIT_CHECKOUT_SAFE_CREATE): + @staticmethod + def _checkout_args_to_options(**kwargs): + # Create the options struct to pass + copts = ffi.new('git_checkout_options *') + check_error(C.git_checkout_init_options(copts, 1)) + + # pygit2's default is SAFE_CREATE + copts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE + # and go through the arguments to see what the user wanted + for k, v in kwargs.iteritems(): + if k == 'strategy': + copts.checkout_strategy = v + + return copts + + def checkout_head(self, **kwargs): + """Checkout HEAD + + For arguments, see Repository.checkout(). + """ + copts = Repository._checkout_args_to_options(**kwargs) + check_error(C.git_checkout_head(self._repo, copts)) + + def checkout_index(self, **kwargs): + """Checkout the repository's index + + For arguments, see Repository.checkout(). + """ + copts = Repository._checkout_args_to_options(**kwargs) + check_error(C.git_checkout_index(self._repo, ffi.NULL, copts)) + + def checkout_tree(self, treeish, **kwargs): + """Checkout the given treeish + + For arguments, see Repository.checkout(). + """ + copts = Repository._checkout_args_to_options(**kwargs) + cptr = ffi.new('git_object **') + ffi.buffer(cptr)[:] = treeish._pointer[:] + + check_error(C.git_checkout_tree(self._repo, cptr[0], copts)) + + def checkout(self, refname=None, **kwargs): """ Checkout the given reference using the given strategy, and update the HEAD. @@ -193,14 +234,24 @@ def checkout(self, refname=None, strategy=GIT_CHECKOUT_SAFE_CREATE): If no reference is given, checkout from the index. + Arguments: + + :param str refname: The reference to checkout. After checkout, + the current branch will be switched to this one. + + :param int strategy: A ``GIT_CHECKOUT_`` value. The default is + ``GIT_CHECKOUT_SAFE_CREATE``. + """ + + # Case 1: Checkout index if refname is None: - return self.checkout_index(strategy) + return self.checkout_index(**kwargs) # Case 2: Checkout head if refname == 'HEAD': - return self.checkout_head(strategy) + return self.checkout_head(**kwargs) # Case 3: Reference if type(refname) is Reference: @@ -211,7 +262,7 @@ def checkout(self, refname=None, strategy=GIT_CHECKOUT_SAFE_CREATE): oid = reference.resolve().target treeish = self[oid] - self.checkout_tree(treeish, strategy) + self.checkout_tree(treeish, **kwargs) self.head = refname diff --git a/src/repository.c b/src/repository.c index 240816451..5d1c533f2 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1288,79 +1288,6 @@ Repository__pointer__get__(Repository *self) return PyBytes_FromStringAndSize((char *) &self->repo, sizeof(git_repository *)); } -PyDoc_STRVAR(Repository_checkout_head__doc__, - "checkout_head(strategy)\n" - "\n" - "Checkout the head using the given strategy."); - -PyObject * -Repository_checkout_head(Repository *self, PyObject *args) -{ - git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; - unsigned int strategy; - int err; - - if (!PyArg_ParseTuple(args, "I", &strategy)) - return NULL; - - opts.checkout_strategy = strategy; - err = git_checkout_head(self->repo, &opts); - if (err < 0) - return Error_set(err); - - Py_RETURN_NONE; -} - - -PyDoc_STRVAR(Repository_checkout_index__doc__, - "checkout_index(strategy)\n" - "\n" - "Checkout the index using the given strategy."); - -PyObject * -Repository_checkout_index(Repository *self, PyObject *args) -{ - git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; - unsigned int strategy; - int err; - - if (!PyArg_ParseTuple(args, "I", &strategy)) - return NULL; - - opts.checkout_strategy = strategy; - err = git_checkout_index(self->repo, NULL, &opts); - if (err < 0) - return Error_set(err); - - Py_RETURN_NONE; -} - - -PyDoc_STRVAR(Repository_checkout_tree__doc__, - "checkout_tree(treeish, strategy)\n" - "\n" - "Checkout the given tree, commit or tag, using the given strategy."); - -PyObject * -Repository_checkout_tree(Repository *self, PyObject *args) -{ - git_checkout_options opts = GIT_CHECKOUT_OPTIONS_INIT; - unsigned int strategy; - Object *py_object; - int err; - - if (!PyArg_ParseTuple(args, "O!I", &ObjectType, &py_object, &strategy)) - return NULL; - - opts.checkout_strategy = strategy; - err = git_checkout_tree(self->repo, py_object->obj, &opts); - if (err < 0) - return Error_set(err); - - Py_RETURN_NONE; -} - - PyDoc_STRVAR(Repository_notes__doc__, ""); PyObject * @@ -1570,9 +1497,6 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, revparse_single, METH_O), METHOD(Repository, status, METH_NOARGS), METHOD(Repository, status_file, METH_O), - METHOD(Repository, checkout_head, METH_VARARGS), - METHOD(Repository, checkout_index, METH_VARARGS), - METHOD(Repository, checkout_tree, METH_VARARGS), METHOD(Repository, notes, METH_VARARGS), METHOD(Repository, create_note, METH_VARARGS), METHOD(Repository, lookup_note, METH_VARARGS), diff --git a/test/test_repository.py b/test/test_repository.py index 4c28815a9..6f3d4abfe 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -213,7 +213,7 @@ def test_checkout_ref(self): head = self.repo.head head = self.repo[head.target] self.assertTrue('new' not in head.tree) - self.repo.checkout(ref_i18n, pygit2.GIT_CHECKOUT_FORCE) + self.repo.checkout(ref_i18n, strategy=pygit2.GIT_CHECKOUT_FORCE) head = self.repo.head head = self.repo[head.target] @@ -243,7 +243,7 @@ def test_checkout_head(self): self.assertTrue('bye.txt' in self.repo.status()) # checkout from head will reset index as well - self.repo.checkout('HEAD', pygit2.GIT_CHECKOUT_FORCE) + self.repo.checkout('HEAD', strategy=pygit2.GIT_CHECKOUT_FORCE) self.assertTrue('bye.txt' not in self.repo.status()) def test_merge_base(self): From 763b571c84b6ef73192aca191ee22f2785f2b2e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Fri, 11 Jul 2014 14:03:17 +0200 Subject: [PATCH 2/2] checkout: add support for overriding the target directory --- pygit2/repository.py | 25 ++++++++++++++++++------- test/test_repository.py | 8 ++++++++ 2 files changed, 26 insertions(+), 7 deletions(-) diff --git a/pygit2/repository.py b/pygit2/repository.py index bffc2d8b2..70acbe7f6 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -185,21 +185,30 @@ def _checkout_args_to_options(**kwargs): copts = ffi.new('git_checkout_options *') check_error(C.git_checkout_init_options(copts, 1)) + # References we need to keep to strings and so forth + refs = [] + # pygit2's default is SAFE_CREATE copts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE # and go through the arguments to see what the user wanted - for k, v in kwargs.iteritems(): - if k == 'strategy': - copts.checkout_strategy = v + strategy = kwargs.get('strategy') + if strategy: + copts.checkout_strategy = strategy + + directory = kwargs.get('directory') + if directory: + target_dir = ffi.new('char[]', to_str(directory)) + refs.append(target_dir) + copts.target_directory = target_dir - return copts + return copts, refs def checkout_head(self, **kwargs): """Checkout HEAD For arguments, see Repository.checkout(). """ - copts = Repository._checkout_args_to_options(**kwargs) + copts, refs = Repository._checkout_args_to_options(**kwargs) check_error(C.git_checkout_head(self._repo, copts)) def checkout_index(self, **kwargs): @@ -207,7 +216,7 @@ def checkout_index(self, **kwargs): For arguments, see Repository.checkout(). """ - copts = Repository._checkout_args_to_options(**kwargs) + copts, refs = Repository._checkout_args_to_options(**kwargs) check_error(C.git_checkout_index(self._repo, ffi.NULL, copts)) def checkout_tree(self, treeish, **kwargs): @@ -215,7 +224,7 @@ def checkout_tree(self, treeish, **kwargs): For arguments, see Repository.checkout(). """ - copts = Repository._checkout_args_to_options(**kwargs) + copts, refs = Repository._checkout_args_to_options(**kwargs) cptr = ffi.new('git_object **') ffi.buffer(cptr)[:] = treeish._pointer[:] @@ -242,6 +251,8 @@ def checkout(self, refname=None, **kwargs): :param int strategy: A ``GIT_CHECKOUT_`` value. The default is ``GIT_CHECKOUT_SAFE_CREATE``. + :param str directory: Alternative checkout path to workdir. + """ diff --git a/test/test_repository.py b/test/test_repository.py index 6f3d4abfe..cda846c08 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -246,6 +246,14 @@ def test_checkout_head(self): self.repo.checkout('HEAD', strategy=pygit2.GIT_CHECKOUT_FORCE) self.assertTrue('bye.txt' not in self.repo.status()) + def test_checkout_alternative_dir(self): + ref_i18n = self.repo.lookup_reference('refs/heads/i18n') + extra_dir = os.path.join(self.repo.workdir, 'extra-dir') + os.mkdir(extra_dir) + self.assertTrue(len(os.listdir(extra_dir)) == 0) + self.repo.checkout(ref_i18n, directory=extra_dir) + self.assertFalse(len(os.listdir(extra_dir)) == 0) + def test_merge_base(self): commit = self.repo.merge_base( '5ebeeebb320790caf276b9fc8b24546d63316533',