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 .. 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 # Import from pygit2
from _pygit2 import Repository as _Repository from _pygit2 import Repository as _Repository
from _pygit2 import Oid, GIT_OID_HEXSZ, GIT_OID_MINPREFIXLEN from _pygit2 import Oid, GIT_OID_HEXSZ, GIT_OID_MINPREFIXLEN
from _pygit2 import GIT_CHECKOUT_SAFE_CREATE
from _pygit2 import Reference
class Repository(_Repository): class Repository(_Repository):
@ -88,3 +90,41 @@ class Repository(_Repository):
return self.create_reference_direct(name, target, force) return self.create_reference_direct(name, target, force)
return self.create_reference_symbolic(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 IndexType;
extern PyTypeObject WalkerType; extern PyTypeObject WalkerType;
extern PyTypeObject SignatureType; extern PyTypeObject SignatureType;
extern PyTypeObject ObjectType;
extern PyTypeObject TreeType; extern PyTypeObject TreeType;
extern PyTypeObject TreeBuilderType; extern PyTypeObject TreeBuilderType;
extern PyTypeObject ConfigType; extern PyTypeObject ConfigType;
@ -183,6 +184,25 @@ Repository_head__get__(Repository *self)
return wrap_reference(head); 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__, PyDoc_STRVAR(Repository_head_is_detached__doc__,
"A repository's HEAD is detached when it points directly to a commit\n" "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__, PyDoc_STRVAR(Repository_checkout_head__doc__,
"checkout([strategy:int, reference:Reference])\n" "checkout_head(strategy)\n"
"\n" "\n"
"Checks out a tree by a given reference and modifies the HEAD pointer\n" "Checkout the head using the given strategy.");
"Standard checkout strategy is pygit2.GIT_CHECKOUT_SAFE_CREATE\n"
"If no reference is given, checkout will use HEAD instead.");
PyObject * PyObject *
Repository_checkout(Repository *self, PyObject *args, PyObject *kw) Repository_checkout_head(Repository *self, PyObject *args)
{ {
git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT; git_checkout_opts opts = GIT_CHECKOUT_OPTS_INIT;
unsigned int strategy = GIT_CHECKOUT_SAFE_CREATE; unsigned int strategy;
Reference* ref = NULL; int err;
git_object* object;
const git_oid* id;
int err, head = 0;
static char *kwlist[] = {"strategy", "reference", "head", NULL}; if (!PyArg_ParseTuple(args, "I", &strategy))
if (!PyArg_ParseTupleAndKeywords(args, kw, "|IO!i", kwlist,
&strategy, &ReferenceType, &ref, &head))
return NULL; return NULL;
if (ref != NULL) { /* checkout from treeish */ opts.checkout_strategy = strategy;
id = git_reference_target(ref->reference); err = git_checkout_head(self->repo, &opts);
err = git_object_lookup(&object, self->repo, id, GIT_OBJ_COMMIT); if (err < 0)
if (err == GIT_OK) { return Error_set(err);
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);
}
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) if (err < 0)
return Error_set(err); return Error_set(err);
@ -1152,7 +1197,7 @@ Repository_checkout(Repository *self, PyObject *args, PyObject *kw)
PyDoc_STRVAR(Repository_notes__doc__, ""); PyDoc_STRVAR(Repository_notes__doc__, "");
PyObject * PyObject *
Repository_notes(Repository *self, PyObject* args) Repository_notes(Repository *self, PyObject *args)
{ {
NoteIter *iter = NULL; NoteIter *iter = NULL;
char *ref = "refs/notes/commits"; char *ref = "refs/notes/commits";
@ -1255,7 +1300,9 @@ PyMethodDef Repository_methods[] = {
METHOD(Repository, status, METH_NOARGS), METHOD(Repository, status, METH_NOARGS),
METHOD(Repository, status_file, METH_O), METHOD(Repository, status_file, METH_O),
METHOD(Repository, create_remote, METH_VARARGS), 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, notes, METH_VARARGS),
METHOD(Repository, create_note, METH_VARARGS), METHOD(Repository, create_note, METH_VARARGS),
METHOD(Repository, lookup_note, METH_VARARGS), METHOD(Repository, lookup_note, METH_VARARGS),
@ -1266,7 +1313,7 @@ PyMethodDef Repository_methods[] = {
PyGetSetDef Repository_getseters[] = { PyGetSetDef Repository_getseters[] = {
GETTER(Repository, index), GETTER(Repository, index),
GETTER(Repository, path), GETTER(Repository, path),
GETTER(Repository, head), GETSET(Repository, head),
GETTER(Repository, head_is_detached), GETTER(Repository, head_is_detached),
GETTER(Repository, head_is_orphaned), GETTER(Repository, head_is_orphaned),
GETTER(Repository, is_empty), 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 /* py_str_to_c_str() returns a newly allocated C string holding
* the string contained in the value argument. */ * 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; char *c_str = NULL;
/* Case 1: byte string */ /* Case 1: byte string */
@ -43,7 +44,6 @@ char * py_str_to_c_str(PyObject *value, const char *encoding)
/* Case 2: text string */ /* Case 2: text string */
if (PyUnicode_Check(value)) { if (PyUnicode_Check(value)) {
if (encoding == NULL) if (encoding == NULL)
value = PyUnicode_AsUTF8String(value); value = PyUnicode_AsUTF8String(value);
else else
@ -60,6 +60,3 @@ char * py_str_to_c_str(PyObject *value, const char *encoding)
Py_TYPE(value)->tp_name); Py_TYPE(value)->tp_name);
return NULL; return NULL;
} }

View File

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