Merge tag '0.23.3' into debian/unstable

This commit is contained in:
Thomas Goirand
2016-02-06 09:53:43 +00:00
94 changed files with 1088 additions and 475 deletions

View File

@@ -6,8 +6,8 @@ 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
pypy.
implements Git plumbing. Pygit2 works with Python 2.7, 3.2, 3.3, 3.4 and
PyPy 2.6
Links:
@@ -25,6 +25,91 @@ How to install
Changelog
==============
0.23.3 (2016-01-01)
-------------------------
- New ``Repository.create_blob_fromiobase(...)``
`#490 <https://github.com/libgit2/pygit2/pull/490>`_
`#577 <https://github.com/libgit2/pygit2/pull/577>`_
- New ``Repository.describe(...)``
`#585 <https://github.com/libgit2/pygit2/pull/585>`_
- Fix ``Signature`` default encoding, UTF-8 now
`#581 <https://github.com/libgit2/pygit2/issues/581>`_
- Fixing ``pip install pygit2``, should install cffi first
- Unit tests, fix binary diff test
`#586 <https://github.com/libgit2/pygit2/pull/586>`_
- Document that ``Diff.patch`` can be ``None``
`#587 <https://github.com/libgit2/pygit2/pull/587>`_
0.23.2 (2015-10-11)
-------------------------
- Unify callbacks system for remotes and clone
`#568 <https://github.com/libgit2/pygit2/pull/568>`_
- New ``TreeEntry._name``
`#570 <https://github.com/libgit2/pygit2/pull/570>`_
- Fix segfault in ``Tag._message``
`#572 <https://github.com/libgit2/pygit2/pull/572>`_
- Documentation improvements
`#569 <https://github.com/libgit2/pygit2/pull/569>`_
`#574 <https://github.com/libgit2/pygit2/pull/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)
-------------------------
- Improve support for cffi 1.0+
`#529 <https://github.com/libgit2/pygit2/pull/529>`_
`#561 <https://github.com/libgit2/pygit2/pull/561>`_
- Fix ``Remote.push``
`#557 <https://github.com/libgit2/pygit2/pull/557>`_
- New ``TreeEntry.type``
`#560 <https://github.com/libgit2/pygit2/pull/560>`_
- New ``pygit2.GIT_DIFF_SHOW_BINARY``
`#566 <https://github.com/libgit2/pygit2/pull/566>`_
0.23.0 (2015-08-14)
-------------------------
@@ -326,7 +411,7 @@ Other changes:
`#380 <https://github.com/libgit2/pygit2/issues/380>`_
`#407 <https://github.com/libgit2/pygit2/pull/407>`_
- Add support for pypy3
- Add support for PyPy3
`#422 <https://github.com/libgit2/pygit2/pull/422>`_
- Documentation improvements
@@ -490,7 +575,7 @@ Other:
0.20.2 (2014-02-04)
-------------------
- Support pypy
- Support PyPy
`#209 <https://github.com/libgit2/pygit2/issues/209>`_
`#327 <https://github.com/libgit2/pygit2/pull/327>`_
`#333 <https://github.com/libgit2/pygit2/pull/333>`_
@@ -663,38 +748,41 @@ Other: `#331 <https://github.com/libgit2/pygit2/pull/331>`_
Authors
==============
93 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
W. Trevor King Dave Borowitz Daniel Rodríguez Troitio
Richo Healey Christian Boos Julien Miotte
Richard Möhn Xu Tao Jose Plana
Matthew Duggan Matthew Gamble Martin Lenders
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 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
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 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 Colin Watson Daniel Bruce
David Fischer David Sanders Devaev Maxim
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 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é
Peter Dave Hello Philippe Ombredanne Ridge Kennedy
Ross Nicoll Rui Abreu Ferreira Soasme
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 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

View File

@@ -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
@@ -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.3'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

View File

@@ -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

View File

@@ -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.3``::
>>> 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.3``::
>>> 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.3``::
>>> print LIBGIT2_VER_REVISION
0
3
.. py:data:: LIBGIT2_VERSION
The libgit2 version number as a string::
>>> print LIBGIT2_VERSION
'0.23.0'
'0.23.3'
Errors
======

View File

@@ -13,11 +13,17 @@ 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+
- Libssh2, optional, used for SSH network operations.
- pkg-config, optional, used for SSH network operations.
- cffi 1.0+
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.
.. warning::
@@ -35,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.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.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::
@@ -56,9 +62,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.4.tar.gz
$ tar xzf v0.23.4.tar.gz
$ cd libgit2-0.23.4/
$ cmake .
$ make
$ sudo make install
@@ -140,9 +146,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.4.tar.gz
$ tar xzf v0.23.4.tar.gz
$ cd libgit2-0.23.4/
$ cmake . -DCMAKE_INSTALL_PREFIX=$LIBGIT2
$ make
$ make install
@@ -195,9 +201,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.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

View File

@@ -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:
@@ -176,6 +177,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 +190,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

View File

@@ -21,6 +21,12 @@ The Remote type
.. autoclass:: pygit2.Remote
:members:
The RemoteCallbacks type
========================
.. autoclass:: pygit2.RemoteCallbacks
:members:
The TransferProgress type
===========================
@@ -42,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

View File

@@ -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

View File

@@ -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,
@@ -35,15 +35,15 @@ 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
from .utils import to_bytes, to_str
from ._utils import __version__
from ._build import __version__
# Features
@@ -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)

View File

@@ -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,
@@ -26,23 +26,18 @@
# 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 setup.py at build time.
But also used by pygit2 at run time.
"""
# Import from the Standard Library
from binascii import crc32
import codecs
import os
from os import getenv
from os.path import abspath, dirname
import sys
#
# The version number of pygit2
#
__version__ = '0.23.0'
__version__ = '0.23.3'
#
@@ -67,44 +62,3 @@ def get_libgit2_paths():
os.path.join(libgit2_path, 'include'),
getenv('LIBGIT2_LIB', os.path.join(libgit2_path, 'lib')),
)
#
# Loads the cffi extension
#
def get_ffi():
import cffi
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))
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())
# The modulename
# Simplified version of what cffi does: remove kwargs and vengine
preamble = "#include <git2.h>"
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)
# 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])
# Ok
return ffi, C

76
pygit2/_run.py Normal file
View File

@@ -0,0 +1,76 @@
# -*- 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.
"""
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.path import abspath, dirname
import sys
# Import from cffi
from cffi import FFI
# Import from pygit2
from _build import get_libgit2_paths
# C_HEADER_SRC
if getattr(sys, 'frozen', False):
dir_path = getattr(sys, '_MEIPASS', None)
if dir_path is None:
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:
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])
# preamble
preamble = "#include <git2.h>"
# 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()

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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 ...

View File

@@ -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,
@@ -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")

View File

@@ -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,
@@ -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 ._run import ffi, preamble, C_KEYWORDS
C = ffi.verify(preamble, **C_KEYWORDS)

View File

@@ -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,
@@ -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 <Diff> 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)
#

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,
@@ -30,9 +30,8 @@ from __future__ import absolute_import
# Import from pygit2
from _pygit2 import Oid
from .errors import check_error, GitError
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
@@ -71,7 +70,27 @@ 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 __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
@@ -80,7 +99,6 @@ class Remote(object):
:param str string: Progress output from the remote
"""
pass
def credentials(self, url, username_from_url, allowed_types):
"""Credentials callback
@@ -100,7 +118,26 @@ class Remote(object):
Return value: credential
"""
pass
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
@@ -109,7 +146,6 @@ class Remote(object):
:param TransferProgress stats: The progress up to now
"""
pass
def update_tips(self, refname, old, new):
"""Update tips callabck
@@ -131,6 +167,162 @@ class Remote(object):
:param str messsage: rejection message from the remote. If None, the update was accepted.
"""
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
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
self._stored_exception = None
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
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)
push_opts.callbacks.payload = self._self_handle
# These functions exist to be called by the git_remote as
# callbacks. They proxy the call to whatever the user set
@ffi.callback('git_transfer_progress_cb')
def _transfer_progress_cb(stats_ptr, data):
self = ffi.from_handle(data)
transfer_progress = getattr(self, 'transfer_progress', None)
if not transfer_progress:
return 0
try:
transfer_progress(TransferProgress(stats_ptr))
except Exception as e:
self._stored_exception = e
return C.GIT_EUSER
return 0
@ffi.callback('git_transport_message_cb')
def _sideband_progress_cb(string, length, data):
self = ffi.from_handle(data)
progress = getattr(self, 'progress', None)
if not progress:
return 0
try:
s = ffi.string(string, length).decode()
progress(s)
except Exception as e:
self._stored_exception = e
return C.GIT_EUSER
return 0
@ffi.callback('int (*update_tips)(const char *refname, const git_oid *a,'
'const git_oid *b, void *data)')
def _update_tips_cb(refname, a, b, data):
self = ffi.from_handle(data)
update_tips = getattr(self, 'update_tips', None)
if not update_tips:
return 0
try:
s = maybe_string(refname)
a = Oid(raw=bytes(ffi.buffer(a)[:]))
b = Oid(raw=bytes(ffi.buffer(b)[:]))
update_tips(s, a, b)
except Exception as e:
self._stored_exception = e
return C.GIT_EUSER
return 0
@ffi.callback("int (*push_update_reference)(const char *ref, const char *msg, void *data)")
def _push_update_reference_cb(ref, msg, data):
self = ffi.from_handle(data)
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)
push_update_reference(refname, message)
except Exception as e:
self._stored_exception = e
return C.GIT_EUSER
return 0
@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, allowed, data):
self = ffi.from_handle(data)
credentials = getattr(self, 'credentials', None)
if not credentials:
return 0
try:
ccred = get_credentials(credentials, url, username, allowed)
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
@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
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 = 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"""
@@ -165,7 +357,7 @@ class Remote(object):
err = C.git_remote_save(self._remote)
check_error(err)
def fetch(self, refspecs=None, message=None):
def fetch(self, refspecs=None, message=None, callbacks=None):
"""Perform a fetch against this remote. Returns a <TransferProgress>
object.
"""
@@ -173,24 +365,19 @@ class Remote(object):
fetch_opts = ffi.new('git_fetch_options *')
err = C.git_fetch_init_options(fetch_opts, C.GIT_FETCH_OPTIONS_VERSION)
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
fetch_opts.callbacks.credentials = self._credentials_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
if callbacks is None:
callbacks = RemoteCallbacks()
self._stored_exception = None
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 self._stored_exception:
raise self._stored_exception
if callbacks._stored_exception:
raise callbacks._stored_exception
check_error(err)
finally:
self._self_handle = None
callbacks._self_handle = None
return TransferProgress(C.git_remote_stats(self._remote))
@@ -225,7 +412,7 @@ class Remote(object):
return strarray_to_strings(specs)
def push(self, specs):
def push(self, specs, callbacks=None):
"""Push the given refspec to the remote. Raises ``GitError`` on
protocol error or unpack failure.
@@ -234,114 +421,18 @@ class Remote(object):
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
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
push_opts.callbacks.credentials = self._credentials_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)
push_opts.callbacks.payload = self._self_handle
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
# These functions exist to be called by the git_remote as
# callbacks. They proxy the call to whatever the user set
@ffi.callback('git_transfer_progress_cb')
def _transfer_progress_cb(stats_ptr, data):
self = ffi.from_handle(data)
if not hasattr(self, 'transfer_progress') \
or not self.transfer_progress:
return 0
try:
self.transfer_progress(TransferProgress(stats_ptr))
except Exception as e:
self._stored_exception = e
return C.GIT_EUSER
return 0
@ffi.callback('git_transport_message_cb')
def _sideband_progress_cb(string, length, data):
self = ffi.from_handle(data)
if not hasattr(self, 'progress') or not self.progress:
return 0
try:
s = ffi.string(string, length).decode()
self.progress(s)
except Exception as e:
self._stored_exception = e
return C.GIT_EUSER
return 0
@ffi.callback('int (*update_tips)(const char *refname, const git_oid *a,'
'const git_oid *b, void *data)')
def _update_tips_cb(refname, a, b, data):
self = ffi.from_handle(data)
if not hasattr(self, 'update_tips') or not self.update_tips:
return 0
try:
s = maybe_string(refname)
a = Oid(raw=bytes(ffi.buffer(a)[:]))
b = Oid(raw=bytes(ffi.buffer(b)[:]))
self.update_tips(s, a, b)
except Exception as e:
self._stored_exception = e
return C.GIT_EUSER
return 0
@ffi.callback("int (*push_update_reference)(const char *ref, const char *msg, void *data)")
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:
return 0
try:
refname = ffi.string(ref)
message = maybe_string(msg)
self.push_update_reference(refname, message)
except Exception as e:
self._stored_exception = e
return C.GIT_EUSER
return 0
@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, allowed, data):
self = ffi.from_handle(data)
if not hasattr(self, 'credentials') or not self.credentials:
return 0
try:
ccred = get_credentials(self.credentials, url, username, allowed)
cred_out[0] = ccred[0]
except Exception as e:
self._stored_exception = e
return C.GIT_EUSER
return 0
callbacks._self_handle = None
def get_credentials(fn, url, username, allowed):
"""Call fn and return the credentials object"""
@@ -351,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:

View File

@@ -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,
@@ -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
#

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

115
setup.py
View File

@@ -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,
@@ -45,9 +45,20 @@ from subprocess import Popen, PIPE
import sys
import unittest
# Get cffi major version
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 _utils import __version__, get_libgit2_paths, get_ffi
from _build import __version__, get_libgit2_paths
if cffi_major_version == 0:
from _run import ffi, preamble, C_KEYWORDS
ffi.verify(preamble, **C_KEYWORDS)
del sys.path[0]
# Python 2 support
@@ -95,19 +106,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 +167,27 @@ 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)
# On Windows we package up the dlls with the plugin.
if os.name == 'nt':
cmdclass['build'] = BuildWithDLLs
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()
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
],
}
for line in stdoutdata.splitlines():
# Skip hidden files at the root
if line[0] == '.':
continue
self.filelist.append(line)
if cffi_major_version == 0:
extra_args['ext_modules'].append(ffi.verifier.get_extension())
else:
extra_args['cffi_modules']=['pygit2/_run.py:ffi']
# 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,
}
setup(name='pygit2',
description='Python bindings for libgit2.',
@@ -188,10 +204,5 @@ setup(name='pygit2',
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)

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,
@@ -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)

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,
@@ -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)
@@ -380,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();

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,
@@ -842,6 +842,68 @@ 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 -1;
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;
}
PyObject *
Repository_create_blob_fromiobase(Repository *self, PyObject *py_file)
{
git_oid oid;
PyObject *py_is_readable;
int is_readable;
int err;
py_is_readable = PyObject_CallMethod(py_file, "readable", NULL);
if (!py_is_readable) {
if (PyErr_ExceptionMatches(PyExc_AttributeError))
PyErr_SetObject(PyExc_TypeError, 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, &read_chunk,
py_file);
if (err < 0)
return Error_set(err);
return git_oid_to_python(&oid);
}
PyDoc_STRVAR(Repository_create_commit__doc__,
"create_commit(reference_name, author, committer, message, tree, parents[, encoding]) -> Oid\n"
"\n"
@@ -1547,6 +1609,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_O),
METHOD(Repository, create_commit, METH_VARARGS),
METHOD(Repository, create_tag, METH_VARARGS),
METHOD(Repository, TreeBuilder, METH_VARARGS),

View File

@@ -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,

View File

@@ -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,
@@ -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;

View File

@@ -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,

View File

@@ -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,
@@ -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[] = {

View File

@@ -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,

View File

@@ -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,
@@ -67,6 +67,24 @@ 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 *
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 *
@@ -168,9 +186,11 @@ 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),
GETTER(TreeEntry, type),
{NULL}
};

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

Binary file not shown.

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,
@@ -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,19 @@ class BlobTest(utils.RepoTestCase):
self.assertTrue(isinstance(blob, pygit2.Blob))
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]
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']

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,
@@ -70,32 +70,37 @@ 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):
@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")
remote.credentials = credentials_cb
url = "https://github.com/github/github"
remote = self.repo.create_remote("github", url)
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):
@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")
remote.credentials = credentials_cb
self.assertRaises(TypeError, remote.fetch)
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):
remote = self.repo.create_remote("bb", "https://bitbucket.org/libgit2/testgitrepository.git")
remote.credentials = UserPass("libgit2", "libgit2")
credentials = UserPass("libgit2", "libgit2")
callbacks = pygit2.RemoteCallbacks(credentials=credentials)
remote.fetch()
url = "https://bitbucket.org/libgit2/testgitrepository.git"
remote = self.repo.create_remote("bb", url)
remote.fetch(callbacks=callbacks)
if __name__ == '__main__':
unittest.main()

106
test/test_describe.py Normal file
View File

@@ -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'))

View File

@@ -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,
@@ -33,6 +33,7 @@ import unittest
import pygit2
from pygit2 import GIT_DIFF_INCLUDE_UNMODIFIED
from pygit2 import GIT_DIFF_IGNORE_WHITESPACE, GIT_DIFF_IGNORE_WHITESPACE_EOL
from pygit2 import GIT_DIFF_SHOW_BINARY
from pygit2 import GIT_DELTA_RENAMED
from . import utils
from itertools import chain
@@ -63,6 +64,22 @@ index 297efb8..0000000
-c/d contents
"""
PATCH_BINARY = """diff --git a/binary_file b/binary_file
index 86e5c10..b835d73 100644
Binary files a/binary_file and b/binary_file differ
"""
PATCH_BINARY_SHOW = """diff --git a/binary_file b/binary_file
index 86e5c1008b5ce635d3e3fffa4434c5eccd8f00b6..b835d73543244b6694f36a8c5dfdffb71b153db7 100644
GIT binary patch
literal 8
Pc${NM%FIhFs^kIy3n&7R
literal 8
Pc${NM&PdElPvrst3ey5{
"""
DIFF_HEAD_TO_INDEX_EXPECTED = [
'staged_changes',
'staged_changes_file_deleted',
@@ -308,5 +325,15 @@ class DiffTest(utils.BareRepoTestCase):
width=80)
self.assertEqual(STATS_EXPECTED, formatted)
class BinaryDiffTest(utils.BinaryFileRepoTestCase):
def test_binary_diff(self):
repo = self.repo
diff = repo.diff('HEAD', 'HEAD^')
self.assertEqual(PATCH_BINARY, diff.patch)
diff = repo.diff('HEAD', 'HEAD^', flags=GIT_DIFF_SHOW_BINARY)
self.assertEqual(PATCH_BINARY_SHOW, diff.patch)
if __name__ == '__main__':
unittest.main()

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,
@@ -212,31 +212,37 @@ class EmptyRepositoryTest(utils.EmptyRepoTestCase):
self.assertEqual(stats.received_objects, REMOTE_REPO_OBJECTS)
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):

View File

@@ -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,
@@ -514,10 +514,9 @@ class CloneRepositoryTest(utils.NoRepoTestCase):
self.assertIsNotNone(repo.remotes["custom_remote"])
def test_clone_with_credentials(self):
credentials = pygit2.UserPass("libgit2", "libgit2")
repo = clone_repository(
"https://bitbucket.org/libgit2/testgitrepository.git",
self._temp_dir, credentials=credentials)
self._temp_dir, callbacks=pygit2.RemoteCallbacks(credentials=pygit2.UserPass("libgit2", "libgit2")))
self.assertFalse(repo.is_empty)

View File

@@ -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,

View File

@@ -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,
@@ -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'

View File

@@ -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,

View File

@@ -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,

View File

@@ -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,
@@ -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
@@ -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))
@@ -89,6 +90,7 @@ class TreeTest(utils.BareRepoTestCase):
tree = self.repo[TREE_SHA]
subtree_entry = tree['c']
self.assertTreeEntryEqual(subtree_entry, SUBTREE_SHA, 'c', 0o0040000)
self.assertEqual(subtree_entry.type, 'tree')
subtree = self.repo[subtree_entry.id]
self.assertEqual(1, len(subtree))
@@ -100,21 +102,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):

View File

@@ -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,

View File

@@ -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,
@@ -160,6 +160,12 @@ class EmptyRepoTestCase(AutoRepoTestCase):
repo_spec = 'tar', 'emptyrepo'
class SubmoduleRepoTestCase(AutoRepoTestCase):
repo_spec = 'tar', 'submodulerepo'
class BinaryFileRepoTestCase(AutoRepoTestCase):
repo_spec = 'tar', 'binaryfilerepo'