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:
parent
9c0a82876b
commit
974f16ca69
@ -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
|
||||
|
@ -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
|
||||
|
119
src/repository.c
119
src/repository.c
@ -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),
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
@ -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):
|
||||
|
Loading…
Reference in New Issue
Block a user