Skip to content

Commit

Permalink
submodule: reimplement with Python CFFI.
Browse files Browse the repository at this point in the history
The submodule type has been implemented as a C type. When opening
a submodule's repository this leads to the bug that instead of an
actual pygit2.Repository being instantiated we only create an
object of the C Repository type.

As this is not trivially fixed within the C code, reimplement the
submodule type as a Python interface with CFFI. As submodules
provide no functionality that is usually accessed repeatedly the
code paths should not prove performance critical. In addition,
maintainability is improved by this reimplementation.
  • Loading branch information
pks-t committed Apr 16, 2015
1 parent cd7e2b2 commit f923e20
Show file tree
Hide file tree
Showing 9 changed files with 104 additions and 271 deletions.
4 changes: 3 additions & 1 deletion docs/submodule.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ dedicated subdirectory of the repositories tree.
The Submodule type
====================

.. automethod:: pygit2.Submodule.open

.. autoattribute:: pygit2.Submodule.name
.. autoattribute:: pygit2.Submodule.path
.. autoattribute:: pygit2.Submodule.url
.. automethod:: pygit2.Submodule.open
.. autoattribute:: pygit2.Submodule.branch
13 changes: 13 additions & 0 deletions pygit2/decl.h
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
typedef ... git_repository;
typedef ... git_submodule;
typedef ... git_remote;
typedef ... git_refspec;
typedef ... git_cred;
Expand Down Expand Up @@ -537,6 +538,18 @@ int git_repository_set_head(git_repository *repo, const char *refname, const git
int git_repository_set_head_detached(git_repository *repo, const git_oid *commitish, const git_signature *signature, const char *log_message);
int git_graph_ahead_behind(size_t *ahead, size_t *behind, git_repository *repo, const git_oid *local, const git_oid *upstream);

/*
* git_submodule
*/

int git_submodule_lookup(git_submodule **out, git_repository *repo, char *path);
void git_submodule_free(git_submodule *subm);
int git_submodule_open(git_repository **out, git_submodule *subm);
const char *git_submodule_name(git_submodule *subm);
const char *git_submodule_path(git_submodule *subm);
const char *git_submodule_url(git_submodule *subm);
const char *git_submodule_branch(git_submodule *subm);

/*
* git_index
*/
Expand Down
9 changes: 9 additions & 0 deletions pygit2/repository.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
from .remote import RemoteCollection
from .blame import Blame
from .utils import to_bytes, is_string
from .submodule import Submodule


class Repository(_Repository):
Expand All @@ -77,6 +78,14 @@ def _common_init(self):
ffi.buffer(repo_cptr)[:] = self._pointer[:]
self._repo = repo_cptr[0]

def lookup_submodule(self, path):
csub = ffi.new('git_submodule **')
cpath = ffi.new('char[]', to_bytes(path))

err = C.git_submodule_lookup(csub, self._repo, cpath)
check_error(err)
return Submodule._from_c(self, csub[0])

#
# Mapping interface
#
Expand Down
79 changes: 79 additions & 0 deletions pygit2/submodule.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
# as published by the Free Software Foundation.
#
# In addition to the permissions in the GNU General Public License,
# the authors give you unlimited permission to link the compiled
# version of this file into combinations with other programs,
# and to distribute those combinations without any restriction
# coming from the use of this file. (The General Public License
# restrictions do apply in other respects; for example, they cover
# modification of the file, and distribution when not linked into
# a combined executable.)
#
# This file is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.


# Import from the future
from __future__ import absolute_import, unicode_literals

from .errors import check_error
from .ffi import ffi, C

class Submodule(object):

@classmethod
def _from_c(cls, repo, cptr):
subm = cls.__new__(cls)

subm._repo = repo
subm._subm = cptr

return subm

def __del__(self):
C.git_submodule_free(self._subm)

def open(self):
"""Open the repository for a submodule."""
crepo = ffi.new('git_repository **')
err = C.git_submodule_open(crepo, self._subm)
check_error(err)

return self._repo._from_c(crepo[0], True)

@property
def name(self):
"""Name of the submodule."""
name = C.git_submodule_name(self._subm)
return ffi.string(name).decode('utf-8')

@property
def path(self):
"""Path of the submodule."""
path = C.git_submodule_path(self._subm)
return ffi.string(path).decode('utf-8')

@property
def url(self):
"""URL of the submodule."""
url = C.git_submodule_url(self._subm)
return ffi.string(url).decode('utf-8')

@property
def branch(self):
"""Branch that is to be tracked by the submodule."""
branch = C.git_submodule_branch(self._subm)
return ffi.string(branch).decode('utf-8')
7 changes: 0 additions & 7 deletions src/pygit2.c
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ extern PyTypeObject RepositoryType;
extern PyTypeObject OidType;
extern PyTypeObject ObjectType;
extern PyTypeObject CommitType;
extern PyTypeObject SubmoduleType;
extern PyTypeObject DiffType;
extern PyTypeObject DiffIterType;
extern PyTypeObject DiffDeltaType;
Expand Down Expand Up @@ -220,12 +219,6 @@ moduleinit(PyObject* m)
ADD_CONSTANT_INT(m, GIT_FILEMODE_LINK)
ADD_CONSTANT_INT(m, GIT_FILEMODE_COMMIT)

/*
* Submodules
*/
INIT_TYPE(SubmoduleType, NULL, NULL);
ADD_TYPE(m, Submodule);

/*
* Log
*/
Expand Down
29 changes: 0 additions & 29 deletions src/repository.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,6 @@
#include "repository.h"
#include "branch.h"
#include "signature.h"
#include "submodule.h"
#include <git2/odb_backend.h>

extern PyObject *GitError;
Expand Down Expand Up @@ -1100,33 +1099,6 @@ Repository_listall_branches(Repository *self, PyObject *args)
return NULL;
}

PyDoc_STRVAR(Repository_lookup_submodule__doc__,
"lookup_submodule(path) -> Submodule\n"
"\n"
"Lookup a submodule by its path in a repository.");

PyObject *
Repository_lookup_submodule(Repository *self, PyObject *py_path)
{
git_submodule *c_submodule;
char *c_name;
int err;

c_name = py_path_to_c_str(py_path);
if (c_name == NULL)
return NULL;

err = git_submodule_lookup(&c_submodule, self->repo, c_name);
if (err < 0) {
PyObject *err_obj = Error_set_str(err, c_name);
free(c_name);
return err_obj;
}
free(c_name);

return wrap_submodule(self, c_submodule);
}

PyDoc_STRVAR(Repository_listall_submodules__doc__,
"listall_submodules() -> [str, ...]\n"
"\n"
Expand Down Expand Up @@ -1583,7 +1555,6 @@ PyMethodDef Repository_methods[] = {
METHOD(Repository, create_reference_direct, METH_VARARGS),
METHOD(Repository, create_reference_symbolic, METH_VARARGS),
METHOD(Repository, listall_references, METH_NOARGS),
METHOD(Repository, lookup_submodule, METH_O),
METHOD(Repository, listall_submodules, METH_NOARGS),
METHOD(Repository, lookup_reference, METH_O),
METHOD(Repository, revparse_single, METH_O),
Expand Down
189 changes: 0 additions & 189 deletions src/submodule.c

This file was deleted.

Loading

0 comments on commit f923e20

Please sign in to comment.