Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move checkout to CFFI and add a target directory option #390

Merged
merged 2 commits into from
Jul 11, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions pygit2/decl.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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
*/
Expand Down
72 changes: 67 additions & 5 deletions pygit2/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,11 +176,61 @@ 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))

# 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
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, refs

def checkout_head(self, **kwargs):
"""Checkout HEAD

For arguments, see Repository.checkout().
"""
copts, refs = 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, refs = 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, refs = 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.
Expand All @@ -193,14 +243,26 @@ 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``.

:param str directory: Alternative checkout path to workdir.

"""


# 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:
Expand All @@ -211,7 +273,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


Expand Down
76 changes: 0 additions & 76 deletions src/repository.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 *
Expand Down Expand Up @@ -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),
Expand Down
12 changes: 10 additions & 2 deletions test/test_repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -243,9 +243,17 @@ 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_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',
Expand Down