Re-work the checkout API

New API:

  Repository.head = refname
  Repository.checkout_head(strategy)
  Repository.checkout_index(strategy)
  Repository.checkout_tree(treeish, strategy)

Changed API:

  # Before
  Repository.checkout(strategy=GIT_CHECKOUT_SAFE_CREATE, reference=None,
                      head=False)

  # Now
  Repository.checkout(refname=None, strategy=GIT_CHECKOUT_SAFE_CREATE)
This commit is contained in:
J. David Ibáñez 2013-05-05 21:34:07 +02:00
parent 9c0a82876b
commit 974f16ca69
5 changed files with 142 additions and 50 deletions

View File

@ -64,3 +64,9 @@ Checkout
====================
.. automethod:: pygit2.Repository.checkout
Lower level API:
.. automethod:: pygit2.Repository.checkout_head
.. automethod:: pygit2.Repository.checkout_tree
.. automethod:: pygit2.Repository.checkout_index

View File

@ -31,6 +31,8 @@ from string import hexdigits
# Import from pygit2
from _pygit2 import Repository as _Repository
from _pygit2 import Oid, GIT_OID_HEXSZ, GIT_OID_MINPREFIXLEN
from _pygit2 import GIT_CHECKOUT_SAFE_CREATE
from _pygit2 import Reference
class Repository(_Repository):
@ -88,3 +90,41 @@ class Repository(_Repository):
return self.create_reference_direct(name, target, force)
return self.create_reference_symbolic(name, target, force)
#
# Checkout
#
def checkout(self, refname=None, strategy=GIT_CHECKOUT_SAFE_CREATE):
"""
Checkout the given reference using the given strategy, and update
the HEAD.
The reference may be a reference name or a Reference object.
The default strategy is GIT_CHECKOUT_SAFE_CREATE.
To checkout from the HEAD, just pass 'HEAD'::
>>> checkout('HEAD')
If no reference is given, checkout from the index.
"""
# Case 1: Checkout index
if refname is None:
return self.checkout_index(strategy)
# Case 2: Checkout head
if refname == 'HEAD':
return self.checkout_head(strategy)
# Case 3: Reference
if type(refname) is Reference:
reference = refname
refname = refname.name
else:
reference = self.lookup_reference(refname)
oid = reference.resolve().target
treeish = self[oid]
self.checkout_tree(treeish, strategy)
self.head = refname

View File

@ -42,6 +42,7 @@ extern PyObject *GitError;
extern PyTypeObject IndexType;
extern PyTypeObject WalkerType;
extern PyTypeObject SignatureType;
extern PyTypeObject ObjectType;
extern PyTypeObject TreeType;
extern PyTypeObject TreeBuilderType;
extern PyTypeObject ConfigType;
@ -183,6 +184,25 @@ Repository_head__get__(Repository *self)
return wrap_reference(head);
}
int
Repository_head__set__(Repository *self, PyObject *py_refname)
{
int err;
const char *refname;
refname = py_str_to_c_str(py_refname, NULL);
if (refname == NULL)
return -1;
err = git_repository_set_head(self->repo, refname);
if (err < 0) {
Error_set_str(err, refname);
return -1;
}
return 0;
}
PyDoc_STRVAR(Repository_head_is_detached__doc__,
"A repository's HEAD is detached when it points directly to a commit\n"
@ -1101,47 +1121,72 @@ Repository_remotes__get__(Repository *self)
}
PyDoc_STRVAR(Repository_checkout__doc__,
"checkout([strategy:int, reference:Reference])\n"
"\n"
"Checks out a tree by a given reference and modifies the HEAD pointer\n"
"Standard checkout strategy is pygit2.GIT_CHECKOUT_SAFE_CREATE\n"
"If no reference is given, checkout will use HEAD instead.");
PyDoc_STRVAR(Repository_checkout_head__doc__,
"checkout_head(strategy)\n"
"\n"
"Checkout the head using the given strategy.");
PyObject *
Repository_checkout(Repository *self, PyObject *args, PyObject *kw)
Repository_checkout_head(Repository *self, PyObject *args)
{
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
unsigned int strategy = GIT_CHECKOUT_SAFE_CREATE;
Reference* ref = NULL;
git_object* object;
const git_oid* id;
int err, head = 0;
unsigned int strategy;
int err;
static char *kwlist[] = {"strategy", "reference", "head", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kw, "|IO!i", kwlist,
&strategy, &ReferenceType, &ref, &head))
if (!PyArg_ParseTuple(args, "I", &strategy))
return NULL;
if (ref != NULL) { /* checkout from treeish */
id = git_reference_target(ref->reference);
err = git_object_lookup(&object, self->repo, id, GIT_OBJ_COMMIT);
if (err == GIT_OK) {
opts.checkout_strategy = strategy;
err = git_checkout_tree(self->repo, object, &opts);
if (err == GIT_OK) {
err = git_repository_set_head(self->repo,
git_reference_name(ref->reference));
}
git_object_free(object);
}
} else { /* checkout from head / index */
opts.checkout_strategy = strategy;
err = (!head) ? git_checkout_index(self->repo, NULL, &opts) :
git_checkout_head(self->repo, &opts);
}
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_opts opts = GIT_CHECKOUT_OPTS_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_opts opts = GIT_CHECKOUT_OPTS_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);
@ -1152,7 +1197,7 @@ Repository_checkout(Repository *self, PyObject *args, PyObject *kw)
PyDoc_STRVAR(Repository_notes__doc__, "");
PyObject *
Repository_notes(Repository *self, PyObject* args)
Repository_notes(Repository *self, PyObject *args)
{
NoteIter *iter = NULL;
char *ref = "refs/notes/commits";
@ -1255,7 +1300,9 @@ PyMethodDef Repository_methods[] = {
METHOD(Repository, status, METH_NOARGS),
METHOD(Repository, status_file, METH_O),
METHOD(Repository, create_remote, METH_VARARGS),
METHOD(Repository, checkout, METH_VARARGS|METH_KEYWORDS),
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),
@ -1266,7 +1313,7 @@ PyMethodDef Repository_methods[] = {
PyGetSetDef Repository_getseters[] = {
GETTER(Repository, index),
GETTER(Repository, path),
GETTER(Repository, head),
GETSET(Repository, head),
GETTER(Repository, head_is_detached),
GETTER(Repository, head_is_orphaned),
GETTER(Repository, is_empty),

View File

@ -34,7 +34,8 @@ extern PyTypeObject ReferenceType;
/* py_str_to_c_str() returns a newly allocated C string holding
* the string contained in the value argument. */
char * py_str_to_c_str(PyObject *value, const char *encoding)
char *
py_str_to_c_str(PyObject *value, const char *encoding)
{
char *c_str = NULL;
/* Case 1: byte string */
@ -43,7 +44,6 @@ char * py_str_to_c_str(PyObject *value, const char *encoding)
/* Case 2: text string */
if (PyUnicode_Check(value)) {
if (encoding == NULL)
value = PyUnicode_AsUTF8String(value);
else
@ -60,6 +60,3 @@ char * py_str_to_c_str(PyObject *value, const char *encoding)
Py_TYPE(value)->tp_name);
return NULL;
}

View File

@ -191,18 +191,17 @@ class RepositoryTest_II(utils.RepoTestCase):
# checkout i18n with conflicts and default strategy should
# not be possible
self.assertRaises(pygit2.GitError,
lambda: self.repo.checkout(reference=ref_i18n))
self.assertRaises(pygit2.GitError, self.repo.checkout, ref_i18n)
# checkout i18n with GIT_CHECKOUT_FORCE
head = self.repo.head
head = self.repo[head.target]
self.assertTrue('new' not in head.tree)
self.repo.checkout(pygit2.GIT_CHECKOUT_FORCE, ref_i18n)
self.repo.checkout(ref_i18n, pygit2.GIT_CHECKOUT_FORCE)
head = self.repo.head
head = self.repo[head.target]
self.assertEqual(head.hex, self.repo[ref_i18n.target].hex)
self.assertEqual(head.hex, ref_i18n.target.hex)
self.assertTrue('new' in head.tree)
self.assertTrue('bye.txt' not in self.repo.status())
@ -213,7 +212,7 @@ class RepositoryTest_II(utils.RepoTestCase):
# checkout index
self.assertTrue('hello.txt' in self.repo.status())
self.repo.checkout(pygit2.GIT_CHECKOUT_FORCE)
self.repo.checkout(strategy=pygit2.GIT_CHECKOUT_FORCE)
self.assertTrue('hello.txt' not in self.repo.status())
def test_checkout_head(self):
@ -224,16 +223,19 @@ class RepositoryTest_II(utils.RepoTestCase):
# checkout from index should not change anything
self.assertTrue('bye.txt' in self.repo.status())
self.repo.checkout(pygit2.GIT_CHECKOUT_FORCE)
self.repo.checkout(strategy=pygit2.GIT_CHECKOUT_FORCE)
self.assertTrue('bye.txt' in self.repo.status())
# checkout from head will reset index as well
self.repo.checkout(pygit2.GIT_CHECKOUT_FORCE, head=True)
self.repo.checkout('HEAD', pygit2.GIT_CHECKOUT_FORCE)
self.assertTrue('bye.txt' not in self.repo.status())
def test_merge_base(self):
commit = self.repo.merge_base('5ebeeebb320790caf276b9fc8b24546d63316533', '4ec4389a8068641da2d6578db0419484972284c8')
self.assertEqual(commit.hex, 'acecd5ea2924a4b900e7e149496e1f4b57976e51')
commit = self.repo.merge_base(
'5ebeeebb320790caf276b9fc8b24546d63316533',
'4ec4389a8068641da2d6578db0419484972284c8')
self.assertEqual(commit.hex,
'acecd5ea2924a4b900e7e149496e1f4b57976e51')
class NewRepositoryTest(utils.NoRepoTestCase):