From 9cce003efe27a2e3a5dd06a6bd1f1a26fe803dc9 Mon Sep 17 00:00:00 2001 From: Masud Rahman Date: Tue, 10 Feb 2015 01:28:23 -0500 Subject: [PATCH 01/36] Implements 'Repository.create_blob_fromiobase'. This commit allows blobs to be constructed from implementatons of the 'RawIOBase' ABC. This allows any conformant stream implementation to be added as a blob. This is useful in the case where the contents of the blob would be too large to create from memory (as 'create_blob_fromstring' allows) and avoids having to write the data to disk (as 'create_blob_fromworkdir' allows). The latter operation is especially useful when reading data from a network socket, since it avoids having to first commit all the data to disk. --- docs/objects.rst | 1 + src/repository.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++ test/test_blob.py | 12 ++++++++++ 3 files changed, 74 insertions(+) diff --git a/docs/objects.rst b/docs/objects.rst index b9d038b..1609d31 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -132,6 +132,7 @@ them to the Git object database: .. automethod:: pygit2.Repository.create_blob_fromworkdir .. automethod:: pygit2.Repository.create_blob_fromdisk +.. automethod:: pygit2.Repository.create_blob_fromiobase There are also some functions to calculate the id for a byte string without creating the blob object: diff --git a/src/repository.c b/src/repository.c index 31075c2..31523c2 100644 --- a/src/repository.c +++ b/src/repository.c @@ -736,6 +736,66 @@ Repository_create_blob_fromdisk(Repository *self, PyObject *args) } +PyDoc_STRVAR(Repository_create_blob_fromiobase__doc__, + "create_blob_fromiobase(io.IOBase) -> Oid\n" + "\n" + "Create a new blob from an IOBase object."); + + +int read_chunk(char *content, size_t max_length, void *payload) +{ + PyObject *py_file; + PyObject *py_bytes; + char *bytes; + Py_ssize_t size; + + py_file = (PyObject *)payload; + py_bytes = PyObject_CallMethod(py_file, "read", "i", max_length); + if (!py_bytes) { + return 0; + } + + bytes = PyBytes_AsString(py_bytes); + size = PyBytes_Size(py_bytes); + memcpy(content, bytes, size); + + Py_DECREF(py_bytes); + return size; +} + +PyObject * +Repository_create_blob_fromiobase(Repository *self, PyObject *args) +{ + git_oid oid; + PyObject *py_is_readable; + PyObject *py_file; + int err; + + if (!PyArg_ParseTuple(args, "O", &py_file)) + return NULL; + + py_is_readable = PyObject_CallMethod(py_file, "readable", NULL); + if (!py_is_readable || !PyObject_IsTrue(py_is_readable)) { + PyErr_SetString(PyExc_TypeError, "expected readable IO type"); + Py_DECREF(py_is_readable); + return NULL; + } + Py_DECREF(py_is_readable); + + err = git_blob_create_fromchunks(&oid, + self->repo, + NULL, + &read_chunk, + py_file); + Py_DECREF(py_file); + + if (err < 0) + return Error_set(err); + + return git_oid_to_python(&oid); +} + + PyDoc_STRVAR(Repository_create_commit__doc__, "create_commit(reference, author, committer, message, tree, parents[, encoding]) -> Oid\n" "\n" @@ -1386,6 +1446,7 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, create_blob, METH_VARARGS), METHOD(Repository, create_blob_fromworkdir, METH_VARARGS), METHOD(Repository, create_blob_fromdisk, METH_VARARGS), + METHOD(Repository, create_blob_fromiobase, METH_VARARGS), METHOD(Repository, create_commit, METH_VARARGS), METHOD(Repository, create_tag, METH_VARARGS), METHOD(Repository, TreeBuilder, METH_VARARGS), diff --git a/test/test_blob.py b/test/test_blob.py index 9fd1d4f..f66d1e1 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -30,6 +30,7 @@ from __future__ import absolute_import from __future__ import unicode_literals from os.path import dirname, join +import io import unittest import pygit2 @@ -112,6 +113,17 @@ class BlobTest(utils.RepoTestCase): self.assertTrue(isinstance(blob, pygit2.Blob)) self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type) + def test_create_blob_fromiobase(self): + f = io.BytesIO(BLOB_CONTENT) + blob_oid = self.repo.create_blob_fromiobase(f) + blob = self.repo[blob_oid] + + self.assertTrue(isinstance(blob, pygit2.Blob)) + self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type) + + self.assertEqual(blob_oid, blob.id) + self.assertEqual(BLOB_SHA, blob_oid.hex) + def test_diff_blob(self): blob = self.repo[BLOB_SHA] old_blob = self.repo['3b18e512dba79e4c8300dd08aeb37f8e728b8dad'] From 38b1975991fe5002f00f1df285deed2e5c1f6f77 Mon Sep 17 00:00:00 2001 From: Masud Rahman Date: Sun, 8 Mar 2015 00:09:20 -0500 Subject: [PATCH 02/36] blob_fromiobase: addresses review comments. --- src/repository.c | 32 ++++++++++++++++++++++---------- 1 file changed, 22 insertions(+), 10 deletions(-) diff --git a/src/repository.c b/src/repository.c index 31523c2..7ae5f9d 100644 --- a/src/repository.c +++ b/src/repository.c @@ -39,6 +39,8 @@ #include "signature.h" #include +extern PyTypeObject PyIOBase_Type; + extern PyObject *GitError; extern PyTypeObject IndexType; @@ -751,13 +753,15 @@ int read_chunk(char *content, size_t max_length, void *payload) py_file = (PyObject *)payload; py_bytes = PyObject_CallMethod(py_file, "read", "i", max_length); - if (!py_bytes) { - return 0; - } + if (!py_bytes) + return -1; - bytes = PyBytes_AsString(py_bytes); - size = PyBytes_Size(py_bytes); - memcpy(content, bytes, size); + size = 0; + if (py_bytes != Py_None) { + bytes = PyBytes_AsString(py_bytes); + size = PyBytes_Size(py_bytes); + memcpy(content, bytes, size); + } Py_DECREF(py_bytes); return size; @@ -768,20 +772,28 @@ Repository_create_blob_fromiobase(Repository *self, PyObject *args) { git_oid oid; PyObject *py_is_readable; + int is_readable; PyObject *py_file; int err; - if (!PyArg_ParseTuple(args, "O", &py_file)) + if (!PyArg_ParseTuple(args, "O!", &PyIOBase_Type, &py_file)) return NULL; py_is_readable = PyObject_CallMethod(py_file, "readable", NULL); - if (!py_is_readable || !PyObject_IsTrue(py_is_readable)) { - PyErr_SetString(PyExc_TypeError, "expected readable IO type"); - Py_DECREF(py_is_readable); + if (!py_is_readable) { + Py_DECREF(py_file); return NULL; } + + is_readable = PyObject_IsTrue(py_is_readable); Py_DECREF(py_is_readable); + if (!is_readable) { + Py_DECREF(py_file); + PyErr_SetString(PyExc_TypeError, "expected readable IO type"); + return NULL; + } + err = git_blob_create_fromchunks(&oid, self->repo, NULL, From 25d02259df7b73ec220b2a0095ad4a2d7d525f96 Mon Sep 17 00:00:00 2001 From: David Six Date: Wed, 26 Aug 2015 11:15:47 -0400 Subject: [PATCH 03/36] Fix: pass push_opts to git_remote_push --- pygit2/remote.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pygit2/remote.py b/pygit2/remote.py index 7ec5bd4..def4497 100644 --- a/pygit2/remote.py +++ b/pygit2/remote.py @@ -246,7 +246,7 @@ class Remote(object): try: with StrArray(specs) as refspecs: - err = C.git_remote_push(self._remote, refspecs, ffi.NULL) + err = C.git_remote_push(self._remote, refspecs, push_opts) check_error(err) finally: self._self_handle = None From becc265c787a7e039df67af4a8c62ae5230a0422 Mon Sep 17 00:00:00 2001 From: Michael Sondergaard Date: Fri, 22 May 2015 02:23:37 +0200 Subject: [PATCH 04/36] Don't use the deprecated cffi.verify by default Instead this does what is recommend in the CFFI docs here: https://cffi.readthedocs.org/en/latest/cdef.html?highlight=verify#out-of-line-api This also means building the cffi extension is neatly handled by cffi's setuptools integration itself, so we can delete the code in setup.py that used to do this. --- pygit2/__init__.py | 2 +- pygit2/ffi.py | 9 +-- pygit2/{_utils.py => libgit2_build.py} | 59 ++++++++---------- setup.py | 85 ++++++++++++-------------- 4 files changed, 71 insertions(+), 84 deletions(-) rename pygit2/{_utils.py => libgit2_build.py} (63%) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 709adca..3e437d7 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -43,7 +43,7 @@ from .repository import Repository from .settings import Settings from .submodule import Submodule from .utils import to_bytes, to_str -from ._utils import __version__ +from .libgit2_build import __version__ # Features diff --git a/pygit2/ffi.py b/pygit2/ffi.py index ae9e12c..47542a1 100644 --- a/pygit2/ffi.py +++ b/pygit2/ffi.py @@ -29,7 +29,8 @@ from __future__ import absolute_import # Import from pygit2 -from ._utils import get_ffi - - -ffi, C = get_ffi() +try: + from ._libgit2 import ffi, lib as C +except ImportError: + from .libgit2_build import ffi, C_HEADER_SRC, C_KEYWORDS + C = ffi.verify(C_HEADER_SRC, **C_KEYWORDS) diff --git a/pygit2/_utils.py b/pygit2/libgit2_build.py similarity index 63% rename from pygit2/_utils.py rename to pygit2/libgit2_build.py index f7b0714..0b2b50d 100644 --- a/pygit2/_utils.py +++ b/pygit2/libgit2_build.py @@ -69,42 +69,37 @@ def get_libgit2_paths(): ) -# -# Loads the cffi extension -# -def get_ffi(): - import cffi +import cffi - ffi = cffi.FFI() +ffi = cffi.FFI() - # Load C definitions - if getattr(sys, 'frozen', False): - if hasattr(sys, '_MEIPASS'): - dir_path = sys._MEIPASS - else: - dir_path = dirname(abspath(sys.executable)) +# Load C definitions +if getattr(sys, 'frozen', False): + if hasattr(sys, '_MEIPASS'): + dir_path = sys._MEIPASS else: - dir_path = dirname(abspath(__file__)) + dir_path = dirname(abspath(sys.executable)) +else: + dir_path = dirname(abspath(__file__)) - decl_path = os.path.join(dir_path, 'decl.h') - with codecs.open(decl_path, 'r', 'utf-8') as header: - ffi.cdef(header.read()) +decl_path = os.path.join(dir_path, 'decl.h') +with codecs.open(decl_path, 'r', 'utf-8') as header: + C_HEADER_SRC = header.read() - # The modulename - # Simplified version of what cffi does: remove kwargs and vengine - preamble = "#include " - key = [sys.version[:3], cffi.__version__, preamble] + ffi._cdefsources - key = '\x00'.join(key) - if sys.version_info >= (3,): - key = key.encode('utf-8') - k1 = hex(crc32(key[0::2]) & 0xffffffff).lstrip('0x').rstrip('L') - k2 = hex(crc32(key[1::2]) & 0xffffffff).lstrip('0').rstrip('L') - modulename = 'pygit2_cffi_%s%s' % (k1, k2) +libgit2_bin, libgit2_include, libgit2_lib = get_libgit2_paths() - # Load extension module - libgit2_bin, libgit2_include, libgit2_lib = get_libgit2_paths() - C = ffi.verify(preamble, modulename=modulename, libraries=["git2"], - include_dirs=[libgit2_include], library_dirs=[libgit2_lib]) +C_KEYWORDS = dict(libraries=['git2'], + library_dirs=[libgit2_lib], + include_dirs=[libgit2_include]) - # Ok - return ffi, C +# The modulename +# Simplified version of what cffi does: remove kwargs and vengine +preamble = "#include " + +if hasattr(ffi, 'set_source'): + ffi.set_source("pygit2._libgit2", preamble, **C_KEYWORDS) + +ffi.cdef(C_HEADER_SRC) + +if __name__ == '__main__': + ffi.compile() diff --git a/setup.py b/setup.py index 92b5280..dd61796 100644 --- a/setup.py +++ b/setup.py @@ -47,7 +47,7 @@ import unittest # Import stuff from pygit2/_utils.py without loading the whole pygit2 package sys.path.insert(0, 'pygit2') -from _utils import __version__, get_libgit2_paths, get_ffi +from libgit2_build import __version__, get_libgit2_paths del sys.path[0] # Python 2 support @@ -95,19 +95,42 @@ class TestCommand(Command): unittest.main(None, defaultTest='test.test_suite', argv=test_argv) -class CFFIBuild(build): - """Hack to combat the chicken and egg problem that we need cffi - to add cffi as an extension. - """ - def finalize_options(self): - ffi, C = get_ffi() - self.distribution.ext_modules.append(ffi.verifier.get_extension()) - build.finalize_options(self) +class sdist_files_from_git(sdist): + def get_file_list(self): + popen = Popen(['git', 'ls-files'], stdout=PIPE, stderr=PIPE) + stdoutdata, stderrdata = popen.communicate() + if popen.returncode != 0: + print(stderrdata) + sys.exit() + + for line in stdoutdata.splitlines(): + # Skip hidden files at the root + if line[0] == '.': + continue + self.filelist.append(line) + + # Ok + self.filelist.sort() + self.filelist.remove_duplicates() + self.write_manifest() -class BuildWithDLLs(CFFIBuild): +classifiers = [ + "Development Status :: 3 - Alpha", + "Intended Audience :: Developers", + "Topic :: Software Development :: Version Control"] - # On Windows, we install the git2.dll too. +with codecs.open('README.rst', 'r', 'utf-8') as readme: + long_description = readme.read() + +cmdclass = { + 'test': TestCommand, + 'sdist': sdist_files_from_git, +} + + +# On Windows, we install the git2.dll too. +class BuildWithDLLs(build): def _get_dlls(self): # return a list of (FQ-in-name, relative-out-name) tuples. ret = [] @@ -133,45 +156,12 @@ class BuildWithDLLs(CFFIBuild): def run(self): build.run(self) - # On Windows we package up the dlls with the plugin. for s, d in self._get_dlls(): self.copy_file(s, d) - -class sdist_files_from_git(sdist): - def get_file_list(self): - popen = Popen(['git', 'ls-files'], stdout=PIPE, stderr=PIPE) - stdoutdata, stderrdata = popen.communicate() - if popen.returncode != 0: - print(stderrdata) - sys.exit() - - for line in stdoutdata.splitlines(): - # Skip hidden files at the root - if line[0] == '.': - continue - self.filelist.append(line) - - # Ok - self.filelist.sort() - self.filelist.remove_duplicates() - self.write_manifest() - -classifiers = [ - "Development Status :: 3 - Alpha", - "Intended Audience :: Developers", - "Topic :: Software Development :: Version Control"] - - -with codecs.open('README.rst', 'r', 'utf-8') as readme: - long_description = readme.read() - - -cmdclass = { - 'build': BuildWithDLLs if os.name == 'nt' else CFFIBuild, - 'test': TestCommand, - 'sdist': sdist_files_from_git, - } +# On Windows we package up the dlls with the plugin. +if os.name == 'nt': + cmdclass['build'] = BuildWithDLLs setup(name='pygit2', description='Python bindings for libgit2.', @@ -185,6 +175,7 @@ setup(name='pygit2', long_description=long_description, packages=['pygit2'], package_data={'pygit2': ['decl.h']}, + cffi_modules=['pygit2/libgit2_build.py:ffi'], setup_requires=['cffi'], install_requires=['cffi'], zip_safe=False, From f28a19935113d9f6468b647c540ad4d234d8c309 Mon Sep 17 00:00:00 2001 From: Sheeo Date: Fri, 28 Aug 2015 19:51:39 +0200 Subject: [PATCH 05/36] Install cffi>=1 on travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 07259ce..6e099ea 100644 --- a/.travis.yml +++ b/.travis.yml @@ -12,7 +12,7 @@ env: LIBGIT2=~/libgit2/_install/ LD_LIBRARY_PATH=~/libgit2/_install/lib before_install: - sudo apt-get install cmake - - pip install cffi + - pip install 'cffi>=1.0.0' - "./.travis.sh" script: From 29a8dbc6b2d7a2ed4bdae13ff7b47754f0803d46 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 3 Sep 2015 09:23:28 +0200 Subject: [PATCH 06/36] Temporarily deactivate travis for PyPy and PyPy3 Until travis supports PyPy 2.6, and there is a new release of PyPy3 Update docs regarding cffi and PyPy --- .travis.yml | 4 ++-- README.rst | 16 +++++++++++++--- docs/install.rst | 4 ++-- 3 files changed, 17 insertions(+), 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 6e099ea..bc845d5 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,8 +5,8 @@ python: - "3.2" - "3.3" - "3.4" - - "pypy" - - "pypy3" +# - "pypy" +# - "pypy3" env: LIBGIT2=~/libgit2/_install/ LD_LIBRARY_PATH=~/libgit2/_install/lib diff --git a/README.rst b/README.rst index 85f6875..9f3b798 100644 --- a/README.rst +++ b/README.rst @@ -7,7 +7,7 @@ pygit2 - libgit2 bindings in Python Pygit2 is a set of Python bindings to the libgit2 shared library, libgit2 implements the core of Git. Pygit2 works with Python 2.7, 3.2, 3.3, 3.4 and -pypy. +PyPy 2.6 Links: @@ -25,6 +25,16 @@ How to install Changelog ============== +0.23.1 (2015-XX-XX) +------------------------- + +- Upgrade to cffi 1.0+ + `#529 `_ + +- Fix ``Remote.push`` + `#557 `_ + + 0.23.0 (2015-08-14) ------------------------- @@ -326,7 +336,7 @@ Other changes: `#380 `_ `#407 `_ -- Add support for pypy3 +- Add support for PyPy3 `#422 `_ - Documentation improvements @@ -490,7 +500,7 @@ Other: 0.20.2 (2014-02-04) ------------------- -- Support pypy +- Support PyPy `#209 `_ `#327 `_ `#333 `_ diff --git a/docs/install.rst b/docs/install.rst index 018080d..0a0a5c4 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -13,9 +13,9 @@ Installation Requirements ============ -- Python 2.7, 3.2+ or pypy (including the development headers) +- Python 2.7, 3.2+ or PyPy 2.6+ (including the development headers) - Libgit2 v0.23.x -- cffi 0.8.1+ +- cffi 1.0+ - Libssh2, optional, used for SSH network operations. - pkg-config, optional, used for SSH network operations. From ac7738bbb3299ea127b4013db88bc7e35477823e Mon Sep 17 00:00:00 2001 From: Nicolas Dandrimont Date: Wed, 9 Sep 2015 21:41:00 +0200 Subject: [PATCH 07/36] Add type attribute to TreeEntry This allows complete iteration and rebuilding of a tree without hitting the object store for every entry. --- docs/objects.rst | 15 ++++++++------- src/tree.c | 10 ++++++++++ test/test_tree.py | 16 +++++++++++++++- 3 files changed, 33 insertions(+), 8 deletions(-) diff --git a/docs/objects.rst b/docs/objects.rst index b9d038b..488c6ed 100644 --- a/docs/objects.rst +++ b/docs/objects.rst @@ -176,6 +176,7 @@ Tree entries .. autoattribute:: pygit2.TreeEntry.id .. autoattribute:: pygit2.TreeEntry.hex .. autoattribute:: pygit2.TreeEntry.filemode +.. autoattribute:: pygit2.TreeEntry.type .. method:: TreeEntry.__cmp__(TreeEntry) @@ -188,14 +189,14 @@ Example:: 6 >>> for entry in tree: # Iteration - ... print(entry.id, entry.name) + ... print(entry.id, entry.type, entry.name) ... - 7151ca7cd3e59f3eab19c485cfbf3cb30928d7fa .gitignore - c36f4cf1e38ec1bb9d9ad146ed572b89ecfc9f18 COPYING - 32b30b90b062f66957d6790c3c155c289c34424e README.md - c87dae4094b3a6d10e08bc6c5ef1f55a7e448659 pygit2.c - 85a67270a49ef16cdd3d328f06a3e4b459f09b27 setup.py - 3d8985bbec338eb4d47c5b01b863ee89d044bd53 test + 7151ca7cd3e59f3eab19c485cfbf3cb30928d7fa blob .gitignore + c36f4cf1e38ec1bb9d9ad146ed572b89ecfc9f18 blob COPYING + 32b30b90b062f66957d6790c3c155c289c34424e blob README.md + c87dae4094b3a6d10e08bc6c5ef1f55a7e448659 blob pygit2.c + 85a67270a49ef16cdd3d328f06a3e4b459f09b27 blob setup.py + 3d8985bbec338eb4d47c5b01b863ee89d044bd53 tree test >>> entry = tree['pygit2.c'] # Get an entry by name >>> entry diff --git a/src/tree.c b/src/tree.c index 535b1f3..e5cb9ef 100644 --- a/src/tree.c +++ b/src/tree.c @@ -67,6 +67,15 @@ TreeEntry_name__get__(TreeEntry *self) } +PyDoc_STRVAR(TreeEntry_type__doc__, "Type."); + +PyObject * +TreeEntry_type__get__(TreeEntry *self) +{ + return to_path(git_object_type2string(git_tree_entry_type(self->entry))); +} + + PyDoc_STRVAR(TreeEntry_id__doc__, "Object id."); PyObject * @@ -171,6 +180,7 @@ PyGetSetDef TreeEntry_getseters[] = { GETTER(TreeEntry, oid), GETTER(TreeEntry, id), GETTER(TreeEntry, hex), + GETTER(TreeEntry, type), {NULL} }; diff --git a/test/test_tree.py b/test/test_tree.py index 6c8b707..f092f2e 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -32,7 +32,7 @@ from __future__ import unicode_literals import operator import unittest -from pygit2 import TreeEntry +from pygit2 import TreeEntry, GIT_FILEMODE_TREE from . import utils @@ -89,6 +89,7 @@ class TreeTest(utils.BareRepoTestCase): tree = self.repo[TREE_SHA] subtree_entry = tree['c'] self.assertTreeEntryEqual(subtree_entry, SUBTREE_SHA, 'c', 0o0040000) + self.assertEquals(subtree_entry.type, 'tree') subtree = self.repo[subtree_entry.id] self.assertEqual(1, len(subtree)) @@ -100,21 +101,34 @@ class TreeTest(utils.BareRepoTestCase): repo = self.repo b0 = repo.create_blob('1') b1 = repo.create_blob('2') + st = repo.TreeBuilder() + st.insert('a', b0, 0o0100644) + subtree = repo[st.write()] + t = repo.TreeBuilder() t.insert('x', b0, 0o0100644) t.insert('y', b1, 0o0100755) + t.insert('z', subtree.id, GIT_FILEMODE_TREE) tree = repo[t.write()] self.assertTrue('x' in tree) self.assertTrue('y' in tree) + self.assertTrue('z' in tree) x = tree['x'] y = tree['y'] + z = tree['z'] self.assertEqual(x.filemode, 0o0100644) self.assertEqual(y.filemode, 0o0100755) + self.assertEqual(z.filemode, GIT_FILEMODE_TREE) self.assertEqual(repo[x.id].id, b0) self.assertEqual(repo[y.id].id, b1) + self.assertEqual(repo[z.id].id, subtree.id) + + self.assertEqual(x.type, 'blob') + self.assertEqual(y.type, 'blob') + self.assertEqual(z.type, 'tree') def test_modify_tree(self): From ec23762c09ce23a58f81cac98e65e55be42dc3f5 Mon Sep 17 00:00:00 2001 From: Nicolas Dandrimont Date: Wed, 9 Sep 2015 22:25:20 +0200 Subject: [PATCH 08/36] Add support for cffi-pre-1.0 --- pygit2/ffi.py | 4 ++-- setup.py | 32 ++++++++++++++++++++++++-------- 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/pygit2/ffi.py b/pygit2/ffi.py index 47542a1..95c8e4e 100644 --- a/pygit2/ffi.py +++ b/pygit2/ffi.py @@ -32,5 +32,5 @@ from __future__ import absolute_import try: from ._libgit2 import ffi, lib as C except ImportError: - from .libgit2_build import ffi, C_HEADER_SRC, C_KEYWORDS - C = ffi.verify(C_HEADER_SRC, **C_KEYWORDS) + from .libgit2_build import ffi, preamble, C_KEYWORDS + C = ffi.verify(preamble, **C_KEYWORDS) diff --git a/setup.py b/setup.py index dd61796..8b37b0f 100644 --- a/setup.py +++ b/setup.py @@ -45,9 +45,16 @@ from subprocess import Popen, PIPE import sys import unittest +# Get cffi major version +import cffi +cffi_major_version = cffi.__version_info__[0] + # Import stuff from pygit2/_utils.py without loading the whole pygit2 package sys.path.insert(0, 'pygit2') from libgit2_build import __version__, get_libgit2_paths +if cffi_major_version == 0: + from libgit2_build import ffi, preamble, C_KEYWORDS + ffi.verify(preamble, **C_KEYWORDS) del sys.path[0] # Python 2 support @@ -163,6 +170,21 @@ class BuildWithDLLs(build): if os.name == 'nt': cmdclass['build'] = BuildWithDLLs +extra_args = { + 'ext_modules': [ + Extension('_pygit2', pygit2_exts, libraries=['git2'], + include_dirs=[libgit2_include], + library_dirs=[libgit2_lib]), + # FFI is added in the build step + ], +} + +if cffi_major_version == 0: + extra_args['ext_modules'].append(ffi.verifier.get_extension()) +else: + extra_args['cffi_modules']=['pygit2/libgit2_build.py:ffi'] + + setup(name='pygit2', description='Python bindings for libgit2.', keywords='git', @@ -175,14 +197,8 @@ setup(name='pygit2', long_description=long_description, packages=['pygit2'], package_data={'pygit2': ['decl.h']}, - cffi_modules=['pygit2/libgit2_build.py:ffi'], setup_requires=['cffi'], install_requires=['cffi'], zip_safe=False, - ext_modules=[ - Extension('_pygit2', pygit2_exts, libraries=['git2'], - include_dirs=[libgit2_include], - library_dirs=[libgit2_lib]), - # FFI is added in the build step - ], - cmdclass=cmdclass) + cmdclass=cmdclass, + **extra_args) From 4b607b8256e985f52e508088affe1e04cddb4d58 Mon Sep 17 00:00:00 2001 From: Nicolas Dandrimont Date: Wed, 9 Sep 2015 23:25:15 +0200 Subject: [PATCH 09/36] Re-enable tests on PyPy --- .travis.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.travis.yml b/.travis.yml index bc845d5..07259ce 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,14 +5,14 @@ python: - "3.2" - "3.3" - "3.4" -# - "pypy" -# - "pypy3" + - "pypy" + - "pypy3" env: LIBGIT2=~/libgit2/_install/ LD_LIBRARY_PATH=~/libgit2/_install/lib before_install: - sudo apt-get install cmake - - pip install 'cffi>=1.0.0' + - pip install cffi - "./.travis.sh" script: From ade211de60754edd513ea5239b6fcd4883faaf6d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Thu, 10 Sep 2015 11:30:37 +0200 Subject: [PATCH 10/36] tests: fix warning --- test/test_tree.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/test_tree.py b/test/test_tree.py index f092f2e..7e9bd9a 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -89,7 +89,7 @@ class TreeTest(utils.BareRepoTestCase): tree = self.repo[TREE_SHA] subtree_entry = tree['c'] self.assertTreeEntryEqual(subtree_entry, SUBTREE_SHA, 'c', 0o0040000) - self.assertEquals(subtree_entry.type, 'tree') + self.assertEqual(subtree_entry.type, 'tree') subtree = self.repo[subtree_entry.id] self.assertEqual(1, len(subtree)) From 802976535ac0690a2888f66a1fbd83ebb2a34818 Mon Sep 17 00:00:00 2001 From: Guille -bisho- Date: Wed, 23 Sep 2015 16:30:19 -0700 Subject: [PATCH 11/36] Add support for GIT_DIFF_SHOW_BINARY Adding the binary diff flag GIT_DIFF_SHOW_BINARY to pygit2. libgit2 0.23.0 already supports this constant to be used in diff flags and produces properly formated binary diffs. --- src/pygit2.c | 1 + test/data/binaryfilerepo.tar | Bin 0 -> 92160 bytes test/test_diff.py | 26 ++++++++++++++++++++++++++ test/utils.py | 6 ++++++ 4 files changed, 33 insertions(+) create mode 100644 test/data/binaryfilerepo.tar diff --git a/src/pygit2.c b/src/pygit2.c index 12d365f..cf613ae 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -323,6 +323,7 @@ moduleinit(PyObject* m) ADD_CONSTANT_INT(m, GIT_DIFF_IGNORE_CASE) ADD_CONSTANT_INT(m, GIT_DIFF_SHOW_UNTRACKED_CONTENT) ADD_CONSTANT_INT(m, GIT_DIFF_SKIP_BINARY_CHECK) + ADD_CONSTANT_INT(m, GIT_DIFF_SHOW_BINARY) ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_TYPECHANGE) ADD_CONSTANT_INT(m, GIT_DIFF_INCLUDE_TYPECHANGE_TREES) ADD_CONSTANT_INT(m, GIT_DIFF_RECURSE_IGNORED_DIRS) diff --git a/test/data/binaryfilerepo.tar b/test/data/binaryfilerepo.tar new file mode 100644 index 0000000000000000000000000000000000000000..c00fc93a0b0e110344d6d9d831a6a0206976cbc2 GIT binary patch literal 92160 zcmeHw4U8knb>5tGu{@L!Lp~vfA{!`i#y2y2!(lgnhr60R&HngywSUs?o|Mb}HrdUY zz9qZ4-Q@0$?oOr>I|^i5iYzBcpu~2JM3D^}NT66JCjuPVRxDjk0UR63Sr;X-Wyvw% z#0FwHunvauy{c|@lWdaB-(7oapwp7g`g!$Uy`OsT)$4a@_f8p_ZaC?(X=% zsVY6w!q2j$?L>B)ojrYQ_u}zWr{8mOtKt}FVOyeAHm?@9)nY+OWU}lMTAY1mOE>Q9 zs0HkROePb0Y+CkCzLYN&OL--sZu_mRnrYqI$t4~rT>Vu9rkm^2b6Fyoo%4Siqi=lv zE2la8hxng_K{x;Bm27T_<>vxT&!7)E|M$@UT^)6>wP!YaP&MgSpZ){oznTl^4_%YUpnqv%EuO`$|x2#vQQrM3}xEZzW;eX=#nJWRG z|J#6hr2OanpD!v>{?CZQ!-wgie<1(G4>#yMFnom3zk>dSTyC)a4{;yJf3p8GN_6>KsCHVH=p!1)a%R~Pw zxm;c;zzazFpWA=Z|2G}p|1+gRd|h8G75a63MhcfWl(;`_qWHh;as8|gf7~0rKs?LGr&V{zK>g zjEX>zJpWCK?{WH#l>hv!*AxGtXT#z@B65^1<>G6OZMBq_r^In6H=DLm{68D658(6p zzl}$c@;^iNpBMiX{(r=Q<@qnb;QT(Fvvsp-8afjjfswyY+H3Q}_k%Wa6#obMzhYZe zb8lLhef}S$|AqfoRq(r_=2SZWWed{(J1yE{^&hGK`T4I`|L2es5kKLw33*n<<#g`!)>O~R7=w*nwtEMn8#>V_VN1@7|TuXBQMcSA3Dsxh@srH7%F{8 z+9^FOD+c>Jyt>Hl$k!da>R<5Og@ zEu;82W`5 zgo*TjZp`z4PU`EHP*EP3kI8vL>5#L7&zBOg?7gh`YAA0{&%@w-&Kci%1{`X?GNj&`@ z-`Ahd^xy1Jq&Qg&F(;TMivKy+&no#V#Q$x4j@1AC`Co7V$oroYkaO%VQS=YwziH{l z^rtZ2{u_S&3*~=6{%2+W*Vwe2q{B%0KPdkzcKjE01^RP><^7*Yh&6{kQT#s}t`C6q z`M-@vQSv{N3EltW`Co;iy#Ev6Z=Rn{oW6J*%0G>_Ih;KAj*dyDfyeqbG^tf8zSZ%YWhj;rtKz zpUX@C-)QPh-y(|sf&912&l;8Hv?^ef{f~4{=>IPNU!L;6rTsrWeMgNjQvQ#+>p4-a z&jK_;Z~ZqJB^pJmdHf#5{~p)ROa=J--^Kz4+y5c{SBu%Kl>hS}ci)&%^bh1eC5H5M z8T)}x|Kaw3DE|lce;)Lg@_%epkKJLU{GY-7zoM+{JT_ex)gg-ic{mj7M$e=&pnA1VK5g8rd{45WV`|C`#BK>(lr zgXDjI{YMpbK&1UYlyEcpaishoGyYpd{%=qGSJr==5v}Gi%s~DpuAh+y$mjny7BE=; zhwcAjz98#A&V$^2V-BRh(EouJ5Ep~``p0u~IE-d3;lJwP0waVxc0haLtbf_jBYZ{`cd5f$~?>X<+p2zk%n! z@cVzb{#O-x|C5aWOoR1!eJ9iZW4-?;o9(avAajD_k!S9$hVj3m&gKB<^M4zwiPZl* z|F_Hkm#6rz^#9GB{7b{=ACmvYX&~|GA4mR+{9nj_dH-`7l*jHnQvOe$|EuB+GQB=Q zDgR?rXub`H@xM}By!;pWzwrN|{=by}^QAqFK8*f>{NFB21Iak@zv$Wj8TtO_X;2=! z?@0L{>Hizk{_oHKlJY+`h34CE82@h<7BBxf|EtA(QQrTXFX6q>htWTf|D~x^v6TM{ zA>~AqEJXhIzyCwZ|B0wI`%c67zm%8qzk~6+`-V~LeCz)JA%{Okm!0{){Vg8IC&TC; z$p7;8G{6HN!|OkW{QrdHmpK zk43i|#{cE*#mj%r|JX5<|1*`*85fZcdHtVZ^bh2}o{NjlsCHw?e_sCs?*fDWPsV?v zusPdjqvb!n#kWWPk6Zsk`hR9iv>A>zjQ{oAeUSge|0Mt6|CRE8hJ+q4)G+!7@}DY# z4HzKC<+c z{}a0Zf%@Nc|5wWYxskmy@G$xZ^1q=~Zgskh{lTaI@cf@J{qaT+>HirU&y#eBC;xlw z|8d{{RqXTsNS!bVvF6Zc82=O3&*B8&^M4!g$CdwC>HnDnwfl!0Mt_|D0u@lnYx;II ztE-iqS~4o7lA6gXnvu)udZ|#Xl#1DW#i;gAfG41TE|+6V!{xuo{}uV4DE}zy|3%_* z)(=O@|ET=WvE=`DM(MBiA@BdqnvfHYH-i7C;Q9a{pa0u<6ea&N3cdd=%>Q{chx~6P z7-D{(9&-Qxy+{7pkDvMLzslacbj|(H-A7)&wfnn2@E`xzOYWZ>{H^afwEQpMXs7@1 zO;^A4Z1vc+|N7swpZ)XSerxt;b}uAue`5cOH~!&^U%v9#_D@{=WctI8ee8DPM_zsH z;#)uaXIB${_fHS5{QM8U`njk6=Fh$I$}hh9%b)&#-~ae8e(>Cfa$mUpjyHX{{s+JE z`rmuY%2R*x=U@BE_g>uEeE+|C^yupR$SX%4j)7tPkN97p|Fd}=Dn6IR)%M*Q5CUG{_C4J zPrdZ=na!X7{nst6{>-BvoY!=6+y@K;!}uThpMn05S0*BIbw2;c(f{K9Pobzt|8FEN zXZ>)b{*S!>GhY24SPQ-PKc)VkH6bS)Zv_8O!Sw+^KL5AzC`$il`s)AWa|--_QvU}y zoYyCu|39pL_;u=cE&bg8P5jW!kACdl@BGpapKreRt1I90>IY8!``>@_vCsYSAOHGe zpZvtf-(tRY{SA+P=!3ucrKP|4+v|Vr{0Gin_|?DjM;mW={K=>O>@(l^kx#wtCtf@E z)&KMQpVZI)yMOe_>tFcP@~6K0O&@#r<*)x%^0UAG%rAWAPrvWQFaPdq&%F4hn=gIy z_N~YM>HqlbPk#H~-Z}Hy*IxP7H{ba7cb_PK=(`@<`upeq`^SIn=->FvPk-W zH-G9|Z+z!hU;c|nK5_KiWAj45Lq6^>{>S~lK>r*03aT<}m*M3v;8c(`vejx;%jL4U zO5Uhc5F1dGa`ZJ}pZ~|u|8M}x{Le*f&B^rtSm*z2R{H-JLCT5d7{UKjaD4!d&;M;a ziqik#^S`kF^BGm@{{Vq={1iI>|L0RLpSkdLod3@}xc0f~Un!Te zijpbm+of_LTi)I-Zx<_uUdm>bTwXJ(s2Ztdqb+uy|KsR?FaD1NFsc6+u}?kUh{wKx5j zIf5jIULXdB^1m9=|J&7^mMLkOoS_#grHWEgbLC7ahp>N1gJ zv;T|o{$~^>Xa8)Z{*UzkkEQ?dE?4RQpFQPfGvWyTpMvWH_o`mu{|UY`l> z7-~|5hPlfcDohR1+*==_A z^s(KG$4{Mp&&jQdV`xp&wwh+$*ijJ`QgWqnt3=2wftWO#WwU|&KQ-3}Q2PAe#-k|x zpGPgJF8){X`cJ9<0|d_T6KE6bkLif>#<>B;o@$x3n!!?K)7`gI?Ct#DcT^q2&>2$yx@nnBQ>$$pX1ls>=uB)-RyAuzA~pb|9KL5AzIZFTI_m2Me-v3ab_kYRyA9E%?h96Gi(9=%t^M4xy#ghNH z5Ki$QDgUR%{OJC1=pSf-$zBHx`j6@V%Ob8Q{ePpWH+_qd@_$hLZ}$Gb{C_3`1uQ*(W=44?nocoe1ovjy`1h4~*9;9>vE{GR}a^ZK;H_7HWexhV>;xh$2koBM`C zAG{5asycRkXA7;;;eC-L5v!)eYCx_MmCCTZYBT2N)sPHH-`greVZXT{CNb zW7q>|b{T44%&^Z@zHtv$2)7?B+e*F9aBQ+Enk|=URVaLBx^9aM1kDn2ICjG^wWi^Z zO8TEhfcl3?LY?hK2-+ejHIUKzUgwf zH<^P+b?*r^rJ<6+>Pb^3w-SjhvaeF^ehQ{ctC46LZj+_%vE=Ggr!U_)ar$CfC|fZ% z@5CN|Jh5!tGb%$Htv;tdkz9R8@`c3Vscf3T{2w>~0^r8i{j_g={vRIy4eS4GUcUcx zUL4;&?n3mx%^}!;2{%_+^g#K5N?BApRGnu0F{|5-1&nH zheoA5*aMa%hl1EVNuknh3kP~+v@Q7S8_bfwC z+0|+aXs*HgLcbBZJd=g!aKa`tiQ6e~uxP@_q6M(BrZZDFG;+M`8J6K_wM|xT3EQXP z7!7!BQUO31iyLU@TesoQf&JAK29wzoX3?b!$97iNiQ0EGhmk$A`}E~g7cSm7ed6T# z%j|i0Z*-Qj*qWQZesz88(9x?KTZdNDUCTJkuBz$A+6HFZ0|+d2+hxh?s~9>7^G8jx z=bvXrWgn)Mcy{zCe@wW#OtzSBtyai80dJJvYSc`8F(R-9SCp{7l1a9MVb=MKYu9@K zTJt7OEHl@JSL=D!vdr5oRqoxIG=;rMxY*jJi|^5hXeK< z(55C{n96pWg;*FY{{#E40f$|x)zE>~i3~tr{*Ph*6$`Td%Phz{a*T!8e_0v-8A*uy z)L=0GPs{bef_(mO<57hDhw^W|?|;qYr2QA*Z=Rpx+JByb06T)-3J1jwZEd?|({>IJ zzj1iL#lB-WP|SL(US>^4v)l&sCk22=TgUdHg!Ua7McflYMut#8@S`}GCPlHR`>s6a zNtgwVJ(&MP`X7d2;Mp4A1n~JkzW$f-p9PVBX(9SwW_B)$oMX>3nExU1r{ekm6rcaw zcoe1o3o5<;t;_$b=H>nG0D*J-6j%R4=bF_6c9*;~4$sx887)cI}5BUj3JH&aPNr8Fa-D_6JmLMgMoT`e0%uB7A=Rg+>Q zr@OOac@&FLhK=S{Op_PYp-`O56K#3W2%#KPq`Dxq z4ibv2CSN*SS4e8&InA(iMQaysFPOUfnuBggNYVfypmarqvuW0JpbA^$3Zh3{2uZTH zA=pHyOtNd-B3xnTZ3L+NsfFvT%E}n|mVxvo&9Pe;T?77^00jm&lChANZET`IQgh9v zT}120ZoJ(Bs`X9ogd=~ORyJ!UZ6BLmvTLBG%T#5}W$Bjd@Kmp+v$4rs)2i^jNSJVM zwfP|&3#4=B55^+GjkyELPhFBaz ze<|8qDceG_cPd4{QIa{3x3kMq?_;a}j(;AJHg@*-joq_ncaGDdQ#xC_!cy&%6%(fWMlkezwYy&W0W;_vheRj=Rf)Wk2w*%XV`_D|N85H z$^8EwI^3_XgZZD%f75V%u%hATKNY$DRR1^Z|IK9bsQ)MP{{tM(>r?mn@3`GKaLm1Z z)Zv81mBGF3P5S?B_DDK%i#?07>hKN2tT#kLpx8-C z->T*o42*6Q_U^NGd6V1IWXyPWJ+~p~?8Qw6x=4UgEixzIVWwjueI$zvWAxk@@Z!xd zR{i|73Jh4-quhWaBR05ih=UxgLztv4?g?V3RTWkUpS#s?aRAfm{0m~vCQRbH$WFwH zDEduz8|b*Zn(;fpdbTS2STjK;R;#mhM8?TDCC53p)}ch{cY>nQa$o`iOT6QT4>rt1 zalww^SbUdm8}Jms4qohrS@HS+Bz}k>V@#_7TAglS)D3435W*Z@vy6MqHLtNaBJCMX zjyexOBmf%11m9Hno~bdIx3k+fA|(-Gh4BMq(cX zQkBtJ`X&+s>6Qe3Q`ivzOhz$w0znIMIxQRsIza12Q=CZnDV!|X3ZTP5jn9xomqZAF zC!b6o+DI%PIg+4%hEE|b%i<7()`qK9=umr}taV`_Bij;bnqs5Uc`ih|?u~+OI8xwu z+;(N6+zK2dr*R-@vI}R3mV#jT{v^$D!sfK61W?)zAbkyoEWqmGcaAV@8;-2h?O>hA zr3~jNCeP!ZOpErc*t#A%yfssDB4z>%VvZ_28wa7yC7^sV0i543tsCO&4yP8mNU%F_ z(Itg#+iV`}AOo1^t6mq)Iw0Nb>2pj1LOW1sUr|yeEmght#0E=K`{i|Lt{qg>-k@(UFc(?S&GqzEE4}G~r(WYf zQK}ZfZou8a);mM?jEQ9Jja4!5@)a@iYKE^ba_r2tjST@B_Qd+zxEOMo&&`DT=lF!F zqrlP&UQ^mX>#M+(|9zP|;Mk?hCr)0x$mkcovgKts!yxs82QScqyIUv4iZFqC%|qcb zHN$_D&h2g8j({PMD8JmpaA)grVXt!T7jhpGaLT_#g8Q00l`gJ4YE@$o9yd4-Ibc27 zd|!j;f*aTJ6{=@$bTkH(v!z4dUWO4t9;zzc63}o#vQFxlyfY@unFKq;F2Kpxt9QtN zfdD0wD(NkJv-62dL6UX}NEOeJfIzfK4{>)LTQ^{IK^h7NFEj!+6x=u5-CH|=6*Fkw zj?}K;VkrP6!!A5S2N;?f)1keNT?$VlX+q)0wopT!lwlbq=Jk-qL~}f?*f6NzEJuGs zb805ZY-l0eO@(JlW0paNRA4>P4E(hf7!yV%;e~Xy#s|O}S<~d|m_GWz@u&s*;eXgW zFlHUILbWU`qe53ycN`GW?G5?qYuCU#9IdLIKXe`_+}Fvu~bY!pzXbonq1Y8 zXU|_Nx0--tPc?|%@I%A-O8ZAFiDnd9oYu?)1cuiO#)O z&;MA5FITfn7uIy*y;MJhMs7pl~$W{=9RgX5hfmY5*D7Jn$k5n;7^M z#~G}|+9NFTi%no^+A^H6Y3lIa7rtblfE)}Un6?e}mazQ9@dfb|KD7q#YK0r}o*SCG zrLnYQh_=|o^@1WENE9ESJ;lv>J^+E@Bh)cadj)rcV67z9GsCdT5!!2eb$Ejhe3{NU zG#B?wa|Wi78D5n@f~n3u;ud&DKonR`ymtg+<2)8#BX$;2j65yst$=qGEJ53cknR`= zDhXjh{44k#`+`?7!PfR;4}eq<(_aNbBan6>aZ_zML~#oJ5X)_PnDs2prp+fO;lbHO zOtcPD0r3+*I0OJ7|3QEJ-v%zU<6N~bBrrzi{XJ3R0|7#uH4x@uHM)e)BT$ey-d!i` z{bdA9n4Lngo>BjKT*`Z-Y1`q?|F`cmwW1$2j7tBN*)Fx|?tn(rCOk+)8+wF4zhwoZ zb^c?=#ET+jYt%eX+ak) zeIX%^ObE~M@jRJCRS>EaKm$f-Rs+`;A?x>y#Ie4KFO!0uo{lAEv_dq-v%>QuRO~v)W@_hbp<55KZUnbiZ|H&4!GXF0?;2b}7$A5M^=RgrW z3F<$2yMY_I!kGo*v{M0UROTPq;AH)hdz(JZ(B(S_VCc-Ck+xKv1P5wc2p>OBR$IaFkJ2^hJr zhO$*WGaep;On>5|SNgRDp%mDb;gxTkT?T4LkL@{tAhvgVd1#*e} zJ@n#LJ-qxbqGZY|wvlp@n25U>2p1jdg#&=}1Me^TB)wsUh0Duvd9afw#*0V6Sk@8b zu-0f{4bMl6V+ocB&yOQ~#+0~5wlW|PMDQ=0VnP^f4%G<})y$9?2J%D{g`geL1vuZ8 zIJ{y^7ioPpGM{P7aEqK%glubs+;BAas+3Yy4cN?HP${#yn&mMi?~K2>!4DffZe(+- ztldI&47ig~9wWWN;Hkxj6}FOINz(x|eZ51xG#niak16XBnDz=(AmjuXH=n2MH#+QM zo)tPN`FVcAUG2>S46gB?(L(rHlkk(WEye>jk$HO^0^dxmEDMML+2fD1RleVzc!KR9 zBQFFDV}~|aC=hl(>x0ZuV*b?{da(Wv?EfiBe{f{^`aiz?pH&q3{>O!Ev_;te`JA-> z7edSlCK=5C({g{~XT$QuB(e{~jQ4j-TS%|K*xpLENxqX%-x7 z!ndx$(ar;jFguYUW$i^A0}zoj%sU3I%v5f9<={GZfDV)UkRJ=&eN9c-a5wS~h}4Z~ zL5sZ#tC^Cx$Zj(C@BzMC{NV$c^4>j_&;qzU#4WnAZSzA(WRG7=Kr4(LUl zU0}Q)4A0tUK&M}m1-5leAnNoA>x0oiAjqcSyocxu3}W7i-g_;o^YSn%&ShJnN%@+{ z`#(Xl$-0bemRu%SkEbAzHyI2oCh{YU5B|R}saO(s!;W7}Vhk2MtvXm_z zh~B|>F-YGspQa+!hlkACKdDa#E=f3nLgJwN5**@W5FZ0%%!ar=CpcZ~l~KLJZ8s9V z8+1tT-lko1gL#K^QR8dEr)5FHZo z7@=y2{BG)pQ#m;=io)jy4TI{2V>7ObT}GS>!$v_40{ufFA-I|}&k;dx1%XdI!oJ-~ zP)+llBsGom#}j7hE}_4rsbD1y5!&41ryEUxwj<{9bKbaM5I=~Ed8mlVU+}_GO&|UZ zp62BFEPT$|^e5zri!^Kt;31;w%X(8MKDeTcZJo3}< z4z5u=l5`xTjfUNna@n;tN(&Ndm*-D-Zguq&+%)@2cLtS_iunx0#K}migKb{LmBhcwfYj%|N z+>BoK(?i3g1>V>EE_i{Em*62%2}4-!?1gjOY#9Rwp+TaNAkxpf_qb;ibhfOoZ~|_` zR!Oe*N?x8}>s|Sc+Q`QdoA>A^W*BGF4u?Ksr}Zu(B<#gcU6hQ1H?-eo1+TEAdSwc# z23B|$UI*q5Cj}iMme}7fyk$j)@z>fj>QqFcy95xLyVe0rEpRSnC&P;3!4D)xY2oKj zQrkf6J Date: Sat, 26 Sep 2015 13:30:24 +0200 Subject: [PATCH 12/36] Update changelog --- README.rst | 11 +++++++++-- docs/install.rst | 3 +++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/README.rst b/README.rst index 9f3b798..c0581ce 100644 --- a/README.rst +++ b/README.rst @@ -6,7 +6,7 @@ pygit2 - libgit2 bindings in Python :target: http://travis-ci.org/libgit2/pygit2 Pygit2 is a set of Python bindings to the libgit2 shared library, libgit2 -implements the core of Git. Pygit2 works with Python 2.7, 3.2, 3.3, 3.4 and +implements Git plumbing. Pygit2 works with Python 2.7, 3.2, 3.3, 3.4 and PyPy 2.6 Links: @@ -28,12 +28,19 @@ Changelog 0.23.1 (2015-XX-XX) ------------------------- -- Upgrade to cffi 1.0+ +- Improve support for cffi 1.0+ `#529 `_ + `#561 `_ - Fix ``Remote.push`` `#557 `_ +- New ``TreeEntry.type`` + `#560 `_ + +- New ``pygit2.GIT_DIFF_SHOW_BINARY`` + `#566 `_ + 0.23.0 (2015-08-14) ------------------------- diff --git a/docs/install.rst b/docs/install.rst index 0a0a5c4..6b4e4c3 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -19,6 +19,9 @@ Requirements - Libssh2, optional, used for SSH network operations. - pkg-config, optional, used for SSH network operations. +It should work with older versions of cffi and PyPy, but using cffi 1.0+ +(and PyPy 2.6+) is strongly encouraged. + .. warning:: One common mistake users do is to choose incompatible versions of libgit2 From e4ef8ea5c280f898987ae0e24c2f69d93be75c19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sat, 26 Sep 2015 20:49:13 +0200 Subject: [PATCH 13/36] Release 0.23.1 --- README.rst | 48 +++++++++++++++++++++-------------------- docs/conf.py | 2 +- docs/general.rst | 12 +++++------ docs/install.rst | 28 ++++++++++++------------ pygit2/libgit2_build.py | 2 +- 5 files changed, 47 insertions(+), 45 deletions(-) diff --git a/README.rst b/README.rst index c0581ce..e2537cf 100644 --- a/README.rst +++ b/README.rst @@ -25,7 +25,7 @@ How to install Changelog ============== -0.23.1 (2015-XX-XX) +0.23.1 (2015-09-26) ------------------------- - Improve support for cffi 1.0+ @@ -680,10 +680,10 @@ Other: `#331 `_ Authors ============== -93 developers have contributed at least 1 commit to pygit2:: +97 developers have contributed at least 1 commit to pygit2:: J. David Ibáñez Carlos Martín Nieto Nico von Geyso - W. Trevor King Dave Borowitz Daniel Rodríguez Troitiño + W. Trevor King Dave Borowitz Daniel Rodríguez Troitiño Richo Healey Christian Boos Julien Miotte Richard Möhn Xu Tao Jose Plana Matthew Duggan Matthew Gamble Martin Lenders @@ -691,28 +691,30 @@ Authors Yonggang Luo Patrick Steinhardt Valentin Haenel Michael Jones Bernardo Heynemann John Szakmeister Vlad Temian Brodie Rao David Versmisse - Rémi Duraffort Sebastian Thiel Alok Singhal - Fraser Tweedale Han-Wen Nienhuys Leonardo Rhodes - Petr Viktorin Ron Cohen Santiago Perez De Rosso - Thomas Kluyver Alex Chamberlain Alexander Bayandin - Amit Bakshi Andrey Devyatkin Arno van Lumig - Ben Davis Eric Schrijver Greg Fitzgerald - Hervé Cauwelier Huang Huang Ian P. McCullough - Jack O'Connor Jared Flatow Jiunn Haur Lim - Jun Omae Kaarel Kitsemets Kevin KIN-FOO - Sarath Lakshman Vicent Marti Zoran Zaric - Adam Spiers Andrew Chin András Veres-Szentkirályi - Ash Berlin Benjamin Kircher Benjamin Pollack - Bryan O'Sullivan Colin Watson Daniel Bruce - David Fischer David Sanders Devaev Maxim + Rémi Duraffort Santiago Perez De Rosso Sebastian Thiel + Alok Singhal Fraser Tweedale Han-Wen Nienhuys + Leonardo Rhodes Nicolas Dandrimont Petr Viktorin + Ron Cohen Thomas Kluyver Alex Chamberlain + Alexander Bayandin Amit Bakshi Andrey Devyatkin + Arno van Lumig Ben Davis Eric Schrijver + Greg Fitzgerald Hervé Cauwelier Huang Huang + Ian P. McCullough Jack O'Connor Jared Flatow + Jiunn Haur Lim Jun Omae Kaarel Kitsemets + Kevin KIN-FOO Michael Sondergaard Sarath Lakshman + Vicent Marti Zoran Zaric Adam Spiers + Andrew Chin András Veres-Szentkirályi Ash Berlin + Benjamin Kircher Benjamin Pollack Bryan O'Sullivan + Colin Watson Daniel Bruce David Fischer + David Sanders David Six Devaev Maxim Eric Davis Erik Meusel Erik van Zijst - Ferengee Gustavo Di Pietro Holger Frey - Hugh Cole-Baker Jasper Lievisse Josh Bleecher Snyder - Justin Clift Kyriakos Oikonomakos Lukas Fleischer - Mathieu Bridon Michael Sondergaard Óscar San José + Ferengee Guille -bisho- Gustavo Di Pietro + Holger Frey Hugh Cole-Baker Jasper Lievisse Adriaanse + Josh Bleecher Snyder Justin Clift Kyriakos Oikonomakos + Lukas Fleischer Mathieu Bridon Óscar San José Peter Dave Hello Philippe Ombredanne Ridge Kennedy - Ross Nicoll Rui Abreu Ferreira Soasme - Vladimir Rutsky chengyuhang earl + Ross Nicoll Rui Abreu Ferreira Sheeo + Soasme Vladimir Rutsky chengyuhang + earl License diff --git a/docs/conf.py b/docs/conf.py index 64d8ed6..07be9ce 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -52,7 +52,7 @@ copyright = u'2010-2014 The pygit2 contributors' # The short X.Y version. version = '0.23' # The full version, including alpha/beta/rc tags. -release = '0.23.0' +release = '0.23.1' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/general.rst b/docs/general.rst index 6671fe2..59a8925 100644 --- a/docs/general.rst +++ b/docs/general.rst @@ -18,7 +18,7 @@ library that has been built against. The version number has a .. py:data:: LIBGIT2_VER_MAJOR Integer value of the major version number. For example, for the version - ``0.23.0``:: + ``0.23.2``:: >>> print LIBGIT2_VER_MAJOR 0 @@ -26,25 +26,25 @@ library that has been built against. The version number has a .. py:data:: LIBGIT2_VER_MINOR Integer value of the minor version number. For example, for the version - ``0.23.0``:: + ``0.23.2``:: >>> print LIBGIT2_VER_MINOR - 22 + 23 .. py:data:: LIBGIT2_VER_REVISION Integer value of the revision version number. For example, for the version - ``0.23.0``:: + ``0.23.2``:: >>> print LIBGIT2_VER_REVISION - 0 + 2 .. py:data:: LIBGIT2_VERSION The libgit2 version number as a string:: >>> print LIBGIT2_VERSION - '0.23.0' + '0.23.2' Errors ====== diff --git a/docs/install.rst b/docs/install.rst index 6b4e4c3..b21e61e 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -38,11 +38,11 @@ while the last number |lq| *.micro* |rq| auto-increments independently. As illustration see this table of compatible releases: -+-----------+--------+----------------+----------------------------------------+ -|**libgit2**| 0.23.0 | 0.22.0, 0.22.1 | 0.21.1, 0.21.2 | -+-----------+--------+----------------+----------------------------------------+ -|**pygit2** | 0.23.0 | 0.22.0 | 0.21.0, 0.21.1, 0.21.2, 0.21.3, 0.21.4 | -+-----------+--------+----------------+----------------------------------------+ ++-----------+------------------------+--------------------------------+ +|**libgit2**| 0.23.0, 0.23.1, 0.23.2 | 0.22.0, 0.22.1, 0.22.2, 0.22.3 | ++-----------+------------------------+--------------------------------+ +|**pygit2** | 0.23.0, 0.23.1 | 0.22.0, 0.22.1 | ++-----------+------------------------+--------------------------------+ .. warning:: @@ -59,9 +59,9 @@ directory, do: .. code-block:: sh - $ wget https://github.com/libgit2/libgit2/archive/v0.23.0.tar.gz - $ tar xzf v0.23.0.tar.gz - $ cd libgit2-0.23.0/ + $ wget https://github.com/libgit2/libgit2/archive/v0.23.2.tar.gz + $ tar xzf v0.23.2.tar.gz + $ cd libgit2-0.23.2/ $ cmake . $ make $ sudo make install @@ -143,9 +143,9 @@ Install libgit2 (see we define the installation prefix): .. code-block:: sh - $ wget https://github.com/libgit2/libgit2/archive/v0.23.0.tar.gz - $ tar xzf v0.23.0.tar.gz - $ cd libgit2-0.23.0/ + $ wget https://github.com/libgit2/libgit2/archive/v0.23.2.tar.gz + $ tar xzf v0.23.2.tar.gz + $ cd libgit2-0.23.2/ $ cmake . -DCMAKE_INSTALL_PREFIX=$LIBGIT2 $ make $ make install @@ -198,9 +198,9 @@ from a bash shell: .. code-block:: sh $ export LIBGIT2=C:/Dev/libgit2 - $ wget https://github.com/libgit2/libgit2/archive/v0.23.0.tar.gz - $ tar xzf v0.23.0.tar.gz - $ cd libgit2-0.23.0/ + $ wget https://github.com/libgit2/libgit2/archive/v0.23.2.tar.gz + $ tar xzf v0.23.2.tar.gz + $ cd libgit2-0.23.2/ $ cmake . -DSTDCALL=OFF -DCMAKE_INSTALL_PREFIX=$LIBGIT2 -G "Visual Studio 9 2008" $ cmake --build . --config release --target install $ ctest -v diff --git a/pygit2/libgit2_build.py b/pygit2/libgit2_build.py index 0b2b50d..52cc63b 100644 --- a/pygit2/libgit2_build.py +++ b/pygit2/libgit2_build.py @@ -42,7 +42,7 @@ import sys # # The version number of pygit2 # -__version__ = '0.23.0' +__version__ = '0.23.1' # From 7b97ade6cea410a5665576294d46c4e7c869d488 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 26 Sep 2015 02:59:48 +0200 Subject: [PATCH 14/36] Move remote callbacks to use a class for callbacks This represents what's going on much better than the remnants from the older methods. What we do is pass a list of callbacks to libgit2 for it to call, and they are valid for a single operation, not for the remote itself. This should also make it easier to re-use callbacks which have already been set up. --- pygit2/__init__.py | 4 +- pygit2/errors.py | 3 + pygit2/remote.py | 228 +++++++++++++++++++++------------------ test/test_credentials.py | 29 ++--- test/test_remote.py | 41 ++++--- 5 files changed, 168 insertions(+), 137 deletions(-) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 3e437d7..b4c7d0d 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -35,10 +35,10 @@ from _pygit2 import * from .blame import Blame, BlameHunk from .config import Config from .credentials import * -from .errors import check_error +from .errors import check_error, Passthrough from .ffi import ffi, C from .index import Index, IndexEntry -from .remote import Remote, get_credentials +from .remote import Remote, RemoteCallbacks, get_credentials from .repository import Repository from .settings import Settings from .submodule import Submodule diff --git a/pygit2/errors.py b/pygit2/errors.py index e0a6767..3d7f6f9 100644 --- a/pygit2/errors.py +++ b/pygit2/errors.py @@ -62,3 +62,6 @@ def check_error(err, io=False): # Generic Git error raise GitError(message) + +# Indicate that we want libgit2 to pretend a function was not set +Passthrough = Exception("The function asked for pass-through") diff --git a/pygit2/remote.py b/pygit2/remote.py index def4497..c38dea0 100644 --- a/pygit2/remote.py +++ b/pygit2/remote.py @@ -30,7 +30,7 @@ from __future__ import absolute_import # Import from pygit2 from _pygit2 import Oid -from .errors import check_error, GitError +from .errors import check_error, GitError, Passthrough from .ffi import ffi, C from .credentials import KeypairFromAgent from .refspec import Refspec @@ -71,7 +71,12 @@ class TransferProgress(object): """"Number of bytes received up to now""" -class Remote(object): +class RemoteCallbacks(object): + """Base class for pygit2 remote callbacks. + + Inherit from this class and override the callbacks which you want to use + in your class, which you can then pass to the network operations. + """ def sideband_progress(self, string): """Progress output callback @@ -100,7 +105,7 @@ class Remote(object): Return value: credential """ - pass + raise Passthrough def transfer_progress(self, stats): """Transfer progress callback @@ -131,48 +136,7 @@ class Remote(object): :param str messsage: rejection message from the remote. If None, the update was accepted. """ - def __init__(self, repo, ptr): - """The constructor is for internal use only""" - - self._repo = repo - self._remote = ptr - self._stored_exception = None - - def __del__(self): - C.git_remote_free(self._remote) - - @property - def name(self): - """Name of the remote""" - - return maybe_string(C.git_remote_name(self._remote)) - - @property - def url(self): - """Url of the remote""" - - return maybe_string(C.git_remote_url(self._remote)) - - @property - def push_url(self): - """Push url of the remote""" - - return maybe_string(C.git_remote_pushurl(self._remote)) - - def save(self): - """Save a remote to its repository's configuration.""" - - err = C.git_remote_save(self._remote) - check_error(err) - - def fetch(self, refspecs=None, message=None): - """Perform a fetch against this remote. Returns a - object. - """ - - fetch_opts = ffi.new('git_fetch_options *') - err = C.git_fetch_init_options(fetch_opts, C.GIT_FETCH_OPTIONS_VERSION) - + def _fill_fetch_options(self, fetch_opts): fetch_opts.callbacks.sideband_progress = self._sideband_progress_cb fetch_opts.callbacks.transfer_progress = self._transfer_progress_cb fetch_opts.callbacks.update_tips = self._update_tips_cb @@ -183,58 +147,7 @@ class Remote(object): self._stored_exception = None - try: - with StrArray(refspecs) as arr: - err = C.git_remote_fetch(self._remote, arr, fetch_opts, to_bytes(message)) - if self._stored_exception: - raise self._stored_exception - check_error(err) - finally: - self._self_handle = None - - return TransferProgress(C.git_remote_stats(self._remote)) - - @property - def refspec_count(self): - """Total number of refspecs in this remote""" - - return C.git_remote_refspec_count(self._remote) - - def get_refspec(self, n): - """Return the object at the given position.""" - spec = C.git_remote_get_refspec(self._remote, n) - return Refspec(self, spec) - - @property - def fetch_refspecs(self): - """Refspecs that will be used for fetching""" - - specs = ffi.new('git_strarray *') - err = C.git_remote_get_fetch_refspecs(specs, self._remote) - check_error(err) - - return strarray_to_strings(specs) - - @property - def push_refspecs(self): - """Refspecs that will be used for pushing""" - - specs = ffi.new('git_strarray *') - err = C.git_remote_get_push_refspecs(specs, self._remote) - check_error(err) - - return strarray_to_strings(specs) - - def push(self, specs): - """Push the given refspec to the remote. Raises ``GitError`` on - protocol error or unpack failure. - - :param [str] specs: push refspecs to use - """ - push_opts = ffi.new('git_push_options *') - err = C.git_push_init_options(push_opts, C.GIT_PUSH_OPTIONS_VERSION) - - # Build custom callback structure + def _fill_push_options(self, push_opts): push_opts.callbacks.sideband_progress = self._sideband_progress_cb push_opts.callbacks.transfer_progress = self._transfer_progress_cb push_opts.callbacks.update_tips = self._update_tips_cb @@ -244,13 +157,6 @@ class Remote(object): self._self_handle = ffi.new_handle(self) push_opts.callbacks.payload = self._self_handle - try: - with StrArray(specs) as refspecs: - err = C.git_remote_push(self._remote, refspecs, push_opts) - check_error(err) - finally: - self._self_handle = None - # These functions exist to be called by the git_remote as # callbacks. They proxy the call to whatever the user set @@ -337,11 +243,125 @@ class Remote(object): cred_out[0] = ccred[0] except Exception as e: + if e is Passthrough: + return C.GIT_PASSTHROUGH + self._stored_exception = e return C.GIT_EUSER return 0 +class Remote(object): + def __init__(self, repo, ptr): + """The constructor is for internal use only""" + + self._repo = repo + self._remote = ptr + self._stored_exception = None + + def __del__(self): + C.git_remote_free(self._remote) + + @property + def name(self): + """Name of the remote""" + + return maybe_string(C.git_remote_name(self._remote)) + + @property + def url(self): + """Url of the remote""" + + return maybe_string(C.git_remote_url(self._remote)) + + @property + def push_url(self): + """Push url of the remote""" + + return maybe_string(C.git_remote_pushurl(self._remote)) + + def save(self): + """Save a remote to its repository's configuration.""" + + err = C.git_remote_save(self._remote) + check_error(err) + + def fetch(self, refspecs=None, callbacks=None, message=None): + """Perform a fetch against this remote. Returns a + object. + """ + + fetch_opts = ffi.new('git_fetch_options *') + err = C.git_fetch_init_options(fetch_opts, C.GIT_FETCH_OPTIONS_VERSION) + + if callbacks is None: + callbacks = RemoteCallbacks() + + callbacks._fill_fetch_options(fetch_opts) + + try: + with StrArray(refspecs) as arr: + err = C.git_remote_fetch(self._remote, arr, fetch_opts, to_bytes(message)) + if callbacks._stored_exception: + raise callbacks._stored_exception + check_error(err) + finally: + callbacks._self_handle = None + + return TransferProgress(C.git_remote_stats(self._remote)) + + @property + def refspec_count(self): + """Total number of refspecs in this remote""" + + return C.git_remote_refspec_count(self._remote) + + def get_refspec(self, n): + """Return the object at the given position.""" + spec = C.git_remote_get_refspec(self._remote, n) + return Refspec(self, spec) + + @property + def fetch_refspecs(self): + """Refspecs that will be used for fetching""" + + specs = ffi.new('git_strarray *') + err = C.git_remote_get_fetch_refspecs(specs, self._remote) + check_error(err) + + return strarray_to_strings(specs) + + @property + def push_refspecs(self): + """Refspecs that will be used for pushing""" + + specs = ffi.new('git_strarray *') + err = C.git_remote_get_push_refspecs(specs, self._remote) + check_error(err) + + return strarray_to_strings(specs) + + def push(self, specs, callbacks=None): + """Push the given refspec to the remote. Raises ``GitError`` on + protocol error or unpack failure. + + :param [str] specs: push refspecs to use + """ + push_opts = ffi.new('git_push_options *') + err = C.git_push_init_options(push_opts, C.GIT_PUSH_OPTIONS_VERSION) + + if callbacks is None: + callbacks = RemoteCallbacks() + + callbacks._fill_push_options(push_opts) + # Build custom callback structure + + try: + with StrArray(specs) as refspecs: + err = C.git_remote_push(self._remote, refspecs, push_opts) + check_error(err) + finally: + callbacks._self_handle = None def get_credentials(fn, url, username, allowed): """Call fn and return the credentials object""" diff --git a/test/test_credentials.py b/test/test_credentials.py index 376032f..461fe5e 100644 --- a/test/test_credentials.py +++ b/test/test_credentials.py @@ -70,32 +70,33 @@ class CredentialCreateTest(utils.NoRepoTestCase): class CredentialCallback(utils.RepoTestCase): def test_callback(self): - def credentials_cb(url, username, allowed): - self.assertTrue(allowed & GIT_CREDTYPE_USERPASS_PLAINTEXT) - raise Exception("I don't know the password") + class MyCallbacks(pygit2.RemoteCallbacks): + def credentials(url, username, allowed): + self.assertTrue(allowed & GIT_CREDTYPE_USERPASS_PLAINTEXT) + raise Exception("I don't know the password") remote = self.repo.create_remote("github", "https://github.com/github/github") - remote.credentials = credentials_cb - self.assertRaises(Exception, remote.fetch) + self.assertRaises(Exception, lambda: remote.fetch(callbacks=MyCallbacks())) def test_bad_cred_type(self): - def credentials_cb(url, username, allowed): - self.assertTrue(allowed & GIT_CREDTYPE_USERPASS_PLAINTEXT) - return Keypair("git", "foo.pub", "foo", "sekkrit") + class MyCallbacks(pygit2.RemoteCallbacks): + def credentials(url, username, allowed): + self.assertTrue(allowed & GIT_CREDTYPE_USERPASS_PLAINTEXT) + return Keypair("git", "foo.pub", "foo", "sekkrit") remote = self.repo.create_remote("github", "https://github.com/github/github") - remote.credentials = credentials_cb - - self.assertRaises(TypeError, remote.fetch) + self.assertRaises(TypeError, lambda: remote.fetch(callbacks=MyCallbacks())) class CallableCredentialTest(utils.RepoTestCase): def test_user_pass(self): - remote = self.repo.create_remote("bb", "https://bitbucket.org/libgit2/testgitrepository.git") - remote.credentials = UserPass("libgit2", "libgit2") + class MyCallbacks(pygit2.RemoteCallbacks): + def __init__(self): + self.credentials = UserPass("libgit2", "libgit2") - remote.fetch() + remote = self.repo.create_remote("bb", "https://bitbucket.org/libgit2/testgitrepository.git") + remote.fetch(callbacks=MyCallbacks()) if __name__ == '__main__': unittest.main() diff --git a/test/test_remote.py b/test/test_remote.py index c2e4f8f..b39312a 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -213,30 +213,37 @@ class EmptyRepositoryTest(utils.EmptyRepoTestCase): def test_transfer_progress(self): self.tp = None - def tp_cb(stats): - self.tp = stats + class MyCallbacks(pygit2.RemoteCallbacks): + def transfer_progress(self, stats): + self.tp = stats + callbacks = MyCallbacks() remote = self.repo.remotes[0] - remote.transfer_progress = tp_cb - stats = remote.fetch() - self.assertEqual(stats.received_bytes, self.tp.received_bytes) - self.assertEqual(stats.indexed_objects, self.tp.indexed_objects) - self.assertEqual(stats.received_objects, self.tp.received_objects) + stats = remote.fetch(callbacks=callbacks) + self.assertEqual(stats.received_bytes, callbacks.tp.received_bytes) + self.assertEqual(stats.indexed_objects, callbacks.tp.indexed_objects) + self.assertEqual(stats.received_objects, callbacks.tp.received_objects) def test_update_tips(self): remote = self.repo.remotes[0] - self.i = 0 - self.tips = [('refs/remotes/origin/master', Oid(hex='0'*40), - Oid(hex='784855caf26449a1914d2cf62d12b9374d76ae78')), - ('refs/tags/root', Oid(hex='0'*40), - Oid(hex='3d2962987c695a29f1f80b6c3aa4ec046ef44369'))] + tips = [('refs/remotes/origin/master', Oid(hex='0'*40), + Oid(hex='784855caf26449a1914d2cf62d12b9374d76ae78')), + ('refs/tags/root', Oid(hex='0'*40), + Oid(hex='3d2962987c695a29f1f80b6c3aa4ec046ef44369'))] - def ut_cb(name, old, new): - self.assertEqual(self.tips[self.i], (name, old, new)) - self.i += 1 + class MyCallbacks(pygit2.RemoteCallbacks): + def __init__(self, test_self, tips): + self.test = test_self + self.tips = tips + self.i = 0 - remote.update_tips = ut_cb - remote.fetch() + def update_tips(self, name, old, new): + self.test.assertEqual(self.tips[self.i], (name, old, new)) + self.i += 1 + + callbacks = MyCallbacks(self, tips) + remote.fetch(callbacks=callbacks) + self.assertTrue(callbacks.i > 0) class PushTestCase(unittest.TestCase): def setUp(self): From ab97c08f72e913a2428ddf8b96316c4429d17479 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 26 Sep 2015 03:10:14 +0200 Subject: [PATCH 15/36] Make clone take the callbacks object This lets use the same callbacks for fetch and clone; it also fills in the callbacks which the clone function did not support. --- docs/remotes.rst | 6 +++++ pygit2/__init__.py | 56 +++++++---------------------------------- test/test_repository.py | 7 ++++-- 3 files changed, 20 insertions(+), 49 deletions(-) diff --git a/docs/remotes.rst b/docs/remotes.rst index 387e9ac..ced708a 100644 --- a/docs/remotes.rst +++ b/docs/remotes.rst @@ -21,6 +21,12 @@ The Remote type .. autoclass:: pygit2.Remote :members: +The RemoteCallbacks type +======================== + +.. autoclass:: pygit2.RemoteCallbacks + :members: + The TransferProgress type =========================== diff --git a/pygit2/__init__.py b/pygit2/__init__.py index b4c7d0d..1fcd4da 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -143,22 +143,6 @@ def init_repository(path, bare=False, # Ok return Repository(to_str(path)) - -@ffi.callback('int (*credentials)(git_cred **cred, const char *url,' - 'const char *username_from_url, unsigned int allowed_types,' - 'void *data)') -def _credentials_cb(cred_out, url, username_from_url, allowed, data): - d = ffi.from_handle(data) - - try: - ccred = get_credentials(d['credentials_cb'], url, username_from_url, allowed) - cred_out[0] = ccred[0] - except Exception as e: - d['exception'] = e - return C.GIT_EUSER - - return 0 - @ffi.callback('int (*git_repository_create_cb)(git_repository **out,' 'const char *path, int bare, void *payload)') def _repository_create_cb(repo_out, path, bare, data): @@ -189,24 +173,9 @@ def _remote_create_cb(remote_out, repo, name, url, data): return 0 -@ffi.callback('int (*git_transport_certificate_check_cb)' - '(git_cert *cert, int valid, const char *host, void *payload)') -def _certificate_cb(cert_i, valid, host, data): - d = ffi.from_handle(data) - try: - # python's parting is deep in the libraries and assumes an OpenSSL-owned cert - val = d['certificate_cb'](None, bool(valid), ffi.string(host)) - if not val: - return C.GIT_ECERTIFICATE - except Exception as e: - d['exception'] = e - return C.GIT_EUSER - - return 0 - def clone_repository( url, path, bare=False, repository=None, remote=None, - checkout_branch=None, credentials=None, certificate=None): + checkout_branch=None, callbacks=None): """Clones a new Git repository from *url* in the given *path*. Returns a Repository class pointing to the newly cloned repository. @@ -224,11 +193,8 @@ def clone_repository( :param str checkout_branch: Branch to checkout after the clone. The default is to use the remote's default branch. - :param callable credentials: authentication to use if the remote - requires it - - :param callable certificate: callback to verify the host's - certificate or fingerprint. + :param RemoteCallbacks callbacks: object which implements the + callbacks as methods. :rtype: Repository @@ -240,8 +206,8 @@ def clone_repository( signature. The Remote it returns will be used instead of the default one. - The certificate callback has `(cert, valid, hostname) -> bool` as - a signature. Return True to accept the connection, False to abort. + The callbacks should be an object which inherits from + `pyclass:RemoteCallbacks`. """ @@ -252,10 +218,8 @@ def clone_repository( # Data, let's use a dict as we don't really want much more d = {} - d['credentials_cb'] = credentials d['repository_cb'] = repository d['remote_cb'] = remote - d['certificate_cb'] = certificate d_handle = ffi.new_handle(d) # Perform the initialization with the version we compiled @@ -277,13 +241,11 @@ def clone_repository( opts.bare = bare - if credentials: - opts.fetch_opts.callbacks.credentials = _credentials_cb - opts.fetch_opts.callbacks.payload = d_handle - if certificate: - opts.fetch_opts.callbacks.certificate_check = _certificate_cb - opts.fetch_opts.callbacks.payload = d_handle + if callbacks is None: + callbacks = RemoteCallbacks() + + callbacks._fill_fetch_options(opts.fetch_opts) err = C.git_clone(crepo, to_bytes(url), to_bytes(path), opts) diff --git a/test/test_repository.py b/test/test_repository.py index 1ef68b0..810e275 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -514,10 +514,13 @@ class CloneRepositoryTest(utils.NoRepoTestCase): self.assertIsNotNone(repo.remotes["custom_remote"]) def test_clone_with_credentials(self): - credentials = pygit2.UserPass("libgit2", "libgit2") + class MyCallbacks(pygit2.RemoteCallbacks): + def __init__(self): + self.credentials = pygit2.UserPass("libgit2", "libgit2") + repo = clone_repository( "https://bitbucket.org/libgit2/testgitrepository.git", - self._temp_dir, credentials=credentials) + self._temp_dir, callbacks=MyCallbacks()) self.assertFalse(repo.is_empty) From ac2e363d043eef1631a83cb03d7a0115a6e9c9af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 26 Sep 2015 22:42:19 +0200 Subject: [PATCH 16/36] Allow setting credentials and certificate in callback ctor This allows for a less verbose way of setting one-liners as these callbacks. --- pygit2/remote.py | 15 +++++++++++++++ test/test_repository.py | 6 +----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/pygit2/remote.py b/pygit2/remote.py index c38dea0..a30cf27 100644 --- a/pygit2/remote.py +++ b/pygit2/remote.py @@ -78,6 +78,21 @@ class RemoteCallbacks(object): in your class, which you can then pass to the network operations. """ + def __init__(self, credentials=None, certificate=None): + """Initialize some callbacks in-line + + Use this constructor to provide credentials and certificate + callbacks in-line, instead of defining your own class for these ones. + + You can e.g. also pass in one of the credential objects as 'credentials' + instead of creating a function which returns a hard-coded object. + """ + + if credentials is not None: + self.credentials = credentials + if certificate is not None: + self.certificate = certificate + def sideband_progress(self, string): """Progress output callback diff --git a/test/test_repository.py b/test/test_repository.py index 810e275..c12c822 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -514,13 +514,9 @@ class CloneRepositoryTest(utils.NoRepoTestCase): self.assertIsNotNone(repo.remotes["custom_remote"]) def test_clone_with_credentials(self): - class MyCallbacks(pygit2.RemoteCallbacks): - def __init__(self): - self.credentials = pygit2.UserPass("libgit2", "libgit2") - repo = clone_repository( "https://bitbucket.org/libgit2/testgitrepository.git", - self._temp_dir, callbacks=MyCallbacks()) + self._temp_dir, callbacks=pygit2.RemoteCallbacks(credentials=pygit2.UserPass("libgit2", "libgit2"))) self.assertFalse(repo.is_empty) From 563cb9018e5371a590c6c70cf3f8d4d6a19e9c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 26 Sep 2015 23:40:01 +0200 Subject: [PATCH 17/36] Bring back the certificate check callback This was implemented for clone, but not for fetch or push, so it was deleted during the conversion, which shows why we need to unify these callback structures. --- pygit2/remote.py | 54 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/pygit2/remote.py b/pygit2/remote.py index a30cf27..8cc9e35 100644 --- a/pygit2/remote.py +++ b/pygit2/remote.py @@ -122,6 +122,25 @@ class RemoteCallbacks(object): """ raise Passthrough + def certificate_check(self, certificate, valid, host): + """Certificate callback + + Override with your own function to determine whether the accept + the server's certificate. + + :param None certificate: The certificate. It is currently always None + while we figure out how to represent it cross-platform + + :param bool valid: Whether the TLS/SSH library thinks the certificate + is valid + + :param str host: The hostname we want to connect to + + Return value: True to connect, False to abort + """ + + raise Passthrough + def transfer_progress(self, stats): """Transfer progress callback @@ -156,6 +175,7 @@ class RemoteCallbacks(object): fetch_opts.callbacks.transfer_progress = self._transfer_progress_cb fetch_opts.callbacks.update_tips = self._update_tips_cb fetch_opts.callbacks.credentials = self._credentials_cb + fetch_opts.callbacks.certificate_check = self._certificate_cb # We need to make sure that this handle stays alive self._self_handle = ffi.new_handle(self) fetch_opts.callbacks.payload = self._self_handle @@ -167,6 +187,7 @@ class RemoteCallbacks(object): push_opts.callbacks.transfer_progress = self._transfer_progress_cb push_opts.callbacks.update_tips = self._update_tips_cb push_opts.callbacks.credentials = self._credentials_cb + push_opts.callbacks.certificate_check = self._certificate_cb push_opts.callbacks.push_update_reference = self._push_update_reference_cb # We need to make sure that this handle stays alive self._self_handle = ffi.new_handle(self) @@ -266,6 +287,39 @@ class RemoteCallbacks(object): return 0 + @ffi.callback('int (*git_transport_certificate_check_cb)' + '(git_cert *cert, int valid, const char *host, void *payload)') + def _certificate_cb(cert_i, valid, host, data): + self = ffi.from_handle(data) + + # We want to simulate what should happen if libgit2 supported pass-through for + # this callback. For SSH, 'valid' is always False, because it doesn't look + # at known_hosts, but we do want to let it through in order to do what libgit2 would + # if the callback were not set. + try: + is_ssh = cert_i.cert_type == C.GIT_CERT_HOSTKEY_LIBSSH2 + + if not hasattr(self, 'certificate_check') or not self.certificate_check: + raise Passthrough + + # python's parsing is deep in the libraries and assumes an OpenSSL-owned cert + val = self.certificate_check(None, bool(valid), ffi.string(host)) + if not val: + return C.GIT_ECERTIFICATE + except Exception as e: + if e is Passthrough: + if is_ssh: + return 0 + elif valid: + return 0 + else: + return C.GIT_ECERTIFICATE + + self._stored_exception = e + return C.GIT_EUSER + + return 0 + class Remote(object): def __init__(self, repo, ptr): """The constructor is for internal use only""" From b8e6852d26cb567346a7723291f61191bbdfc29a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sun, 27 Sep 2015 03:09:25 +0200 Subject: [PATCH 18/36] Add some missing fields to DiffFile's docs --- docs/diff.rst | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/diff.rst b/docs/diff.rst index 3ae7696..97a52c4 100644 --- a/docs/diff.rst +++ b/docs/diff.rst @@ -83,6 +83,9 @@ Attributes: .. autoattribute:: pygit2.DiffFile.path .. autoattribute:: pygit2.DiffFile.id +.. autoattribute:: pygit2.DiffFile.size +.. autoattribute:: pygit2.DiffFile.flags +.. autoattribute:: pygit2.DiffFile.mode The DiffHunk type From 8a66da12786cd02891a627d640df1b90a6712406 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 27 Sep 2015 13:57:30 +0200 Subject: [PATCH 19/36] docs: remove reference to old Remote.credentials --- docs/remotes.rst | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/remotes.rst b/docs/remotes.rst index ced708a..6aa7bd7 100644 --- a/docs/remotes.rst +++ b/docs/remotes.rst @@ -48,8 +48,6 @@ Refspecs objects are not constructed directly, but returned by Credentials ================ -.. automethod:: pygit2.Remote.credentials - There are two types of credentials: username/password and SSH key pairs. Both :py:class:`pygit2.UserPass` and :py:class:`pygit2.Keypair` are callable objects, with the appropriate signature for the From 681c7d43413d2b25db1fd0ecf7614fa2e10f47a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 27 Sep 2015 14:03:32 +0200 Subject: [PATCH 20/36] Fixing pip install pygit2 Regression in the latest release, "pip install pygit2" fails if cffi is not already installed. Should be fixed with this change, not tested. --- setup.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 8b37b0f..15dc51e 100644 --- a/setup.py +++ b/setup.py @@ -46,8 +46,12 @@ import sys import unittest # Get cffi major version -import cffi -cffi_major_version = cffi.__version_info__[0] +try: + import cffi +except ImportError: + cffi_major_version = None +else: + cffi_major_version = cffi.__version_info__[0] # Import stuff from pygit2/_utils.py without loading the whole pygit2 package sys.path.insert(0, 'pygit2') From 2b083a1509debd80f136443420311ffb63089e3e Mon Sep 17 00:00:00 2001 From: Nicolas Dandrimont Date: Mon, 28 Sep 2015 18:24:48 +0200 Subject: [PATCH 21/36] Add _name attribute to TreeEntry This mirrors the _message attribute for Tag, which gives you the raw bytes from the entry name. Useful to parse repos where some filenames aren't encoded as utf-8, such as https://github.com/wuts/earthquake. --- src/tree.c | 10 ++++++++++ test/test_tree.py | 1 + 2 files changed, 11 insertions(+) diff --git a/src/tree.c b/src/tree.c index e5cb9ef..e265829 100644 --- a/src/tree.c +++ b/src/tree.c @@ -67,6 +67,15 @@ TreeEntry_name__get__(TreeEntry *self) } +PyDoc_STRVAR(TreeEntry__name__doc__, "Name (bytes)."); + +PyObject * +TreeEntry__name__get__(TreeEntry *self) +{ + return PyBytes_FromString(git_tree_entry_name(self->entry)); +} + + PyDoc_STRVAR(TreeEntry_type__doc__, "Type."); PyObject * @@ -177,6 +186,7 @@ TreeEntry_repr(TreeEntry *self) PyGetSetDef TreeEntry_getseters[] = { GETTER(TreeEntry, filemode), GETTER(TreeEntry, name), + GETTER(TreeEntry, _name), GETTER(TreeEntry, oid), GETTER(TreeEntry, id), GETTER(TreeEntry, hex), diff --git a/test/test_tree.py b/test/test_tree.py index 7e9bd9a..7a40851 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -45,6 +45,7 @@ class TreeTest(utils.BareRepoTestCase): def assertTreeEntryEqual(self, entry, sha, name, filemode): self.assertEqual(entry.hex, sha) self.assertEqual(entry.name, name) + self.assertEqual(entry._name, name.encode('utf-8')) self.assertEqual(entry.filemode, filemode, '0%o != 0%o' % (entry.filemode, filemode)) From eadc2a320fadb8c6ba9ec5aecb66494dd0189b7c Mon Sep 17 00:00:00 2001 From: Nicolas Dandrimont Date: Mon, 5 Oct 2015 19:08:56 +0200 Subject: [PATCH 22/36] tag._message: avoid NULL pointer dereference A tag message can be empty. In that case, git_tag_message returns NULL. PyBytes_FromString doesn't check its argument for nullness, and therefore accessing _message on a tag with an empty message segfaults Python. --- src/tag.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/tag.c b/src/tag.c index d6507ae..87cef47 100644 --- a/src/tag.c +++ b/src/tag.c @@ -111,7 +111,11 @@ PyDoc_STRVAR(Tag__message__doc__, "Tag message (bytes)."); PyObject * Tag__message__get__(Tag *self) { - return PyBytes_FromString(git_tag_message(self->tag)); + const char *message; + message = git_tag_message(self->tag); + if (!message) + Py_RETURN_NONE; + return PyBytes_FromString(message); } PyMethodDef Tag_methods[] = { From cf439e42866f9f35532d2e4301bd071c357a4c3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Sanguinetti?= Date: Tue, 6 Oct 2015 14:44:18 -0300 Subject: [PATCH 23/36] List OpenSSL as a dependency in the docs --- docs/install.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/install.rst b/docs/install.rst index b21e61e..f1e6e22 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -16,6 +16,7 @@ Requirements - Python 2.7, 3.2+ or PyPy 2.6+ (including the development headers) - Libgit2 v0.23.x - cffi 1.0+ +- OpenSSL development headers, optional, used for HTTPS network operations. - Libssh2, optional, used for SSH network operations. - pkg-config, optional, used for SSH network operations. From 9db87373645ea031cecefe92e7d14f9d65370755 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 11 Oct 2015 11:06:22 +0200 Subject: [PATCH 24/36] Update changelog --- README.rst | 46 ++++++++++++++++++++++++++++++++++++++++ docs/install.rst | 8 ++++--- pygit2/remote.py | 2 +- test/test_credentials.py | 18 ++++++++++------ test/test_remote.py | 1 - 5 files changed, 63 insertions(+), 12 deletions(-) diff --git a/README.rst b/README.rst index e2537cf..c706403 100644 --- a/README.rst +++ b/README.rst @@ -25,6 +25,52 @@ How to install Changelog ============== +0.23.2 (2015-1X-XX) +------------------------- + +- Unify callbacks system for remotes and clone + `#568 `_ + +- New ``TreeEntry._name`` + `#570 `_ + +- Fix segfault in ``Tag._message`` + `#572 `_ + +- Documentation improvements + `#569 `_ + `#574 `_ + +API changes to clone:: + + # Before + clone_repository(..., credentials, certificate) + + # Now + callbacks = RemoteCallbacks(credentials, certificate) + clone_repository(..., callbacks) + +API changes to remote:: + + # Before + def transfer_progress(stats): + ... + + remote.credentials = credentials + remote.transfer_progress = transfer_progress + remote.fetch() + remote.push(specs) + + # Now + class MyCallbacks(RemoteCallbacks): + def transfer_progress(self, stats): + ... + + callbacks = MyCallbacks(credentials) + remote.fetch(callbacks=callbacks) + remote.push(specs, callbacks=callbacks) + + 0.23.1 (2015-09-26) ------------------------- diff --git a/docs/install.rst b/docs/install.rst index f1e6e22..d428b08 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -16,9 +16,11 @@ Requirements - Python 2.7, 3.2+ or PyPy 2.6+ (including the development headers) - Libgit2 v0.23.x - cffi 1.0+ -- OpenSSL development headers, optional, used for HTTPS network operations. -- Libssh2, optional, used for SSH network operations. -- pkg-config, optional, used for SSH network operations. + +Optional libgit2 dependecies to support ssh and https: + +- https: WinHTTP (Windows), SecureTransport (OS X) or OpenSSL. +- ssh: libssh2, pkg-config It should work with older versions of cffi and PyPy, but using cffi 1.0+ (and PyPy 2.6+) is strongly encouraged. diff --git a/pygit2/remote.py b/pygit2/remote.py index 8cc9e35..49af59e 100644 --- a/pygit2/remote.py +++ b/pygit2/remote.py @@ -355,7 +355,7 @@ class Remote(object): err = C.git_remote_save(self._remote) check_error(err) - def fetch(self, refspecs=None, callbacks=None, message=None): + def fetch(self, refspecs=None, message=None, callbacks=None): """Perform a fetch against this remote. Returns a object. """ diff --git a/test/test_credentials.py b/test/test_credentials.py index 461fe5e..5fddfe2 100644 --- a/test/test_credentials.py +++ b/test/test_credentials.py @@ -71,32 +71,36 @@ class CredentialCreateTest(utils.NoRepoTestCase): class CredentialCallback(utils.RepoTestCase): def test_callback(self): class MyCallbacks(pygit2.RemoteCallbacks): + @staticmethod def credentials(url, username, allowed): self.assertTrue(allowed & GIT_CREDTYPE_USERPASS_PLAINTEXT) raise Exception("I don't know the password") - remote = self.repo.create_remote("github", "https://github.com/github/github") + url = "https://github.com/github/github" + remote = self.repo.create_remote("github", url) self.assertRaises(Exception, lambda: remote.fetch(callbacks=MyCallbacks())) def test_bad_cred_type(self): class MyCallbacks(pygit2.RemoteCallbacks): + @staticmethod def credentials(url, username, allowed): self.assertTrue(allowed & GIT_CREDTYPE_USERPASS_PLAINTEXT) return Keypair("git", "foo.pub", "foo", "sekkrit") - remote = self.repo.create_remote("github", "https://github.com/github/github") + url = "https://github.com/github/github" + remote = self.repo.create_remote("github", url) self.assertRaises(TypeError, lambda: remote.fetch(callbacks=MyCallbacks())) class CallableCredentialTest(utils.RepoTestCase): def test_user_pass(self): - class MyCallbacks(pygit2.RemoteCallbacks): - def __init__(self): - self.credentials = UserPass("libgit2", "libgit2") + credentials = UserPass("libgit2", "libgit2") + callbacks = pygit2.RemoteCallbacks(credentials=credentials) - remote = self.repo.create_remote("bb", "https://bitbucket.org/libgit2/testgitrepository.git") - remote.fetch(callbacks=MyCallbacks()) + url = "https://bitbucket.org/libgit2/testgitrepository.git" + remote = self.repo.create_remote("bb", url) + remote.fetch(callbacks=callbacks) if __name__ == '__main__': unittest.main() diff --git a/test/test_remote.py b/test/test_remote.py index b39312a..cecabf0 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -212,7 +212,6 @@ class EmptyRepositoryTest(utils.EmptyRepoTestCase): self.assertEqual(stats.received_objects, REMOTE_REPO_OBJECTS) def test_transfer_progress(self): - self.tp = None class MyCallbacks(pygit2.RemoteCallbacks): def transfer_progress(self, stats): self.tp = stats From f5aa1829ac2e3ec9f0e4430e391a979db804a83d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 11 Oct 2015 11:58:07 +0200 Subject: [PATCH 25/36] Do not use hasattr, not robust In Python versions older than 3.2 hasattr is not robust as it masks real errors. And usually is not efficient as a call to hasattr is often followed by another call to getattr. It is best to avoid using it completely. See https://docs.python.org/3/whatsnew/3.2.html#other-language-changes --- pygit2/index.py | 25 ++++++++++++---------- pygit2/libgit2_build.py | 14 ++++++------ pygit2/remote.py | 47 ++++++++++++++++++++++------------------- 3 files changed, 45 insertions(+), 41 deletions(-) diff --git a/pygit2/index.py b/pygit2/index.py index 9354ab4..9167df0 100644 --- a/pygit2/index.py +++ b/pygit2/index.py @@ -48,6 +48,7 @@ class Index(object): err = C.git_index_open(cindex, to_bytes(path)) check_error(err) + self._repo = None self._index = cindex[0] self._cindex = cindex @@ -125,14 +126,15 @@ class Index(object): The tree will be read recursively and all its children will also be inserted into the Index. """ + repo = self._repo if is_string(tree): - tree = self._repo[tree] + tree = repo[tree] if isinstance(tree, Oid): - if not hasattr(self, '_repo'): + if repo is None: raise TypeError("id given but no associated repository") - tree = self._repo[tree] + tree = repo[tree] elif not isinstance(tree, Tree): raise TypeError("argument must be Oid or Tree") @@ -214,7 +216,8 @@ class Index(object): interhunk_lines: the maximum number of unchanged lines between hunk boundaries before the hunks will be merged into a one """ - if not hasattr(self, '_repo'): + repo = self._repo + if repo is None: raise ValueError('diff needs an associated repository') copts = ffi.new('git_diff_options *') @@ -226,11 +229,11 @@ class Index(object): copts.interhunk_lines = interhunk_lines cdiff = ffi.new('git_diff **') - err = C.git_diff_index_to_workdir(cdiff, self._repo._repo, - self._index, copts) + err = C.git_diff_index_to_workdir(cdiff, repo._repo, self._index, + copts) check_error(err) - return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), self._repo) + return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), repo) def diff_to_tree(self, tree, flags=0, context_lines=3, interhunk_lines=0): """Diff the index against a tree. Return a object with the @@ -248,8 +251,8 @@ class Index(object): interhunk_lines: the maximum number of unchanged lines between hunk boundaries before the hunks will be merged into a one. """ - - if not hasattr(self, '_repo'): + repo = self._repo + if repo is None: raise ValueError('diff needs an associated repository') if not isinstance(tree, Tree): @@ -267,11 +270,11 @@ class Index(object): ffi.buffer(ctree)[:] = tree._pointer[:] cdiff = ffi.new('git_diff **') - err = C.git_diff_tree_to_index(cdiff, self._repo._repo, ctree[0], + err = C.git_diff_tree_to_index(cdiff, repo._repo, ctree[0], self._index, copts) check_error(err) - return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), self._repo) + return Diff.from_c(bytes(ffi.buffer(cdiff)[:]), repo) # diff --git a/pygit2/libgit2_build.py b/pygit2/libgit2_build.py index 52cc63b..30cd63b 100644 --- a/pygit2/libgit2_build.py +++ b/pygit2/libgit2_build.py @@ -31,7 +31,6 @@ pygit2 at run-time. """ # Import from the Standard Library -from binascii import crc32 import codecs import os from os import getenv @@ -75,9 +74,8 @@ ffi = cffi.FFI() # Load C definitions if getattr(sys, 'frozen', False): - if hasattr(sys, '_MEIPASS'): - dir_path = sys._MEIPASS - else: + dir_path = getattr(sys, '_MEIPASS', None) + if dir_path is None: dir_path = dirname(abspath(sys.executable)) else: dir_path = dirname(abspath(__file__)) @@ -94,10 +92,10 @@ C_KEYWORDS = dict(libraries=['git2'], # The modulename # Simplified version of what cffi does: remove kwargs and vengine -preamble = "#include " - -if hasattr(ffi, 'set_source'): - ffi.set_source("pygit2._libgit2", preamble, **C_KEYWORDS) +set_source = getattr(ffi, 'set_source', None) +if set_source is not None: + preamble = "#include " + set_source("pygit2._libgit2", preamble, **C_KEYWORDS) ffi.cdef(C_HEADER_SRC) diff --git a/pygit2/remote.py b/pygit2/remote.py index 49af59e..7cb6232 100644 --- a/pygit2/remote.py +++ b/pygit2/remote.py @@ -30,9 +30,8 @@ from __future__ import absolute_import # Import from pygit2 from _pygit2 import Oid -from .errors import check_error, GitError, Passthrough +from .errors import check_error, Passthrough from .ffi import ffi, C -from .credentials import KeypairFromAgent from .refspec import Refspec from .utils import to_bytes, strarray_to_strings, StrArray @@ -100,7 +99,6 @@ class RemoteCallbacks(object): :param str string: Progress output from the remote """ - pass def credentials(self, url, username_from_url, allowed_types): """Credentials callback @@ -148,7 +146,6 @@ class RemoteCallbacks(object): :param TransferProgress stats: The progress up to now """ - pass def update_tips(self, refname, old, new): """Update tips callabck @@ -200,12 +197,12 @@ class RemoteCallbacks(object): def _transfer_progress_cb(stats_ptr, data): self = ffi.from_handle(data) - if not hasattr(self, 'transfer_progress') \ - or not self.transfer_progress: + transfer_progress = getattr(self, 'transfer_progress', None) + if not transfer_progress: return 0 try: - self.transfer_progress(TransferProgress(stats_ptr)) + transfer_progress(TransferProgress(stats_ptr)) except Exception as e: self._stored_exception = e return C.GIT_EUSER @@ -216,12 +213,13 @@ class RemoteCallbacks(object): def _sideband_progress_cb(string, length, data): self = ffi.from_handle(data) - if not hasattr(self, 'progress') or not self.progress: + progress = getattr(self, 'progress', None) + if not progress: return 0 try: s = ffi.string(string, length).decode() - self.progress(s) + progress(s) except Exception as e: self._stored_exception = e return C.GIT_EUSER @@ -233,7 +231,8 @@ class RemoteCallbacks(object): def _update_tips_cb(refname, a, b, data): self = ffi.from_handle(data) - if not hasattr(self, 'update_tips') or not self.update_tips: + update_tips = getattr(self, 'update_tips', None) + if not update_tips: return 0 try: @@ -241,7 +240,7 @@ class RemoteCallbacks(object): a = Oid(raw=bytes(ffi.buffer(a)[:])) b = Oid(raw=bytes(ffi.buffer(b)[:])) - self.update_tips(s, a, b) + update_tips(s, a, b) except Exception as e: self._stored_exception = e return C.GIT_EUSER @@ -252,13 +251,14 @@ class RemoteCallbacks(object): def _push_update_reference_cb(ref, msg, data): self = ffi.from_handle(data) - if not hasattr(self, 'push_update_reference') or not self.push_update_reference: + push_update_reference = getattr(self, 'push_update_reference', None) + if not push_update_reference: return 0 try: refname = ffi.string(ref) message = maybe_string(msg) - self.push_update_reference(refname, message) + push_update_reference(refname, message) except Exception as e: self._stored_exception = e return C.GIT_EUSER @@ -271,11 +271,12 @@ class RemoteCallbacks(object): def _credentials_cb(cred_out, url, username, allowed, data): self = ffi.from_handle(data) - if not hasattr(self, 'credentials') or not self.credentials: + credentials = getattr(self, 'credentials', None) + if not credentials: return 0 try: - ccred = get_credentials(self.credentials, url, username, allowed) + ccred = get_credentials(credentials, url, username, allowed) cred_out[0] = ccred[0] except Exception as e: @@ -299,11 +300,12 @@ class RemoteCallbacks(object): try: is_ssh = cert_i.cert_type == C.GIT_CERT_HOSTKEY_LIBSSH2 - if not hasattr(self, 'certificate_check') or not self.certificate_check: + certificate_check = getattr(self, 'certificate_check', None) + if not certificate_check: raise Passthrough # python's parsing is deep in the libraries and assumes an OpenSSL-owned cert - val = self.certificate_check(None, bool(valid), ffi.string(host)) + val = certificate_check(None, bool(valid), ffi.string(host)) if not val: return C.GIT_ECERTIFICATE except Exception as e: @@ -440,23 +442,24 @@ def get_credentials(fn, url, username, allowed): creds = fn(url_str, username_str, allowed) - if not hasattr(creds, 'credential_type') \ - or not hasattr(creds, 'credential_tuple'): + credential_type = getattr(creds, 'credential_type', None) + credential_tuple = getattr(creds, 'credential_tuple', None) + if not credential_type or not credential_tuple: raise TypeError("credential does not implement interface") - cred_type = creds.credential_type + cred_type = credential_type if not (allowed & cred_type): raise TypeError("invalid credential type") ccred = ffi.new('git_cred **') if cred_type == C.GIT_CREDTYPE_USERPASS_PLAINTEXT: - name, passwd = creds.credential_tuple + name, passwd = credential_tuple err = C.git_cred_userpass_plaintext_new(ccred, to_bytes(name), to_bytes(passwd)) elif cred_type == C.GIT_CREDTYPE_SSH_KEY: - name, pubkey, privkey, passphrase = creds.credential_tuple + name, pubkey, privkey, passphrase = credential_tuple if pubkey is None and privkey is None: err = C.git_cred_ssh_key_from_agent(ccred, to_bytes(name)) else: From d25a0d61def974c65f5c9801b746e784b7b7f198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 11 Oct 2015 12:08:53 +0200 Subject: [PATCH 26/36] Fix error introduced accidentally in previous commit --- pygit2/libgit2_build.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pygit2/libgit2_build.py b/pygit2/libgit2_build.py index 30cd63b..9e6112f 100644 --- a/pygit2/libgit2_build.py +++ b/pygit2/libgit2_build.py @@ -92,9 +92,10 @@ C_KEYWORDS = dict(libraries=['git2'], # The modulename # Simplified version of what cffi does: remove kwargs and vengine +preamble = "#include " + set_source = getattr(ffi, 'set_source', None) if set_source is not None: - preamble = "#include " set_source("pygit2._libgit2", preamble, **C_KEYWORDS) ffi.cdef(C_HEADER_SRC) From 64150d3535ac3dd8d156f5482c25bb61e2d1c4ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 11 Oct 2015 17:49:17 +0200 Subject: [PATCH 27/36] Release 0.23.2 --- README.rst | 2 +- docs/conf.py | 2 +- docs/install.rst | 10 +++++----- pygit2/libgit2_build.py | 2 +- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/README.rst b/README.rst index c706403..4bdf294 100644 --- a/README.rst +++ b/README.rst @@ -25,7 +25,7 @@ How to install Changelog ============== -0.23.2 (2015-1X-XX) +0.23.2 (2015-10-11) ------------------------- - Unify callbacks system for remotes and clone diff --git a/docs/conf.py b/docs/conf.py index 07be9ce..e184c0c 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -52,7 +52,7 @@ copyright = u'2010-2014 The pygit2 contributors' # The short X.Y version. version = '0.23' # The full version, including alpha/beta/rc tags. -release = '0.23.1' +release = '0.23.2' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/install.rst b/docs/install.rst index d428b08..55382b4 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -41,11 +41,11 @@ while the last number |lq| *.micro* |rq| auto-increments independently. As illustration see this table of compatible releases: -+-----------+------------------------+--------------------------------+ -|**libgit2**| 0.23.0, 0.23.1, 0.23.2 | 0.22.0, 0.22.1, 0.22.2, 0.22.3 | -+-----------+------------------------+--------------------------------+ -|**pygit2** | 0.23.0, 0.23.1 | 0.22.0, 0.22.1 | -+-----------+------------------------+--------------------------------+ ++-----------+--------------------------------+--------------------------------+ +|**libgit2**| 0.23.0, 0.23.1, 0.23.2, 0.23.3 | 0.22.0, 0.22.1, 0.22.2, 0.22.3 | ++-----------+--------------------------------+--------------------------------+ +|**pygit2** | 0.23.0, 0.23.1, 0.23.2 | 0.22.0, 0.22.1 | ++-----------+--------------------------------+--------------------------------+ .. warning:: diff --git a/pygit2/libgit2_build.py b/pygit2/libgit2_build.py index 9e6112f..d4bc20a 100644 --- a/pygit2/libgit2_build.py +++ b/pygit2/libgit2_build.py @@ -41,7 +41,7 @@ import sys # # The version number of pygit2 # -__version__ = '0.23.1' +__version__ = '0.23.2' # From 203335bd63cb43c16b5ecbfa60040048d785d5a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 11 Oct 2015 18:42:30 +0200 Subject: [PATCH 28/36] Trying to fix install with pip --- pygit2/__init__.py | 2 +- pygit2/_build.py | 64 ++++++++++++++++++++++++++++ pygit2/{libgit2_build.py => _run.py} | 50 +++++----------------- pygit2/ffi.py | 2 +- setup.py | 6 +-- 5 files changed, 80 insertions(+), 44 deletions(-) create mode 100644 pygit2/_build.py rename pygit2/{libgit2_build.py => _run.py} (69%) diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 1fcd4da..595f91f 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -43,7 +43,7 @@ from .repository import Repository from .settings import Settings from .submodule import Submodule from .utils import to_bytes, to_str -from .libgit2_build import __version__ +from ._build import __version__ # Features diff --git a/pygit2/_build.py b/pygit2/_build.py new file mode 100644 index 0000000..5044866 --- /dev/null +++ b/pygit2/_build.py @@ -0,0 +1,64 @@ +# -*- 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. + +""" +This is an special module, it provides stuff used by setup.py at build time. +But also used by pygit2 at run time. +""" + +# Import from the Standard Library +import os +from os import getenv + +# +# The version number of pygit2 +# +__version__ = '0.23.2' + + +# +# Utility functions to get the paths required for bulding extensions +# +def _get_libgit2_path(): + # LIBGIT2 environment variable takes precedence + libgit2_path = getenv("LIBGIT2") + if libgit2_path is not None: + return libgit2_path + + # Default + if os.name == 'nt': + return '%s\libgit2' % getenv("ProgramFiles") + return '/usr/local' + + +def get_libgit2_paths(): + libgit2_path = _get_libgit2_path() + return ( + os.path.join(libgit2_path, 'bin'), + os.path.join(libgit2_path, 'include'), + getenv('LIBGIT2_LIB', os.path.join(libgit2_path, 'lib')), + ) diff --git a/pygit2/libgit2_build.py b/pygit2/_run.py similarity index 69% rename from pygit2/libgit2_build.py rename to pygit2/_run.py index d4bc20a..bf26cab 100644 --- a/pygit2/libgit2_build.py +++ b/pygit2/_run.py @@ -26,53 +26,23 @@ # Boston, MA 02110-1301, USA. """ -This is an special module, it provides stuff used by setup.py and by -pygit2 at run-time. +This is an special module, it provides stuff used by by pygit2 at run-time. """ # Import from the Standard Library import codecs import os -from os import getenv from os.path import abspath, dirname import sys +# Import from cffi +from cffi import FFI -# -# The version number of pygit2 -# -__version__ = '0.23.2' +# Import from pygit2 +from _build import get_libgit2_paths -# -# Utility functions to get the paths required for bulding extensions -# -def _get_libgit2_path(): - # LIBGIT2 environment variable takes precedence - libgit2_path = getenv("LIBGIT2") - if libgit2_path is not None: - return libgit2_path - - # Default - if os.name == 'nt': - return '%s\libgit2' % getenv("ProgramFiles") - return '/usr/local' - - -def get_libgit2_paths(): - libgit2_path = _get_libgit2_path() - return ( - os.path.join(libgit2_path, 'bin'), - os.path.join(libgit2_path, 'include'), - getenv('LIBGIT2_LIB', os.path.join(libgit2_path, 'lib')), - ) - - -import cffi - -ffi = cffi.FFI() - -# Load C definitions +# C_HEADER_SRC if getattr(sys, 'frozen', False): dir_path = getattr(sys, '_MEIPASS', None) if dir_path is None: @@ -84,21 +54,23 @@ decl_path = os.path.join(dir_path, 'decl.h') with codecs.open(decl_path, 'r', 'utf-8') as header: C_HEADER_SRC = header.read() +# C_KEYWORDS libgit2_bin, libgit2_include, libgit2_lib = get_libgit2_paths() - C_KEYWORDS = dict(libraries=['git2'], library_dirs=[libgit2_lib], include_dirs=[libgit2_include]) -# The modulename -# Simplified version of what cffi does: remove kwargs and vengine +# preamble preamble = "#include " +# ffi +ffi = FFI() set_source = getattr(ffi, 'set_source', None) if set_source is not None: set_source("pygit2._libgit2", preamble, **C_KEYWORDS) ffi.cdef(C_HEADER_SRC) + if __name__ == '__main__': ffi.compile() diff --git a/pygit2/ffi.py b/pygit2/ffi.py index 95c8e4e..9b9a2d5 100644 --- a/pygit2/ffi.py +++ b/pygit2/ffi.py @@ -32,5 +32,5 @@ from __future__ import absolute_import try: from ._libgit2 import ffi, lib as C except ImportError: - from .libgit2_build import ffi, preamble, C_KEYWORDS + from ._run import ffi, preamble, C_KEYWORDS C = ffi.verify(preamble, **C_KEYWORDS) diff --git a/setup.py b/setup.py index 15dc51e..23f24c7 100644 --- a/setup.py +++ b/setup.py @@ -55,9 +55,9 @@ else: # Import stuff from pygit2/_utils.py without loading the whole pygit2 package sys.path.insert(0, 'pygit2') -from libgit2_build import __version__, get_libgit2_paths +from _build import __version__, get_libgit2_paths if cffi_major_version == 0: - from libgit2_build import ffi, preamble, C_KEYWORDS + from _run import ffi, preamble, C_KEYWORDS ffi.verify(preamble, **C_KEYWORDS) del sys.path[0] @@ -186,7 +186,7 @@ extra_args = { if cffi_major_version == 0: extra_args['ext_modules'].append(ffi.verifier.get_extension()) else: - extra_args['cffi_modules']=['pygit2/libgit2_build.py:ffi'] + extra_args['cffi_modules']=['pygit2/_run.py:ffi'] setup(name='pygit2', From 70edbf256a49dd8a4b8cc16cfe41ae0e2a9a4cc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 11 Oct 2015 18:48:59 +0200 Subject: [PATCH 29/36] Update copyright years --- docs/conf.py | 2 +- pygit2/__init__.py | 2 +- pygit2/_build.py | 2 +- pygit2/_run.py | 2 +- pygit2/blame.py | 2 +- pygit2/config.py | 2 +- pygit2/credentials.py | 2 +- pygit2/errors.py | 2 +- pygit2/ffi.py | 2 +- pygit2/index.py | 2 +- pygit2/py2.py | 2 +- pygit2/py3.py | 2 +- pygit2/refspec.py | 2 +- pygit2/remote.py | 2 +- pygit2/repository.py | 2 +- pygit2/settings.py | 2 +- pygit2/submodule.py | 2 +- pygit2/utils.py | 2 +- setup.py | 2 +- src/blob.c | 2 +- src/blob.h | 2 +- src/branch.c | 2 +- src/branch.h | 2 +- src/commit.c | 2 +- src/commit.h | 2 +- src/diff.c | 2 +- src/diff.h | 2 +- src/error.c | 2 +- src/error.h | 2 +- src/note.c | 2 +- src/note.h | 2 +- src/object.c | 2 +- src/object.h | 2 +- src/oid.c | 2 +- src/oid.h | 2 +- src/options.c | 2 +- src/options.h | 2 +- src/patch.c | 2 +- src/patch.h | 2 +- src/pygit2.c | 2 +- src/reference.c | 2 +- src/reference.h | 2 +- src/repository.c | 2 +- src/repository.h | 2 +- src/signature.c | 2 +- src/signature.h | 2 +- src/tag.c | 2 +- src/tag.h | 2 +- src/tree.c | 2 +- src/tree.h | 2 +- src/treebuilder.c | 2 +- src/treebuilder.h | 2 +- src/types.h | 2 +- src/utils.c | 2 +- src/utils.h | 2 +- src/walker.c | 2 +- src/walker.h | 2 +- test/__init__.py | 2 +- test/test_archive.py | 2 +- test/test_attributes.py | 2 +- test/test_blame.py | 2 +- test/test_blob.py | 2 +- test/test_branch.py | 2 +- test/test_cherrypick.py | 2 +- test/test_commit.py | 2 +- test/test_config.py | 2 +- test/test_credentials.py | 2 +- test/test_diff.py | 2 +- test/test_index.py | 2 +- test/test_merge.py | 2 +- test/test_note.py | 2 +- test/test_object.py | 2 +- test/test_oid.py | 2 +- test/test_options.py | 2 +- test/test_refs.py | 2 +- test/test_remote.py | 2 +- test/test_repository.py | 2 +- test/test_revwalk.py | 2 +- test/test_signature.py | 2 +- test/test_status.py | 2 +- test/test_tag.py | 2 +- test/test_tree.py | 2 +- test/test_treebuilder.py | 2 +- test/utils.py | 2 +- 84 files changed, 84 insertions(+), 84 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index e184c0c..fba3b66 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -43,7 +43,7 @@ master_doc = 'index' # General information about the project. project = u'pygit2' -copyright = u'2010-2014 The pygit2 contributors' +copyright = u'2010-2015 The pygit2 contributors' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the diff --git a/pygit2/__init__.py b/pygit2/__init__.py index 595f91f..d11d013 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/pygit2/_build.py b/pygit2/_build.py index 5044866..c430d5e 100644 --- a/pygit2/_build.py +++ b/pygit2/_build.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/pygit2/_run.py b/pygit2/_run.py index bf26cab..bd87a1c 100644 --- a/pygit2/_run.py +++ b/pygit2/_run.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/pygit2/blame.py b/pygit2/blame.py index 9105401..f0882e5 100644 --- a/pygit2/blame.py +++ b/pygit2/blame.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/pygit2/config.py b/pygit2/config.py index a01fdf6..30d94ce 100644 --- a/pygit2/config.py +++ b/pygit2/config.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/pygit2/credentials.py b/pygit2/credentials.py index 52e6b60..4f4376d 100644 --- a/pygit2/credentials.py +++ b/pygit2/credentials.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/pygit2/errors.py b/pygit2/errors.py index 3d7f6f9..e814440 100644 --- a/pygit2/errors.py +++ b/pygit2/errors.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/pygit2/ffi.py b/pygit2/ffi.py index 9b9a2d5..b089b65 100644 --- a/pygit2/ffi.py +++ b/pygit2/ffi.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/pygit2/index.py b/pygit2/index.py index 9167df0..d312bb6 100644 --- a/pygit2/index.py +++ b/pygit2/index.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/pygit2/py2.py b/pygit2/py2.py index a2d2359..e94e098 100644 --- a/pygit2/py2.py +++ b/pygit2/py2.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/pygit2/py3.py b/pygit2/py3.py index 0b6c79a..522bb5b 100644 --- a/pygit2/py3.py +++ b/pygit2/py3.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/pygit2/refspec.py b/pygit2/refspec.py index 9e93d3a..a553cda 100644 --- a/pygit2/refspec.py +++ b/pygit2/refspec.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/pygit2/remote.py b/pygit2/remote.py index 7cb6232..431b356 100644 --- a/pygit2/remote.py +++ b/pygit2/remote.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/pygit2/repository.py b/pygit2/repository.py index b92a3a2..c714a63 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/pygit2/settings.py b/pygit2/settings.py index 88b6971..dbedd27 100644 --- a/pygit2/settings.py +++ b/pygit2/settings.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/pygit2/submodule.py b/pygit2/submodule.py index 0eafd5a..5fd48f6 100644 --- a/pygit2/submodule.py +++ b/pygit2/submodule.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/pygit2/utils.py b/pygit2/utils.py index 5b6a365..c2dba8a 100644 --- a/pygit2/utils.py +++ b/pygit2/utils.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/setup.py b/setup.py index 23f24c7..54bbb0c 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,7 @@ # -*- coding: utf-8 -*- # coding: UTF-8 # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/src/blob.c b/src/blob.c index 2d6a304..d389248 100644 --- a/src/blob.c +++ b/src/blob.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/blob.h b/src/blob.h index 7af6537..d72cb48 100644 --- a/src/blob.h +++ b/src/blob.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/branch.c b/src/branch.c index d725ab1..ed786d3 100644 --- a/src/branch.c +++ b/src/branch.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/branch.h b/src/branch.h index cad0c2d..3d1514b 100644 --- a/src/branch.h +++ b/src/branch.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/commit.c b/src/commit.c index 85f59cd..3ea05f2 100644 --- a/src/commit.c +++ b/src/commit.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/commit.h b/src/commit.h index eeb0a92..c8d37e0 100644 --- a/src/commit.h +++ b/src/commit.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/diff.c b/src/diff.c index 336bc25..e0e57bc 100644 --- a/src/diff.c +++ b/src/diff.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/diff.h b/src/diff.h index 5399e96..3e1411f 100644 --- a/src/diff.h +++ b/src/diff.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/error.c b/src/error.c index 848178c..417c80b 100644 --- a/src/error.c +++ b/src/error.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/error.h b/src/error.h index cc52790..3aa297b 100644 --- a/src/error.h +++ b/src/error.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/note.c b/src/note.c index 6a7a437..e1846ad 100644 --- a/src/note.c +++ b/src/note.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/note.h b/src/note.h index 18858f0..9342376 100644 --- a/src/note.h +++ b/src/note.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/object.c b/src/object.c index 4a6d991..f07bb40 100644 --- a/src/object.c +++ b/src/object.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/object.h b/src/object.h index 5cf01af..2844a56 100644 --- a/src/object.h +++ b/src/object.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/oid.c b/src/oid.c index 8cc5b58..105b999 100644 --- a/src/oid.c +++ b/src/oid.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/oid.h b/src/oid.h index 4d4825b..b050f82 100644 --- a/src/oid.h +++ b/src/oid.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/options.c b/src/options.c index f6eaa5e..d44c312 100644 --- a/src/options.c +++ b/src/options.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/options.h b/src/options.h index 8dccda1..6b05558 100644 --- a/src/options.h +++ b/src/options.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/patch.c b/src/patch.c index dd31035..b51ff30 100644 --- a/src/patch.c +++ b/src/patch.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/patch.h b/src/patch.h index 3dfc6a1..56c1d99 100644 --- a/src/patch.h +++ b/src/patch.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/pygit2.c b/src/pygit2.c index cf613ae..02ddcb9 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/reference.c b/src/reference.c index 2a20cca..fa1ca11 100644 --- a/src/reference.c +++ b/src/reference.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/reference.h b/src/reference.h index 324866a..fd92c4f 100644 --- a/src/reference.h +++ b/src/reference.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/repository.c b/src/repository.c index 4ab50de..eb63bdc 100644 --- a/src/repository.c +++ b/src/repository.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/repository.h b/src/repository.h index 3f6947e..6947269 100644 --- a/src/repository.h +++ b/src/repository.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/signature.c b/src/signature.c index e3f7d71..57dad00 100644 --- a/src/signature.c +++ b/src/signature.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/signature.h b/src/signature.h index 425b3dd..2fd86c7 100644 --- a/src/signature.h +++ b/src/signature.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/tag.c b/src/tag.c index 87cef47..9252b62 100644 --- a/src/tag.c +++ b/src/tag.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/tag.h b/src/tag.h index ada402c..a013134 100644 --- a/src/tag.h +++ b/src/tag.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/tree.c b/src/tree.c index e265829..936d486 100644 --- a/src/tree.c +++ b/src/tree.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/tree.h b/src/tree.h index 6392cf9..7ec9d56 100644 --- a/src/tree.h +++ b/src/tree.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/treebuilder.c b/src/treebuilder.c index dbec825..549d961 100644 --- a/src/treebuilder.c +++ b/src/treebuilder.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/treebuilder.h b/src/treebuilder.h index 16a5af0..cc65f52 100644 --- a/src/treebuilder.h +++ b/src/treebuilder.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/types.h b/src/types.h index f3c1858..e6bb99e 100644 --- a/src/types.h +++ b/src/types.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/utils.c b/src/utils.c index 99cb6f7..8ed860b 100644 --- a/src/utils.c +++ b/src/utils.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/utils.h b/src/utils.h index e0f3d00..adfb603 100644 --- a/src/utils.h +++ b/src/utils.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/walker.c b/src/walker.c index 7c76560..15a9547 100644 --- a/src/walker.c +++ b/src/walker.c @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/src/walker.h b/src/walker.h index 1f4b801..b9eaab2 100644 --- a/src/walker.h +++ b/src/walker.h @@ -1,5 +1,5 @@ /* - * Copyright 2010-2014 The pygit2 contributors + * Copyright 2010-2015 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, diff --git a/test/__init__.py b/test/__init__.py index f065d8b..f21961a 100644 --- a/test/__init__.py +++ b/test/__init__.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_archive.py b/test/test_archive.py index d13a16a..069bae1 100644 --- a/test/test_archive.py +++ b/test/test_archive.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_attributes.py b/test/test_attributes.py index 7b9c398..3244811 100644 --- a/test/test_attributes.py +++ b/test/test_attributes.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_blame.py b/test/test_blame.py index 3e1bf7e..4804e37 100644 --- a/test/test_blame.py +++ b/test/test_blame.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_blob.py b/test/test_blob.py index 9fd1d4f..7869fc7 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_branch.py b/test/test_branch.py index 87e3a57..1fbbf49 100644 --- a/test/test_branch.py +++ b/test/test_branch.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_cherrypick.py b/test/test_cherrypick.py index 10798c9..887d77f 100644 --- a/test/test_cherrypick.py +++ b/test/test_cherrypick.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_commit.py b/test/test_commit.py index 4a637f8..c94d9cd 100644 --- a/test/test_commit.py +++ b/test/test_commit.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_config.py b/test/test_config.py index 7f8b4ce..c6ec4d4 100644 --- a/test/test_config.py +++ b/test/test_config.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_credentials.py b/test/test_credentials.py index 5fddfe2..92482d9 100644 --- a/test/test_credentials.py +++ b/test/test_credentials.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_diff.py b/test/test_diff.py index f379216..2b98e98 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_index.py b/test/test_index.py index f6ec534..6dc17eb 100644 --- a/test/test_index.py +++ b/test/test_index.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_merge.py b/test/test_merge.py index 86d1eea..3c7e7ad 100644 --- a/test/test_merge.py +++ b/test/test_merge.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_note.py b/test/test_note.py index 5dce89c..94c222d 100644 --- a/test/test_note.py +++ b/test/test_note.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_object.py b/test/test_object.py index 6630404..c2d916f 100644 --- a/test/test_object.py +++ b/test/test_object.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_oid.py b/test/test_oid.py index 6c9a71e..6746286 100644 --- a/test/test_oid.py +++ b/test/test_oid.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_options.py b/test/test_options.py index a6bc22b..2a3095f 100644 --- a/test/test_options.py +++ b/test/test_options.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_refs.py b/test/test_refs.py index 0d362c7..53de205 100644 --- a/test/test_refs.py +++ b/test/test_refs.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_remote.py b/test/test_remote.py index cecabf0..dd01d96 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_repository.py b/test/test_repository.py index c12c822..8558340 100644 --- a/test/test_repository.py +++ b/test/test_repository.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_revwalk.py b/test/test_revwalk.py index db08c65..43ca441 100644 --- a/test/test_revwalk.py +++ b/test/test_revwalk.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_signature.py b/test/test_signature.py index 13a2a25..f71762e 100644 --- a/test/test_signature.py +++ b/test/test_signature.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_status.py b/test/test_status.py index 70ee5bf..a4dc2a2 100644 --- a/test/test_status.py +++ b/test/test_status.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_tag.py b/test/test_tag.py index 707ade0..97831e6 100644 --- a/test/test_tag.py +++ b/test/test_tag.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_tree.py b/test/test_tree.py index 7a40851..1ec2810 100644 --- a/test/test_tree.py +++ b/test/test_tree.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/test_treebuilder.py b/test/test_treebuilder.py index 6a513e4..90772e6 100644 --- a/test/test_treebuilder.py +++ b/test/test_treebuilder.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, diff --git a/test/utils.py b/test/utils.py index c8fa8e6..a751e91 100644 --- a/test/utils.py +++ b/test/utils.py @@ -1,6 +1,6 @@ # -*- coding: UTF-8 -*- # -# Copyright 2010-2014 The pygit2 contributors +# Copyright 2010-2015 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, From 13f4ddec1da60fd05fb2946d6c61f3fb0d627da3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 25 Oct 2015 19:17:05 +0100 Subject: [PATCH 30/36] Fix create_blob_fromiobase with Python 2.7 --- src/repository.c | 17 +++++------------ test/test_blob.py | 2 ++ 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/src/repository.c b/src/repository.c index 59cd976..1fa6a72 100644 --- a/src/repository.c +++ b/src/repository.c @@ -39,8 +39,6 @@ #include "signature.h" #include -extern PyTypeObject PyIOBase_Type; - extern PyObject *GitError; extern PyTypeObject IndexType; @@ -874,20 +872,18 @@ int read_chunk(char *content, size_t max_length, void *payload) } PyObject * -Repository_create_blob_fromiobase(Repository *self, PyObject *args) +Repository_create_blob_fromiobase(Repository *self, PyObject *py_file) { git_oid oid; PyObject *py_is_readable; int is_readable; - PyObject *py_file; int err; - if (!PyArg_ParseTuple(args, "O!", &PyIOBase_Type, &py_file)) - return NULL; - py_is_readable = PyObject_CallMethod(py_file, "readable", NULL); if (!py_is_readable) { Py_DECREF(py_file); + if (PyErr_ExceptionMatches(PyExc_AttributeError)) + PyErr_SetObject(PyExc_TypeError, py_file); return NULL; } @@ -900,10 +896,7 @@ Repository_create_blob_fromiobase(Repository *self, PyObject *args) return NULL; } - err = git_blob_create_fromchunks(&oid, - self->repo, - NULL, - &read_chunk, + err = git_blob_create_fromchunks(&oid, self->repo, NULL, &read_chunk, py_file); Py_DECREF(py_file); @@ -1619,7 +1612,7 @@ PyMethodDef Repository_methods[] = { METHOD(Repository, create_blob, METH_VARARGS), METHOD(Repository, create_blob_fromworkdir, METH_VARARGS), METHOD(Repository, create_blob_fromdisk, METH_VARARGS), - METHOD(Repository, create_blob_fromiobase, METH_VARARGS), + METHOD(Repository, create_blob_fromiobase, METH_O), METHOD(Repository, create_commit, METH_VARARGS), METHOD(Repository, create_tag, METH_VARARGS), METHOD(Repository, TreeBuilder, METH_VARARGS), diff --git a/test/test_blob.py b/test/test_blob.py index 10a4f92..78c4c4b 100644 --- a/test/test_blob.py +++ b/test/test_blob.py @@ -114,6 +114,8 @@ class BlobTest(utils.RepoTestCase): self.assertEqual(pygit2.GIT_OBJ_BLOB, blob.type) def test_create_blob_fromiobase(self): + self.assertRaises(TypeError, self.repo.create_blob_fromiobase, 'bad type') + f = io.BytesIO(BLOB_CONTENT) blob_oid = self.repo.create_blob_fromiobase(f) blob = self.repo[blob_oid] From 91bb93d26647260da8f52035e73e49d647039801 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Carlos=20Mart=C3=ADn=20Nieto?= Date: Sat, 31 Oct 2015 16:57:20 +0100 Subject: [PATCH 31/36] Fix create_blob_fromiobase with pypy The file object is passed to us, so we never incremented its refcount. We shouldn't decrement it on exit. --- src/repository.c | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/repository.c b/src/repository.c index 1fa6a72..0d96930 100644 --- a/src/repository.c +++ b/src/repository.c @@ -881,7 +881,6 @@ Repository_create_blob_fromiobase(Repository *self, PyObject *py_file) py_is_readable = PyObject_CallMethod(py_file, "readable", NULL); if (!py_is_readable) { - Py_DECREF(py_file); if (PyErr_ExceptionMatches(PyExc_AttributeError)) PyErr_SetObject(PyExc_TypeError, py_file); return NULL; @@ -898,8 +897,6 @@ Repository_create_blob_fromiobase(Repository *self, PyObject *py_file) err = git_blob_create_fromchunks(&oid, self->repo, NULL, &read_chunk, py_file); - Py_DECREF(py_file); - if (err < 0) return Error_set(err); From 7a8474cd44e4aaaa52ad9163d7d1bf971255662f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Sun, 15 Nov 2015 19:31:08 +0100 Subject: [PATCH 32/36] Change Signature default encoding to UTF-8 Fixes #581 --- src/signature.c | 2 +- test/test_signature.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/signature.c b/src/signature.c index 57dad00..94f7626 100644 --- a/src/signature.c +++ b/src/signature.c @@ -38,7 +38,7 @@ Signature_init(Signature *self, PyObject *args, PyObject *kwds) { char *keywords[] = {"name", "email", "time", "offset", "encoding", NULL}; PyObject *py_name, *tname; - char *email, *encoding = "ascii"; + char *email, *encoding = "utf-8"; const char *name; long long time = -1; int offset = 0; diff --git a/test/test_signature.py b/test/test_signature.py index f71762e..e4c5e8e 100644 --- a/test/test_signature.py +++ b/test/test_signature.py @@ -38,9 +38,9 @@ class SignatureTest(NoRepoTestCase): def test_default(self): signature = Signature( - 'Foo', 'foo@example.com', 1322174594, 60) + 'Foo Ibáñez', 'foo@example.com', 1322174594, 60) encoding = signature._encoding - self.assertEqual(encoding, 'ascii') + self.assertEqual(encoding, 'utf-8') self.assertEqual(signature.name, signature.raw_name.decode(encoding)) self.assertEqual(signature.name.encode(encoding), signature.raw_name) self.assertEqual(signature.email, @@ -50,7 +50,7 @@ class SignatureTest(NoRepoTestCase): def test_ascii(self): self.assertRaises(UnicodeEncodeError, - Signature, 'Foo Ibáñez', 'foo@example.com') + Signature, 'Foo Ibáñez', 'foo@example.com', encoding='ascii') def test_latin1(self): encoding = 'iso-8859-1' From 99dfce9ab8202dacf45127cf1257318d584f18a0 Mon Sep 17 00:00:00 2001 From: Noah Fontes Date: Sat, 5 Dec 2015 23:22:37 -0800 Subject: [PATCH 33/36] Add support for Repository.describe(...). --- docs/repository.rst | 1 + pygit2/decl.h | 41 ++++++++++++++++ pygit2/repository.py | 94 +++++++++++++++++++++++++++++++++++++ src/pygit2.c | 5 ++ test/test_describe.py | 106 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 247 insertions(+) create mode 100644 test/test_describe.py diff --git a/docs/repository.rst b/docs/repository.rst index e434661..cad8908 100644 --- a/docs/repository.rst +++ b/docs/repository.rst @@ -71,3 +71,4 @@ Below there are some general attributes and methods: .. automethod:: pygit2.Repository.state_cleanup .. automethod:: pygit2.Repository.write_archive .. automethod:: pygit2.Repository.ahead_behind +.. automethod:: pygit2.Repository.describe diff --git a/pygit2/decl.h b/pygit2/decl.h index 61afe33..ed59542 100644 --- a/pygit2/decl.h +++ b/pygit2/decl.h @@ -756,6 +756,47 @@ int git_merge_trees(git_index **out, git_repository *repo, const git_tree *ances int git_merge_file_from_index(git_merge_file_result *out, git_repository *repo, const git_index_entry *ancestor, const git_index_entry *ours, const git_index_entry *theirs, const git_merge_file_options *opts); void git_merge_file_result_free(git_merge_file_result *result); +/* + * Describe + */ + +typedef enum { + GIT_DESCRIBE_DEFAULT, + GIT_DESCRIBE_TAGS, + GIT_DESCRIBE_ALL, +} git_describe_strategy_t; + +typedef struct git_describe_options { + unsigned int version; + unsigned int max_candidates_tags; + unsigned int describe_strategy; + const char *pattern; + int only_follow_first_parent; + int show_commit_oid_as_fallback; +} git_describe_options; + +#define GIT_DESCRIBE_OPTIONS_VERSION ... + +int git_describe_init_options(git_describe_options *opts, unsigned int version); + +typedef struct { + unsigned int version; + unsigned int abbreviated_size; + int always_use_long_format; + const char *dirty_suffix; +} git_describe_format_options; + +#define GIT_DESCRIBE_FORMAT_OPTIONS_VERSION ... + +int git_describe_init_format_options(git_describe_format_options *opts, unsigned int version); + +typedef ... git_describe_result; + +int git_describe_commit(git_describe_result **result, git_object *committish, git_describe_options *opts); +int git_describe_workdir(git_describe_result **out, git_repository *repo, git_describe_options *opts); +int git_describe_format(git_buf *out, const git_describe_result *result, const git_describe_format_options *opts); +void git_describe_result_free(git_describe_result *result); + #define GIT_ATTR_CHECK_FILE_THEN_INDEX ... #define GIT_ATTR_CHECK_INDEX_THEN_FILE ... #define GIT_ATTR_CHECK_INDEX_ONLY ... diff --git a/pygit2/repository.py b/pygit2/repository.py index c714a63..f8a6cad 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -637,6 +637,100 @@ class Repository(_Repository): return Index.from_c(self, cindex) + # + # Describe + # + def describe(self, committish=None, max_candidates_tags=None, + describe_strategy=None, pattern=None, + only_follow_first_parent=None, + show_commit_oid_as_fallback=None, abbreviated_size=None, + always_use_long_format=None, dirty_suffix=None): + """Describe a commit-ish or the current working tree. + + :param committish: Commit-ish object or object name to describe, or + `None` to describe the current working tree. + :type committish: `str`, :class:`~.Reference`, or :class:`~.Commit` + + :param int max_candidates_tags: The number of candidate tags to + consider. Increasing above 10 will take slightly longer but may + produce a more accurate result. A value of 0 will cause only exact + matches to be output. + :param int describe_strategy: A GIT_DESCRIBE_* constant. + :param str pattern: Only consider tags matching the given `glob(7)` + pattern, excluding the "refs/tags/" prefix. + :param bool only_follow_first_parent: Follow only the first parent + commit upon seeing a merge commit. + :param bool show_commit_oid_as_fallback: Show uniquely abbreviated + commit object as fallback. + :param int abbreviated_size: The minimum number of hexadecimal digits + to show for abbreviated object names. A value of 0 will suppress + long format, only showing the closest tag. + :param bool always_use_long_format: Always output the long format (the + nearest tag, the number of commits, and the abbrevated commit name) + even when the committish matches a tag. + :param str dirty_suffix: A string to append if the working tree is + dirty. + + :returns: The description. + :rtype: `str` + + Example:: + + repo.describe(pattern='public/*', dirty_suffix='-dirty') + """ + + options = ffi.new('git_describe_options *') + C.git_describe_init_options(options, C.GIT_DESCRIBE_OPTIONS_VERSION) + + if max_candidates_tags is not None: + options.max_candidates_tags = max_candidates_tags + if describe_strategy is not None: + options.describe_strategy = describe_strategy + if pattern: + options.pattern = ffi.new('char[]', to_bytes(pattern)) + if only_follow_first_parent is not None: + options.only_follow_first_parent = only_follow_first_parent + if show_commit_oid_as_fallback is not None: + options.show_commit_oid_as_fallback = show_commit_oid_as_fallback + + result = ffi.new('git_describe_result **') + if committish: + if is_string(committish): + committish = self.revparse_single(committish) + + commit = committish.peel(Commit) + + cptr = ffi.new('git_object **') + ffi.buffer(cptr)[:] = commit._pointer[:] + + err = C.git_describe_commit(result, cptr[0], options) + else: + err = C.git_describe_workdir(result, self._repo, options) + check_error(err) + + try: + format_options = ffi.new('git_describe_format_options *') + C.git_describe_init_format_options(format_options, C.GIT_DESCRIBE_FORMAT_OPTIONS_VERSION) + + if abbreviated_size is not None: + format_options.abbreviated_size = abbreviated_size + if always_use_long_format is not None: + format_options.always_use_long_format = always_use_long_format + if dirty_suffix: + format_options.dirty_suffix = ffi.new('char[]', to_bytes(dirty_suffix)) + + buf = ffi.new('git_buf *', (ffi.NULL, 0)) + + err = C.git_describe_format(buf, result[0], format_options) + check_error(err) + + try: + return ffi.string(buf.ptr).decode('utf-8') + finally: + C.git_buf_free(buf) + finally: + C.git_describe_result_free(result[0]) + # # Utility for writing a tree into an archive # diff --git a/src/pygit2.c b/src/pygit2.c index 02ddcb9..47f9edb 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -381,6 +381,11 @@ moduleinit(PyObject* m) ADD_CONSTANT_INT(m, GIT_MERGE_ANALYSIS_FASTFORWARD) ADD_CONSTANT_INT(m, GIT_MERGE_ANALYSIS_UNBORN) + /* Describe */ + ADD_CONSTANT_INT(m, GIT_DESCRIBE_DEFAULT); + ADD_CONSTANT_INT(m, GIT_DESCRIBE_TAGS); + ADD_CONSTANT_INT(m, GIT_DESCRIBE_ALL); + /* Global initialization of libgit2 */ git_libgit2_init(); diff --git a/test/test_describe.py b/test/test_describe.py new file mode 100644 index 0000000..f4e2cd9 --- /dev/null +++ b/test/test_describe.py @@ -0,0 +1,106 @@ +# -*- coding: UTF-8 -*- +# +# Copyright 2010-2015 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. + +"""Tests for describing commits.""" + +from __future__ import absolute_import +from __future__ import unicode_literals +import unittest + +from pygit2 import GIT_DESCRIBE_DEFAULT, GIT_DESCRIBE_TAGS, GIT_DESCRIBE_ALL +import pygit2 +from . import utils + + +def add_tag(repo, name, target): + message = 'Example tag.\n' + tagger = pygit2.Signature('John Doe', 'jdoe@example.com', 12347, 0) + + sha = repo.create_tag(name, target, pygit2.GIT_OBJ_COMMIT, tagger, message) + return sha + + +class DescribeTest(utils.RepoTestCase): + + def test_describe(self): + add_tag(self.repo, 'thetag', '4ec4389a8068641da2d6578db0419484972284c8') + self.assertEqual('thetag-2-g2be5719', self.repo.describe()) + + def test_describe_without_ref(self): + self.assertRaises(pygit2.GitError, self.repo.describe) + + def test_describe_default_oid(self): + self.assertEqual('2be5719', self.repo.describe(show_commit_oid_as_fallback=True)) + + def test_describe_strategies(self): + self.assertEqual('heads/master', self.repo.describe(describe_strategy=GIT_DESCRIBE_ALL)) + + self.repo.create_reference('refs/tags/thetag', '4ec4389a8068641da2d6578db0419484972284c8') + self.assertRaises(KeyError, self.repo.describe) + self.assertEqual('thetag-2-g2be5719', self.repo.describe(describe_strategy=GIT_DESCRIBE_TAGS)) + + def test_describe_pattern(self): + add_tag(self.repo, 'private/tag1', '5ebeeebb320790caf276b9fc8b24546d63316533') + add_tag(self.repo, 'public/tag2', '4ec4389a8068641da2d6578db0419484972284c8') + + self.assertEqual('public/tag2-2-g2be5719', self.repo.describe(pattern='public/*')) + + def test_describe_committish(self): + add_tag(self.repo, 'thetag', 'acecd5ea2924a4b900e7e149496e1f4b57976e51') + self.assertEqual('thetag-4-g2be5719', self.repo.describe(committish='HEAD')) + self.assertEqual('thetag-1-g5ebeeeb', self.repo.describe(committish='HEAD^')) + + self.assertEqual('thetag-4-g2be5719', self.repo.describe(committish=self.repo.head)) + + self.assertEqual('thetag-1-g6aaa262', self.repo.describe(committish='6aaa262e655dd54252e5813c8e5acd7780ed097d')) + self.assertEqual('thetag-1-g6aaa262', self.repo.describe(committish='6aaa262')) + + def test_describe_follows_first_branch_only(self): + add_tag(self.repo, 'thetag', '4ec4389a8068641da2d6578db0419484972284c8') + self.assertRaises(KeyError, self.repo.describe, only_follow_first_parent=True) + + def test_describe_abbreviated_size(self): + add_tag(self.repo, 'thetag', '4ec4389a8068641da2d6578db0419484972284c8') + self.assertEqual('thetag-2-g2be5719152d4f82c', self.repo.describe(abbreviated_size=16)) + self.assertEqual('thetag', self.repo.describe(abbreviated_size=0)) + + def test_describe_long_format(self): + add_tag(self.repo, 'thetag', '2be5719152d4f82c7302b1c0932d8e5f0a4a0e98') + self.assertEqual('thetag-0-g2be5719', self.repo.describe(always_use_long_format=True)) + + +class DescribeDirtyWorkdirTest(utils.DirtyRepoTestCase): + + def setUp(self): + super(utils.DirtyRepoTestCase, self).setUp() + add_tag(self.repo, 'thetag', 'a763aa560953e7cfb87ccbc2f536d665aa4dff22') + + def test_describe(self): + self.assertEqual('thetag', self.repo.describe()) + + def test_describe_with_dirty_suffix(self): + self.assertEqual('thetag-dirty', self.repo.describe(dirty_suffix='-dirty')) From a5cfea21a71a95414525d2e97a7cbee0e4e43e3e Mon Sep 17 00:00:00 2001 From: Chason Chaffin Date: Tue, 15 Dec 2015 17:20:50 -0800 Subject: [PATCH 34/36] Fix broken binary diff test In a recent commit to libgit2, binary diffs were changed to have a trailing empty line. This broke a test in test_diff because it compares it directly against the string. I've added the extra line to the expected output and the test now passes correctly. --- test/test_diff.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/test_diff.py b/test/test_diff.py index 2b98e98..fff0c67 100644 --- a/test/test_diff.py +++ b/test/test_diff.py @@ -77,6 +77,7 @@ Pc${NM%FIhFs^kIy3n&7R literal 8 Pc${NM&PdElPvrst3ey5{ + """ DIFF_HEAD_TO_INDEX_EXPECTED = [ From daff45f2d408057056360005e929e5fe101e241e Mon Sep 17 00:00:00 2001 From: Chris Rebert Date: Tue, 22 Dec 2015 12:23:59 -0700 Subject: [PATCH 35/36] Document that Diff.patch can be None; fixes #467 --- src/diff.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/diff.c b/src/diff.c index e0e57bc..c48fda3 100644 --- a/src/diff.c +++ b/src/diff.c @@ -482,7 +482,8 @@ Diff_len(Diff *self) return (Py_ssize_t)git_diff_num_deltas(self->diff); } -PyDoc_STRVAR(Diff_patch__doc__, "Patch diff string."); +PyDoc_STRVAR(Diff_patch__doc__, + "Patch diff string. Can be None in some cases, such as empty commits."); PyObject * Diff_patch__get__(Diff *self) From fa60e2233d16d6b27640c27d0ba62a410d548c70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=2E=20David=20Ib=C3=A1=C3=B1ez?= Date: Fri, 1 Jan 2016 19:18:34 +0100 Subject: [PATCH 36/36] Release 0.23.3 --- README.rst | 51 +++++++++++++++++++++++++++++++++++------------- docs/conf.py | 2 +- docs/general.rst | 10 +++++----- docs/install.rst | 28 +++++++++++++------------- pygit2/_build.py | 2 +- 5 files changed, 58 insertions(+), 35 deletions(-) diff --git a/README.rst b/README.rst index 4bdf294..49e1414 100644 --- a/README.rst +++ b/README.rst @@ -25,6 +25,28 @@ How to install Changelog ============== +0.23.3 (2016-01-01) +------------------------- + +- New ``Repository.create_blob_fromiobase(...)`` + `#490 `_ + `#577 `_ + +- New ``Repository.describe(...)`` + `#585 `_ + +- Fix ``Signature`` default encoding, UTF-8 now + `#581 `_ + +- Fixing ``pip install pygit2``, should install cffi first + +- Unit tests, fix binary diff test + `#586 `_ + +- Document that ``Diff.patch`` can be ``None`` + `#587 `_ + + 0.23.2 (2015-10-11) ------------------------- @@ -726,7 +748,7 @@ Other: `#331 `_ Authors ============== -97 developers have contributed at least 1 commit to pygit2:: +102 developers have contributed at least 1 commit to pygit2:: J. David Ibáñez Carlos Martín Nieto Nico von Geyso W. Trevor King Dave Borowitz Daniel Rodríguez Troitiño @@ -736,31 +758,32 @@ Authors Petr Hosek Victor Garcia Xavier Delannoy Yonggang Luo Patrick Steinhardt Valentin Haenel Michael Jones Bernardo Heynemann John Szakmeister - Vlad Temian Brodie Rao David Versmisse - Rémi Duraffort Santiago Perez De Rosso Sebastian Thiel - Alok Singhal Fraser Tweedale Han-Wen Nienhuys - Leonardo Rhodes Nicolas Dandrimont Petr Viktorin + Vlad Temian Brodie Rao Nicolas Dandrimont + David Versmisse Rémi Duraffort Santiago Perez De Rosso + Sebastian Thiel Alok Singhal Fraser Tweedale + Han-Wen Nienhuys Leonardo Rhodes Petr Viktorin Ron Cohen Thomas Kluyver Alex Chamberlain Alexander Bayandin Amit Bakshi Andrey Devyatkin Arno van Lumig Ben Davis Eric Schrijver Greg Fitzgerald Hervé Cauwelier Huang Huang Ian P. McCullough Jack O'Connor Jared Flatow Jiunn Haur Lim Jun Omae Kaarel Kitsemets - Kevin KIN-FOO Michael Sondergaard Sarath Lakshman - Vicent Marti Zoran Zaric Adam Spiers - Andrew Chin András Veres-Szentkirályi Ash Berlin - Benjamin Kircher Benjamin Pollack Bryan O'Sullivan + Kevin KIN-FOO Masud Rahman Michael Sondergaard + Sarath Lakshman Vicent Marti Zoran Zaric + Adam Spiers Andrew Chin András Veres-Szentkirályi + Ash Berlin Benjamin Kircher Benjamin Pollack + Bryan O'Sullivan Chason Chaffin Chris Rebert Colin Watson Daniel Bruce David Fischer David Sanders David Six Devaev Maxim Eric Davis Erik Meusel Erik van Zijst Ferengee Guille -bisho- Gustavo Di Pietro Holger Frey Hugh Cole-Baker Jasper Lievisse Adriaanse Josh Bleecher Snyder Justin Clift Kyriakos Oikonomakos - Lukas Fleischer Mathieu Bridon Óscar San José - Peter Dave Hello Philippe Ombredanne Ridge Kennedy - Ross Nicoll Rui Abreu Ferreira Sheeo - Soasme Vladimir Rutsky chengyuhang - earl + Lukas Fleischer Mathieu Bridon Nicolás Sanguinetti + Noah Fontes Óscar San José Peter Dave Hello + Philippe Ombredanne Ridge Kennedy Ross Nicoll + Rui Abreu Ferreira Sheeo Soasme + Vladimir Rutsky chengyuhang earl License diff --git a/docs/conf.py b/docs/conf.py index fba3b66..b569356 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -52,7 +52,7 @@ copyright = u'2010-2015 The pygit2 contributors' # The short X.Y version. version = '0.23' # The full version, including alpha/beta/rc tags. -release = '0.23.2' +release = '0.23.3' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. diff --git a/docs/general.rst b/docs/general.rst index 59a8925..c54678b 100644 --- a/docs/general.rst +++ b/docs/general.rst @@ -18,7 +18,7 @@ library that has been built against. The version number has a .. py:data:: LIBGIT2_VER_MAJOR Integer value of the major version number. For example, for the version - ``0.23.2``:: + ``0.23.3``:: >>> print LIBGIT2_VER_MAJOR 0 @@ -26,7 +26,7 @@ library that has been built against. The version number has a .. py:data:: LIBGIT2_VER_MINOR Integer value of the minor version number. For example, for the version - ``0.23.2``:: + ``0.23.3``:: >>> print LIBGIT2_VER_MINOR 23 @@ -34,17 +34,17 @@ library that has been built against. The version number has a .. py:data:: LIBGIT2_VER_REVISION Integer value of the revision version number. For example, for the version - ``0.23.2``:: + ``0.23.3``:: >>> print LIBGIT2_VER_REVISION - 2 + 3 .. py:data:: LIBGIT2_VERSION The libgit2 version number as a string:: >>> print LIBGIT2_VERSION - '0.23.2' + '0.23.3' Errors ====== diff --git a/docs/install.rst b/docs/install.rst index 55382b4..84f1d65 100644 --- a/docs/install.rst +++ b/docs/install.rst @@ -41,11 +41,11 @@ while the last number |lq| *.micro* |rq| auto-increments independently. As illustration see this table of compatible releases: -+-----------+--------------------------------+--------------------------------+ -|**libgit2**| 0.23.0, 0.23.1, 0.23.2, 0.23.3 | 0.22.0, 0.22.1, 0.22.2, 0.22.3 | -+-----------+--------------------------------+--------------------------------+ -|**pygit2** | 0.23.0, 0.23.1, 0.23.2 | 0.22.0, 0.22.1 | -+-----------+--------------------------------+--------------------------------+ ++-----------+----------------------------------------+--------------------------------+ +|**libgit2**| 0.23.0, 0.23.1, 0.23.2, 0.23.3, 0.23.4 | 0.22.0, 0.22.1, 0.22.2, 0.22.3 | ++-----------+----------------------------------------+--------------------------------+ +|**pygit2** | 0.23.0, 0.23.1, 0.23.2, 0.23.3 | 0.22.0, 0.22.1 | ++-----------+----------------------------------------+--------------------------------+ .. warning:: @@ -62,9 +62,9 @@ directory, do: .. code-block:: sh - $ wget https://github.com/libgit2/libgit2/archive/v0.23.2.tar.gz - $ tar xzf v0.23.2.tar.gz - $ cd libgit2-0.23.2/ + $ wget https://github.com/libgit2/libgit2/archive/v0.23.4.tar.gz + $ tar xzf v0.23.4.tar.gz + $ cd libgit2-0.23.4/ $ cmake . $ make $ sudo make install @@ -146,9 +146,9 @@ Install libgit2 (see we define the installation prefix): .. code-block:: sh - $ wget https://github.com/libgit2/libgit2/archive/v0.23.2.tar.gz - $ tar xzf v0.23.2.tar.gz - $ cd libgit2-0.23.2/ + $ wget https://github.com/libgit2/libgit2/archive/v0.23.4.tar.gz + $ tar xzf v0.23.4.tar.gz + $ cd libgit2-0.23.4/ $ cmake . -DCMAKE_INSTALL_PREFIX=$LIBGIT2 $ make $ make install @@ -201,9 +201,9 @@ from a bash shell: .. code-block:: sh $ export LIBGIT2=C:/Dev/libgit2 - $ wget https://github.com/libgit2/libgit2/archive/v0.23.2.tar.gz - $ tar xzf v0.23.2.tar.gz - $ cd libgit2-0.23.2/ + $ wget https://github.com/libgit2/libgit2/archive/v0.23.4.tar.gz + $ tar xzf v0.23.4.tar.gz + $ cd libgit2-0.23.4/ $ cmake . -DSTDCALL=OFF -DCMAKE_INSTALL_PREFIX=$LIBGIT2 -G "Visual Studio 9 2008" $ cmake --build . --config release --target install $ ctest -v diff --git a/pygit2/_build.py b/pygit2/_build.py index c430d5e..7f679dc 100644 --- a/pygit2/_build.py +++ b/pygit2/_build.py @@ -37,7 +37,7 @@ from os import getenv # # The version number of pygit2 # -__version__ = '0.23.2' +__version__ = '0.23.3' #