Merge tag '0.22.0' into debian/unstable

This commit is contained in:
Thomas Goirand
2015-05-26 10:41:17 +02:00
29 changed files with 646 additions and 374 deletions

View File

@@ -2,7 +2,7 @@
cd ~
git clone --depth=1 -b v0.21.2 https://github.com/libgit2/libgit2.git
git clone --depth=1 -b maint/v0.22 https://github.com/libgit2/libgit2.git
cd libgit2/
mkdir build && cd build

View File

@@ -2,7 +2,7 @@
pygit2 - libgit2 bindings in Python
######################################################################
.. image:: https://secure.travis-ci.org/libgit2/pygit2.png
.. image:: https://secure.travis-ci.org/libgit2/pygit2.svg
:target: http://travis-ci.org/libgit2/pygit2
Pygit2 is a set of Python bindings to the libgit2 shared library, libgit2
@@ -25,6 +25,62 @@ How to install
Changelog
==============
0.22.0 (2015-01-16)
-------------------
New:
- Update to libgit2 v0.22
`#459 <https://github.com/libgit2/pygit2/pull/459>`_
- Add support for libgit2 feature detection
(new ``pygit2.features`` and ``pygit2.GIT_FEATURE_*``)
`#475 <https://github.com/libgit2/pygit2/pull/475>`_
- New ``Repository.remotes`` (``RemoteCollection``)
`#447 <https://github.com/libgit2/pygit2/pull/447>`_
API Changes:
- Prototype of ``clone_repository`` changed, check documentation
- Removed ``clone_into``, use ``clone_repository`` with callbacks instead
- Use ``Repository.remotes.rename(name, new_name)`` instead of
``Remote.rename(new_name)``
- Use ``Repository.remotes.delete(name)`` instead of ``Remote.delete()``
- Now ``Remote.push(...)`` takes a list of refspecs instead of just one
- Change ``Patch.old_id``, ``Patch.new_id``, ``Note.annotated_id``,
``RefLogEntry.oid_old`` and ``RefLogEntry.oid_new`` to be ``Oid`` objects
instead of strings
`#449 <https://github.com/libgit2/pygit2/pull/449>`_
Other:
- Fix ``init_repository`` when passing optional parameters ``workdir_path``,
``description``, ``template_path``, ``initial_head`` or ``origin_url``
`#466 <https://github.com/libgit2/pygit2/issues/466>`_
`#471 <https://github.com/libgit2/pygit2/pull/471>`_
- Fix use-after-free when patch outlives diff
`#457 <https://github.com/libgit2/pygit2/issues/457>`_
`#461 <https://github.com/libgit2/pygit2/pull/461>`_
`#474 <https://github.com/libgit2/pygit2/pull/474>`_
- Documentation improvements
`#456 <https://github.com/libgit2/pygit2/issues/456>`_
`#462 <https://github.com/libgit2/pygit2/pull/462>`_
`#465 <https://github.com/libgit2/pygit2/pull/465>`_
`#472 <https://github.com/libgit2/pygit2/pull/472>`_
`#473 <https://github.com/libgit2/pygit2/pull/473>`_
- Make the GPL exception explicit in setup.py
`#450 <https://github.com/libgit2/pygit2/pull/450>`_
0.21.4 (2014-11-04)
-------------------
@@ -446,7 +502,7 @@ Other: `#331 <https://github.com/libgit2/pygit2/pull/331>`_
Authors
==============
77 developers have contributed at least 1 commit to pygit2::
83 developers have contributed at least 1 commit to pygit2::
J. David Ibáñez Sebastian Thiel András Veres-Szentkirályi
Carlos Martín Nieto Fraser Tweedale Ash Berlin

View File

@@ -50,9 +50,9 @@ copyright = u'2010-2014 The pygit2 contributors'
# built documents.
#
# The short X.Y version.
version = '0.21'
version = '0.22'
# The full version, including alpha/beta/rc tags.
release = '0.21.4'
release = '0.22.0'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

8
docs/features.rst Normal file
View File

@@ -0,0 +1,8 @@
**********************************************************************
Feature detection
**********************************************************************
.. py:data:: pygit2.features
This variable contains a combination of `GIT_FEATURE_*` flags,
indicating which features a particular build of libgit2 supports.

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.21.2``::
``0.22.0``::
>>> 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.21.2``::
``0.22.0``::
>>> print LIBGIT2_VER_MINOR
21
22
.. py:data:: LIBGIT2_VER_REVISION
Integer value of the revision version number. For example, for the version
``0.21.2``::
``0.22.0``::
>>> print LIBGIT2_VER_REVISION
1
0
.. py:data:: LIBGIT2_VERSION
The libgit2 version number as a string::
>>> print LIBGIT2_VERSION
'0.21.2'
'0.22.0'
Errors
======

View File

@@ -47,6 +47,7 @@ Usage guide:
remotes
blame
settings
features
Indices and tables

View File

@@ -14,8 +14,9 @@ Requirements
============
- Python 2.7, 3.2+ or pypy (including the development headers)
- Libgit2 v0.21.1+
- Libgit2 v0.22.x
- cffi 0.8.1+
- Libssh2, optional, used for SSH network operations.
.. warning::
@@ -33,11 +34,11 @@ while the last number |lq| *.micro* |rq| auto-increments independently.
As illustration see this table of compatible releases:
+-----------+---------------------------------------+------------------------------+--------------+
|**libgit2**|0.21.1, 0.21.2 |0.20.0 |0.19.0 |
+-----------+---------------------------------------+------------------------------+--------------+
|**pygit2** |0.21.0, 0.21.1, 0.21.2, 0.21.3, 0.21.4 |0.20.0, 0.20.1, 0.20.2, 0.20.3|0.19.0, 0.19.1|
+-----------+---------------------------------------+------------------------------+--------------+
+-----------+--------+----------------------------------------+-------------------------------+
|**libgit2**| 0.22.0 | 0.21.1, 0.21.2 |0.20.0 |
+-----------+--------+----------------------------------------+-------------------------------+
|**pygit2** | 0.22.0 | 0.21.0, 0.21.1, 0.21.2, 0.21.3, 0.21.4 | 0.20.0, 0.20.1, 0.20.2, 0.20.3|
+-----------+--------+----------------------------------------+-------------------------------+
.. warning::
@@ -54,9 +55,9 @@ directory, do:
.. code-block:: sh
$ wget https://github.com/libgit2/libgit2/archive/v0.21.2.tar.gz
$ tar xzf v0.21.2.tar.gz
$ cd libgit2-0.21.2/
$ wget https://github.com/libgit2/libgit2/archive/v0.22.0.tar.gz
$ tar xzf v0.22.0.tar.gz
$ cd libgit2-0.22.0/
$ cmake .
$ make
$ sudo make install
@@ -138,9 +139,9 @@ Install libgit2 (see we define the installation prefix):
.. code-block:: sh
$ wget https://github.com/libgit2/libgit2/archive/v0.21.2.tar.gz
$ tar xzf v0.21.2.tar.gz
$ cd libgit2-0.21.2/
$ wget https://github.com/libgit2/libgit2/archive/v0.22.0.tar.gz
$ tar xzf v0.22.0.tar.gz
$ cd libgit2-0.22.0/
$ cmake . -DCMAKE_INSTALL_PREFIX=$LIBGIT2
$ make
$ make install
@@ -193,9 +194,9 @@ from a bash shell:
.. code-block:: sh
$ export LIBGIT2=C:/Dev/libgit2
$ wget https://github.com/libgit2/libgit2/archive/v0.21.2.tar.gz
$ tar xzf v0.21.2.tar.gz
$ cd libgit2-0.21.2/
$ wget https://github.com/libgit2/libgit2/archive/v0.22.0.tar.gz
$ tar xzf v0.22.0.tar.gz
$ cd libgit2-0.22.0/
$ cmake . -DSTDCALL=OFF -DCMAKE_INSTALL_PREFIX=$LIBGIT2 -G "Visual Studio 9 2008"
$ cmake --build . --config release --target install
$ ctest -v

View File

@@ -17,7 +17,6 @@ Main porcelain commands
.. toctree::
:maxdepth: 1
git-branch (List, create, or delete branches.) <recipes/git-branch>
git-init (Create an empty git repository or reinitialize an existing one.) <recipes/git-init>
git-log (Show commit logs.) <recipes/git-log>
git-show (Show various types of objects.) <recipes/git-show>

View File

@@ -1,30 +0,0 @@
**********************************************************************
git-branch
**********************************************************************
----------------------------------------------------------------------
Listing branches
----------------------------------------------------------------------
======================================================================
List all branches
======================================================================
.. code-block:: bash
$> git branch
.. code-block:: python
>>> regex = re.compile('^refs/heads/')
>>> branches = filter(lambda r: regex.match(r), repo.listall_references())
`Note that the next release will probably allow` ``repo.listall_branches()``.
----------------------------------------------------------------------
References
----------------------------------------------------------------------
- git-branch_.
.. _git-branch: https://www.kernel.org/pub/software/scm/git/docs/git-branch.html

View File

@@ -2,10 +2,18 @@
Remotes
**********************************************************************
.. py:attribute:: Repository.remotes
The collection of configured remotes, an instance of
:py:class:`pygit2.remote.RemoteCollection`
.. autoattribute:: pygit2.Repository.remotes
.. automethod:: pygit2.Repository.create_remote
The remote collection
==========================
.. autoclass:: pygit2.remote.RemoteCollection
:members:
The Remote type
====================
@@ -24,6 +32,10 @@ This class contains the data which is available to us during a fetch.
The Refspec type
===================
Refspecs objects are not constructed directly, but returned by
:meth:`pygit2.Remote.get_refspec`. To create a new a refspec on a Remote, use
:meth:`pygit2.Remote.add_fetch` or :meth:`pygit2.Remote.add_push`.
.. autoclass:: pygit2.refspec.Refspec
:members:

View File

@@ -41,7 +41,7 @@ from .index import Index, IndexEntry
from .remote import Remote, get_credentials
from .repository import Repository
from .settings import Settings
from .utils import to_bytes
from .utils import to_bytes, to_str
from ._utils import __version__
@@ -86,11 +86,26 @@ def init_repository(path, bare=False,
C.git_repository_init_init_options(options, C.GIT_REPOSITORY_INIT_OPTIONS_VERSION)
options.flags = flags
options.mode = mode
options.workdir_path = to_bytes(workdir_path)
options.description = to_bytes(description)
options.template_path = to_bytes(template_path)
options.initial_head = to_bytes(initial_head)
options.origin_url = to_bytes(origin_url)
if workdir_path:
workdir_path_ref = ffi.new('char []', to_bytes(workdir_path))
options.workdir_path = workdir_path_ref
if description:
description_ref = ffi.new('char []', to_bytes(description))
options.description = description_ref
if template_path:
template_path_ref = ffi.new('char []', to_bytes(template_path))
options.template_path = template_path_ref
if initial_head:
initial_head_ref = ffi.new('char []', to_bytes(initial_head))
options.initial_head = initial_head_ref
if origin_url:
origin_url_ref = ffi.new('char []', to_bytes(origin_url))
options.origin_url = origin_url_ref
# Call
crepository = ffi.new('git_repository **')
@@ -98,7 +113,7 @@ def init_repository(path, bare=False,
check_error(err)
# Ok
return Repository(path)
return Repository(to_str(path))
@ffi.callback('int (*credentials)(git_cred **cred, const char *url,'
@@ -108,7 +123,7 @@ def _credentials_cb(cred_out, url, username_from_url, allowed, data):
d = ffi.from_handle(data)
try:
ccred = get_credentials(d['callback'], url, username_from_url, allowed)
ccred = get_credentials(d['credentials_cb'], url, username_from_url, allowed)
cred_out[0] = ccred[0]
except Exception as e:
d['exception'] = e
@@ -116,10 +131,54 @@ def _credentials_cb(cred_out, url, username_from_url, allowed, data):
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):
d = ffi.from_handle(data)
try:
repository = d['repository_cb'](ffi.string(path), bare != 0)
# we no longer own the C object
repository._disown()
repo_out[0] = repository._repo
except Exception as e:
d['exception'] = e
return C.GIT_EUSER
return 0
@ffi.callback('int (*git_remote_create_cb)(git_remote **out, git_repository *repo,'
'const char *name, const char *url, void *payload)')
def _remote_create_cb(remote_out, repo, name, url, data):
d = ffi.from_handle(data)
try:
remote = d['remote_cb'](Repository._from_c(repo, False), ffi.string(name), ffi.string(url))
remote_out[0] = remote._remote
# we no longer own the C object
remote._remote = ffi.NULL
except Exception as e:
d['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):
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, ignore_cert_errors=False,
remote_name="origin", checkout_branch=None, credentials=None):
url, path, bare=False, repository=None, remote=None,
checkout_branch=None, credentials=None, certificate=None):
"""Clones a new Git repository from *url* in the given *path*.
Returns a Repository class pointing to the newly cloned repository.
@@ -130,7 +189,9 @@ def clone_repository(
:param bool bare: Whether the local repository should be bare
:param str remote_name: Name to give the remote at *url*.
:param callable remote: Callback for the remote to use.
:param callable repository: Callback for the repository to use.
:param str checkout_branch: Branch to checkout after the
clone. The default is to use the remote's default branch.
@@ -138,8 +199,22 @@ def clone_repository(
:param callable credentials: authentication to use if the remote
requires it
:param callable certificate: callback to verify the host's
certificate or fingerprint.
:rtype: Repository
The repository callback has `(path, bare) -> Repository` as a
signature. The Repository it returns will be used instead of
creating a new one.
The remote callback has `(Repository, name, url) -> Remote` as a
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.
"""
opts = ffi.new('git_clone_options *')
@@ -149,7 +224,10 @@ def clone_repository(
# Data, let's use a dict as we don't really want much more
d = {}
d['callback'] = credentials
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
@@ -161,48 +239,36 @@ def clone_repository(
checkout_branch_ref = ffi.new('char []', to_bytes(branch))
opts.checkout_branch = checkout_branch_ref
remote_name_ref = ffi.new('char []', to_bytes(remote_name))
opts.remote_name = remote_name_ref
if repository:
opts.repository_cb = _repository_create_cb
opts.repository_cb_payload = d_handle
if remote:
opts.remote_cb = _remote_create_cb
opts.remote_cb_payload = d_handle
opts.ignore_cert_errors = ignore_cert_errors
opts.bare = bare
if credentials:
opts.remote_callbacks.credentials = _credentials_cb
opts.remote_callbacks.payload = d_handle
if certificate:
opts.remote_callbacks.certificate_check = _certificate_cb
opts.remote_callbacks.payload = d_handle
err = C.git_clone(crepo, to_bytes(url), to_bytes(path), opts)
C.git_repository_free(crepo[0])
if 'exception' in d:
raise d['exception']
check_error(err)
return Repository(path)
def clone_into(repo, remote, branch=None):
"""Clone into an empty repository from the specified remote
:param Repository repo: The empty repository into which to clone
:param Remote remote: The remote from which to clone
:param str branch: Branch to checkout after the clone. Pass None
to use the remotes's default branch.
This allows you specify arbitrary repository and remote configurations
before performing the clone step itself. E.g. you can replicate git-clone's
'--mirror' option by setting a refspec of '+refs/*:refs/*', 'core.mirror'
to true and calling this function.
"""
err = C.git_clone_into(repo._repo, remote._remote, ffi.NULL,
to_bytes(branch), ffi.NULL)
if remote._stored_exception:
raise remote._stored_exception
check_error(err)
return Repository._from_c(crepo[0], owned=True)
settings = Settings()
features = C.git_libgit2_features()
GIT_FEATURE_THREADS = C.GIT_FEATURE_THREADS
GIT_FEATURE_HTTPS = C.GIT_FEATURE_HTTPS
GIT_FEATURE_SSH = C.GIT_FEATURE_SSH

View File

@@ -43,7 +43,7 @@ import sys
#
# The version number of pygit2
#
__version__ = '0.21.4'
__version__ = '0.22.0'
#

View File

@@ -65,7 +65,8 @@ class Keypair(object):
remote server
:param str pubkey: the path to the user's public key file
:param str privkey: the path to the user's private key file
:param str passphrase: the password used to decrypt the private key file
:param str passphrase: the password used to decrypt the private key file,
or empty string if no passphrase is required.
"""

View File

@@ -1,7 +1,6 @@
typedef ... git_repository;
typedef ... git_remote;
typedef ... git_refspec;
typedef ... git_push;
typedef ... git_cred;
typedef ... git_object;
typedef ... git_tree;
@@ -74,6 +73,12 @@ typedef struct git_signature {
git_time when;
} git_signature;
#define GIT_FEATURE_THREADS ...
#define GIT_FEATURE_HTTPS ...
#define GIT_FEATURE_SSH ...
int git_libgit2_features(void);
const git_error * giterr_last(void);
void git_strarray_free(git_strarray *array);
@@ -106,9 +111,38 @@ typedef enum {
GIT_CREDTYPE_SSH_KEY,
GIT_CREDTYPE_SSH_CUSTOM,
GIT_CREDTYPE_DEFAULT,
GIT_CREDTYPE_SSH_INTERACTIVE,
GIT_CREDTYPE_USERNAME,
...
} git_credtype_t;
typedef enum git_cert_t {
GIT_CERT_X509,
GIT_CERT_HOSTKEY_LIBSSH2,
} git_cert_t;
typedef enum {
GIT_CERT_SSH_MD5 = 1,
GIT_CERT_SSH_SHA1 = 2,
} git_cert_ssh_t;
typedef struct {
git_cert_t cert_type;
git_cert_ssh_t type;
unsigned char hash_md5[16];
unsigned char hash_sha1[20];
} git_cert_hostkey;
typedef struct {
git_cert_t cert_type;
void *data;
size_t len;
} git_cert_x509;
typedef struct {
git_cert_t cert_type;
} git_cert;
typedef int (*git_transport_message_cb)(const char *str, int len, void *data);
typedef int (*git_cred_acquire_cb)(
git_cred **cred,
@@ -117,40 +151,59 @@ typedef int (*git_cred_acquire_cb)(
unsigned int allowed_types,
void *payload);
typedef int (*git_transfer_progress_cb)(const git_transfer_progress *stats, void *payload);
typedef int (*git_transport_certificate_check_cb)(git_cert *cert, int valid, const char *host, void *payload);
typedef int (*git_packbuilder_progress)(
int stage,
unsigned int current,
unsigned int total,
void *payload);
typedef int (*git_push_transfer_progress)(
unsigned int current,
unsigned int total,
size_t bytes,
void* payload);
struct git_remote_callbacks {
unsigned int version;
git_transport_message_cb sideband_progress;
int (*completion)(git_remote_completion_type type, void *data);
git_cred_acquire_cb credentials;
git_transport_certificate_check_cb certificate_check;
git_transfer_progress_cb transfer_progress;
int (*update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data);
git_packbuilder_progress pack_progress;
git_push_transfer_progress push_transfer_progress;
int (*push_update_reference)(const char *refname, const char *status, void *data);
void *payload;
};
typedef struct git_remote_callbacks git_remote_callbacks;
typedef struct {
unsigned int version;
unsigned int pb_parallelism;
} git_push_options;
int git_remote_list(git_strarray *out, git_repository *repo);
int git_remote_load(git_remote **out, git_repository *repo, const char *name);
int git_remote_lookup(git_remote **out, git_repository *repo, const char *name);
int git_remote_create(
git_remote **out,
git_repository *repo,
const char *name,
const char *url);
int git_remote_delete(git_remote *remote);
int git_remote_delete(git_repository *repo, const char *name);
int git_repository_state_cleanup(git_repository *repo);
const char * git_remote_name(const git_remote *remote);
int git_remote_rename(
git_strarray *problems,
git_remote *remote,
const char *new_name);
int git_remote_rename(git_strarray *problems, git_repository *repo, const char *name, const char *new_name);
const char * git_remote_url(const git_remote *remote);
int git_remote_set_url(git_remote *remote, const char* url);
const char * git_remote_pushurl(const git_remote *remote);
int git_remote_set_pushurl(git_remote *remote, const char* url);
int git_remote_fetch(git_remote *remote, const git_signature *signature, const char *reflog_message);
int git_remote_fetch(git_remote *remote, const git_strarray *refspecs, const git_signature *signature, const char *reflog_message);
int git_remote_push(git_remote *remote, git_strarray *refspecs, const git_push_options *opts, const git_signature *signature, const char *reflog_message);
const git_transfer_progress * git_remote_stats(git_remote *remote);
int git_remote_add_push(git_remote *remote, const char *refspec);
int git_remote_add_fetch(git_remote *remote, const char *refspec);
@@ -167,22 +220,6 @@ int git_remote_set_push_refspecs(git_remote *remote, git_strarray *array);
void git_remote_free(git_remote *remote);
int git_push_new(git_push **push, git_remote *remote);
int git_push_add_refspec(git_push *push, const char *refspec);
int git_push_finish(git_push *push);
int git_push_unpack_ok(git_push *push);
int git_push_status_foreach(
git_push *push,
int (*cb)(const char *ref, const char *msg, void *data),
void *data);
int git_push_update_tips(
git_push *push,
const git_signature *signature,
const char *reflog_message);
void git_push_free(git_push *push);
const char * git_refspec_src(const git_refspec *refspec);
const char * git_refspec_dst(const git_refspec *refspec);
int git_refspec_force(const git_refspec *refspec);
@@ -261,14 +298,12 @@ typedef int (*git_diff_notify_cb)(
typedef struct {
unsigned int version;
uint32_t flags;
git_submodule_ignore_t ignore_submodules;
git_strarray pathspec;
git_diff_notify_cb notify_cb;
void *notify_payload;
uint16_t context_lines;
uint16_t interhunk_lines;
uint32_t context_lines;
uint32_t interhunk_lines;
uint16_t id_abbrev;
git_off_t max_size;
const char *old_prefix;
@@ -339,13 +374,6 @@ typedef struct git_checkout_options {
const char *their_label;
} git_checkout_options;
typedef enum {
GIT_CLONE_LOCAL_AUTO,
GIT_CLONE_LOCAL,
GIT_CLONE_NO_LOCAL,
GIT_CLONE_LOCAL_NO_LINKS,
} git_clone_local_t;
int git_checkout_init_options(git_checkout_options *opts, unsigned int version);
int git_checkout_tree(git_repository *repo, const git_object *treeish, const git_checkout_options *opts);
int git_checkout_head(git_repository *repo, const git_checkout_options *opts);
@@ -355,18 +383,38 @@ int git_checkout_index(git_repository *repo, git_index *index, const git_checkou
* git_clone
*/
typedef int (*git_remote_create_cb)(
git_remote **out,
git_repository *repo,
const char *name,
const char *url,
void *payload);
typedef int (*git_repository_create_cb)(
git_repository **out,
const char *path,
int bare,
void *payload);
typedef enum {
GIT_CLONE_LOCAL_AUTO,
GIT_CLONE_LOCAL,
GIT_CLONE_NO_LOCAL,
GIT_CLONE_LOCAL_NO_LINKS,
} git_clone_local_t;
typedef struct git_clone_options {
unsigned int version;
git_checkout_options checkout_opts;
git_remote_callbacks remote_callbacks;
int bare;
int ignore_cert_errors;
git_clone_local_t local;
const char *remote_name;
const char* checkout_branch;
git_signature *signature;
git_repository_create_cb repository_cb;
void *repository_cb_payload;
git_remote_create_cb remote_cb;
void *remote_cb_payload;
} git_clone_options;
#define GIT_CLONE_OPTIONS_VERSION ...
@@ -377,13 +425,6 @@ int git_clone(git_repository **out,
const char *local_path,
const git_clone_options *options);
int git_clone_into(
git_repository *repo,
git_remote *remote,
const git_checkout_options *co_opts,
const char *branch,
const git_signature *signature);
/*
* git_config
*/

View File

@@ -35,6 +35,7 @@ from .utils import to_bytes
class Refspec(object):
"""The constructor is for internal use only"""
def __init__(self, owner, ptr):
self._owner = owner
self._refspec = ptr
@@ -95,7 +96,7 @@ class Refspec(object):
return self._transform(ref, C.git_refspec_transform)
def rtransform(self, ref):
"""transform(str) -> str
"""rtransform(str) -> str
Transform a reference name according to this refspec from the lhs
to the rhs"""

View File

@@ -121,6 +121,16 @@ class Remote(object):
:param Oid new: the reference's new value
"""
def push_update_reference(self, refname, message):
"""Push update reference callback
Override with your own function to report the remote's
acceptace or rejection of reference updates.
:param str refname: the name of the reference (on the remote)
: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"""
@@ -137,25 +147,6 @@ class Remote(object):
return maybe_string(C.git_remote_name(self._remote))
def rename(self, new_name):
"""Rename this remote
Returns a list of fetch refspecs which were not in the standard format
and thus could not be remapped
"""
if not new_name:
raise ValueError("New remote name must be a non-empty string")
problems = ffi.new('git_strarray *')
err = C.git_remote_rename(problems, self._remote, to_bytes(new_name))
check_error(err)
ret = strarray_to_strings(problems)
C.git_strarray_free(problems)
return ret
@property
def url(self):
"""Url of the remote"""
@@ -178,14 +169,6 @@ class Remote(object):
err = C.git_remote_set_pushurl(self._remote, to_bytes(value))
check_error(err)
def delete(self):
"""Remove this remote
All remote-tracking branches and configuration settings for the remote will be removed.
"""
err = C.git_remote_delete(self._remote)
check_error(err)
def save(self):
"""save()
@@ -231,7 +214,7 @@ class Remote(object):
self._stored_exception = None
try:
err = C.git_remote_fetch(self._remote, ptr, to_bytes(message))
err = C.git_remote_fetch(self._remote, ffi.NULL, ptr, to_bytes(message))
if self._stored_exception:
raise self._stored_exception
@@ -293,7 +276,7 @@ class Remote(object):
def add_fetch(self, spec):
"""add_fetch(refspec)
Add a fetch refspec to the remote"""
Add a fetch refspec (str) to the remote"""
err = C.git_remote_add_fetch(self._remote, to_bytes(spec))
check_error(err)
@@ -301,32 +284,35 @@ class Remote(object):
def add_push(self, spec):
"""add_push(refspec)
Add a push refspec to the remote"""
Add a push refspec (str) to the remote"""
err = C.git_remote_add_push(self._remote, to_bytes(spec))
check_error(err)
@ffi.callback("int (*cb)(const char *ref, const char *msg, void *data)")
def _push_cb(ref, msg, data):
self = ffi.from_handle(data)
if msg:
self._bad_message = ffi.string(msg).decode()
return 0
def push(self, specs, signature=None, message=None):
"""push(specs, signature, message)
def push(self, spec, signature=None, message=None):
"""push(refspec, signature, message)
Push the given refspec to the remote. Raises ``GitError`` on
protocol error or unpack failure.
Push the given refspec to the remote. Raises ``GitError`` on error.
:param str spec: push refspec to use
:param [str] specs: push refspecs to use
:param Signature signature: signature to use when updating the tips
:param str message: message to use when updating the tips
"""
# Get the default callbacks first
defaultcallbacks = ffi.new('git_remote_callbacks *')
err = C.git_remote_init_callbacks(defaultcallbacks, 1)
check_error(err)
refspecs, refspecs_refs = strings_to_strarray(specs)
if signature:
sig_cptr = ffi.new('git_signature **')
ffi.buffer(sig_cptr)[:] = signature._pointer[:]
sig_ptr = sig_cptr[0]
else:
sig_ptr = ffi.NULL
# Build custom callback structure
callbacks = ffi.new('git_remote_callbacks *')
callbacks.version = 1
@@ -334,53 +320,23 @@ class Remote(object):
callbacks.transfer_progress = self._transfer_progress_cb
callbacks.update_tips = self._update_tips_cb
callbacks.credentials = self._credentials_cb
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)
callbacks.payload = self._self_handle
err = C.git_remote_set_callbacks(self._remote, callbacks)
try:
err = C.git_remote_set_callbacks(self._remote, callbacks)
check_error(err)
except:
self._self_handle = None
raise
cpush = ffi.new('git_push **')
err = C.git_push_new(cpush, self._remote)
check_error(err)
push = cpush[0]
try:
err = C.git_push_add_refspec(push, to_bytes(spec))
err = C.git_remote_push(self._remote, refspecs, ffi.NULL, sig_ptr, to_bytes(message))
check_error(err)
err = C.git_push_finish(push)
check_error(err)
if not C.git_push_unpack_ok(push):
raise GitError("remote failed to unpack objects")
err = C.git_push_status_foreach(push, self._push_cb,
ffi.new_handle(self))
check_error(err)
if hasattr(self, '_bad_message'):
raise GitError(self._bad_message)
if signature:
ptr = signature._pointer[:]
else:
ptr = ffi.NULL
err = C.git_push_update_tips(push, ptr, to_bytes(message))
check_error(err)
finally:
self._self_handle = None
C.git_push_free(push)
# These functions exist to be called by the git_remote as
# callbacks. They proxy the call to whatever the user set
@@ -437,6 +393,23 @@ class Remote(object):
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)')
@@ -494,3 +467,99 @@ def get_credentials(fn, url, username, allowed):
check_error(err)
return ccred
class RemoteCollection(object):
"""Collection of configured remotes
You can use this class to look up and manage the remotes configured
in a repository. You can access repositories using index
access. E.g. to look up the "origin" remote, you can use
>>> repo.remotes["origin"]
"""
def __init__(self, repo):
self._repo = repo;
def __len__(self):
names = ffi.new('git_strarray *')
try:
err = C.git_remote_list(names, self._repo._repo)
check_error(err)
return names.count
finally:
C.git_strarray_free(names)
def __iter__(self):
names = ffi.new('git_strarray *')
try:
err = C.git_remote_list(names, self._repo._repo)
check_error(err)
cremote = ffi.new('git_remote **')
for i in range(names.count):
err = C.git_remote_lookup(cremote, self._repo._repo, names.strings[i])
check_error(err)
yield Remote(self._repo, cremote[0])
finally:
C.git_strarray_free(names)
def __getitem__(self, name):
if isinstance(name, int):
return list(self)[name]
cremote = ffi.new('git_remote **')
err = C.git_remote_lookup(cremote, self._repo._repo, to_bytes(name))
check_error(err)
return Remote(self._repo, cremote[0])
def create(self, name, url):
"""create(name, url) -> Remote
Create a new remote with the given name and url.
"""
cremote = ffi.new('git_remote **')
err = C.git_remote_create(cremote, self._repo._repo, to_bytes(name), to_bytes(url))
check_error(err)
return Remote(self._repo, cremote[0])
def rename(self, name, new_name):
"""rename(name, new_name) -> [str]
Rename a remote in the configuration. The refspecs in strandard
format will be renamed.
Returns a list of fetch refspecs which were not in the standard format
and thus could not be remapped
"""
if not new_name:
raise ValueError("Current remote name must be a non-empty string")
if not new_name:
raise ValueError("New remote name must be a non-empty string")
problems = ffi.new('git_strarray *')
err = C.git_remote_rename(problems, self._repo._repo, to_bytes(name), to_bytes(new_name))
check_error(err)
ret = strarray_to_strings(problems)
C.git_strarray_free(problems)
return ret
def delete(self, name):
"""Remove a remote from the configuration
All remote-tracking branches and configuration settings for the remote will be removed.
"""
err = C.git_remote_delete(self._repo._repo, to_bytes(name))
check_error(err)

View File

@@ -48,7 +48,7 @@ from .config import Config
from .errors import check_error
from .ffi import ffi, C
from .index import Index
from .remote import Remote
from .remote import RemoteCollection
from .blame import Blame
from .utils import to_bytes, is_string
@@ -57,6 +57,19 @@ class Repository(_Repository):
def __init__(self, *args, **kwargs):
super(Repository, self).__init__(*args, **kwargs)
self._common_init()
@classmethod
def _from_c(cls, ptr, owned):
cptr = ffi.new('git_repository **')
cptr[0] = ptr
repo = cls.__new__(cls)
super(cls, repo)._from_c(bytes(ffi.buffer(cptr)[:]), owned)
repo._common_init()
return repo
def _common_init(self):
self.remotes = RemoteCollection(self)
# Get the pointer as the contents of a buffer and store it for
# later access
@@ -90,36 +103,11 @@ class Repository(_Repository):
"""create_remote(name, url) -> Remote
Creates a new remote.
This method is deprecated, please use Remote.remotes.create()
"""
cremote = ffi.new('git_remote **')
err = C.git_remote_create(cremote, self._repo, to_bytes(name),
to_bytes(url))
check_error(err)
return Remote(self, cremote[0])
@property
def remotes(self):
"""Returns all configured remotes"""
names = ffi.new('git_strarray *')
try:
err = C.git_remote_list(names, self._repo)
check_error(err)
l = [None] * names.count
cremote = ffi.new('git_remote **')
for i in range(names.count):
err = C.git_remote_load(cremote, self._repo, names.strings[i])
check_error(err)
l[i] = Remote(self, cremote[0])
return l
finally:
C.git_strarray_free(names)
return self.remotes.create(name, url)
#
# Configuration

View File

@@ -37,6 +37,8 @@ from distutils.command.build import build
from distutils.command.sdist import sdist
from distutils import log
import os
from os import getenv, listdir, pathsep
from os.path import abspath, isfile
from setuptools import setup, Extension, Command
import shlex
from subprocess import Popen, PIPE
@@ -58,7 +60,7 @@ else:
libgit2_bin, libgit2_include, libgit2_lib = get_libgit2_paths()
pygit2_exts = [os.path.join('src', name) for name in os.listdir('src')
pygit2_exts = [os.path.join('src', name) for name in listdir('src')
if name.endswith('.c')]
@@ -71,7 +73,6 @@ class TestCommand(Command):
def initialize_options(self):
self.args = ''
pass
def finalize_options(self):
pass
@@ -80,7 +81,7 @@ class TestCommand(Command):
self.run_command('build')
bld = self.distribution.get_command_obj('build')
# Add build_lib in to sys.path so that unittest can found DLLs and libs
sys.path = [os.path.abspath(bld.build_lib)] + sys.path
sys.path = [abspath(bld.build_lib)] + sys.path
test_argv0 = [sys.argv[0] + ' test --args=']
# For transfering args to unittest, we have to split args by ourself,
@@ -93,6 +94,7 @@ class TestCommand(Command):
test_argv = test_argv0 + shlex.split(self.args)
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.
@@ -116,12 +118,12 @@ class BuildWithDLLs(CFFIBuild):
libgit2_dlls.append('git2.dll')
elif compiler_type == 'mingw32':
libgit2_dlls.append('libgit2.dll')
look_dirs = [libgit2_bin] + os.getenv("PATH", "").split(os.pathsep)
target = os.path.abspath(self.build_lib)
look_dirs = [libgit2_bin] + getenv("PATH", "").split(pathsep)
target = abspath(self.build_lib)
for bin in libgit2_dlls:
for look in look_dirs:
f = os.path.join(look, bin)
if os.path.isfile(f):
if isfile(f):
ret.append((f, target))
break
else:
@@ -131,10 +133,9 @@ class BuildWithDLLs(CFFIBuild):
def run(self):
build.run(self)
if os.name == 'nt':
# 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.
for s, d in self._get_dlls():
self.copy_file(s, d)
class sdist_files_from_git(sdist):
@@ -167,16 +168,10 @@ with codecs.open('README.rst', 'r', 'utf-8') as readme:
cmdclass = {
'build': BuildWithDLLs if os.name == 'nt' else CFFIBuild,
'test': TestCommand,
'sdist': sdist_files_from_git}
if os.name == 'nt':
# BuildWithDLLs can copy external DLLs into source directory.
cmdclass['build'] = BuildWithDLLs
else:
# Build cffi
cmdclass['build'] = CFFIBuild
'sdist': sdist_files_from_git,
}
setup(name='pygit2',
description='Python bindings for libgit2.',
@@ -184,7 +179,7 @@ setup(name='pygit2',
version=__version__,
url='http://github.com/libgit2/pygit2',
classifiers=classifiers,
license='GPLv2',
license='GPLv2 with linking exception',
maintainer=u('J. David Ibáñez'),
maintainer_email='jdavid.ibp@gmail.com',
long_description=long_description,

View File

@@ -31,6 +31,7 @@
#include "error.h"
#include "types.h"
#include "utils.h"
#include "oid.h"
#include "diff.h"
extern PyObject *GitError;
@@ -43,7 +44,7 @@ extern PyTypeObject RepositoryType;
PyTypeObject PatchType;
PyObject*
PyObject *
wrap_diff(git_diff *diff, Repository *repo)
{
Diff *py_diff;
@@ -52,7 +53,7 @@ wrap_diff(git_diff *diff, Repository *repo)
if (py_diff) {
Py_INCREF(repo);
py_diff->repo = repo;
py_diff->list = diff;
py_diff->diff = diff;
}
return (PyObject*) py_diff;
@@ -76,13 +77,13 @@ wrap_patch(git_patch *patch)
delta = git_patch_get_delta(patch);
py_patch->old_file_path = delta->old_file.path;
py_patch->new_file_path = delta->new_file.path;
py_patch->old_file_path = strdup(delta->old_file.path);
py_patch->new_file_path = strdup(delta->new_file.path);
py_patch->status = git_diff_status_char(delta->status);
py_patch->similarity = delta->similarity;
py_patch->flags = delta->flags;
py_patch->old_id = git_oid_allocfmt(&delta->old_file.id);
py_patch->new_id = git_oid_allocfmt(&delta->new_file.id);
py_patch->old_id = git_oid_to_python(&delta->old_file.id);
py_patch->new_id = git_oid_to_python(&delta->new_file.id);
git_patch_line_stats(NULL, &additions, &deletions, patch);
py_patch->additions = additions;
@@ -133,7 +134,7 @@ wrap_patch(git_patch *patch)
return (PyObject*) py_patch;
}
PyObject*
PyObject *
diff_get_patch_byindex(git_diff *diff, size_t idx)
{
git_patch *patch = NULL;
@@ -150,18 +151,18 @@ static void
Patch_dealloc(Patch *self)
{
Py_CLEAR(self->hunks);
free(self->old_id);
free(self->new_id);
/* We do not have to free old_file_path and new_file_path, they will
* be freed by git_diff_list_free in Diff_dealloc */
Py_CLEAR(self->old_id);
Py_CLEAR(self->new_id);
free(self->old_file_path);
free(self->new_file_path);
PyObject_Del(self);
}
PyMemberDef Patch_members[] = {
MEMBER(Patch, old_file_path, T_STRING, "old file path"),
MEMBER(Patch, new_file_path, T_STRING, "new file path"),
MEMBER(Patch, old_id, T_STRING, "old oid"),
MEMBER(Patch, new_id, T_STRING, "new oid"),
MEMBER(Patch, old_id, T_OBJECT, "old oid"),
MEMBER(Patch, new_id, T_OBJECT, "new oid"),
MEMBER(Patch, status, T_CHAR, "status"),
MEMBER(Patch, similarity, T_INT, "similarity"),
MEMBER(Patch, hunks, T_OBJECT, "hunks"),
@@ -234,7 +235,7 @@ PyObject *
DiffIter_iternext(DiffIter *self)
{
if (self->i < self->n)
return diff_get_patch_byindex(self->diff->list, self->i++);
return diff_get_patch_byindex(self->diff->diff, self->i++);
PyErr_SetNone(PyExc_StopIteration);
return NULL;
@@ -283,8 +284,8 @@ PyTypeObject DiffIterType = {
Py_ssize_t
Diff_len(Diff *self)
{
assert(self->list);
return (Py_ssize_t)git_diff_num_deltas(self->list);
assert(self->diff);
return (Py_ssize_t)git_diff_num_deltas(self->diff);
}
PyDoc_STRVAR(Diff_patch__doc__, "Patch diff string.");
@@ -295,15 +296,15 @@ Diff_patch__get__(Diff *self)
git_patch* patch;
git_buf buf = {NULL};
int err = GIT_ERROR;
size_t i, len, num;
size_t i, num;
PyObject *py_patch = NULL;
num = git_diff_num_deltas(self->list);
num = git_diff_num_deltas(self->diff);
if (num == 0)
Py_RETURN_NONE;
for (i = 0, len = 1; i < num ; ++i) {
err = git_patch_from_diff(&patch, self->list, i);
for (i = 0; i < num ; ++i) {
err = git_patch_from_diff(&patch, self->diff, i);
if (err < 0)
goto cleanup;
@@ -429,7 +430,7 @@ Diff_merge(Diff *self, PyObject *args)
if (py_diff->repo->repo != self->repo->repo)
return Error_set(GIT_ERROR);
err = git_diff_merge(self->list, py_diff->list);
err = git_diff_merge(self->diff, py_diff->diff);
if (err < 0)
return Error_set(err);
@@ -454,7 +455,7 @@ Diff_find_similar(Diff *self, PyObject *args, PyObject *kwds)
&opts.flags, &opts.rename_threshold, &opts.copy_threshold, &opts.rename_from_rewrite_threshold, &opts.break_rewrite_threshold, &opts.rename_limit))
return NULL;
err = git_diff_find_similar(self->list, &opts);
err = git_diff_find_similar(self->diff, &opts);
if (err < 0)
return Error_set(err);
@@ -471,7 +472,7 @@ Diff_iter(Diff *self)
Py_INCREF(self);
iter->diff = self;
iter->i = 0;
iter->n = git_diff_num_deltas(self->list);
iter->n = git_diff_num_deltas(self->diff);
}
return (PyObject*)iter;
}
@@ -486,14 +487,14 @@ Diff_getitem(Diff *self, PyObject *value)
i = PyLong_AsUnsignedLong(value);
return diff_get_patch_byindex(self->list, i);
return diff_get_patch_byindex(self->diff, i);
}
static void
Diff_dealloc(Diff *self)
{
git_diff_free(self->list);
git_diff_free(self->diff);
Py_CLEAR(self->repo);
PyObject_Del(self);
}

View File

@@ -39,13 +39,13 @@ extern PyTypeObject SignatureType;
PyDoc_STRVAR(Note_remove__doc__,
"Removes a note for an annotated object");
PyObject*
PyObject *
Note_remove(Note *self, PyObject* args)
{
char *ref = "refs/notes/commits";
int err = GIT_ERROR;
git_oid annotated_id;
Signature *py_author, *py_committer;
Oid *id;
if (!PyArg_ParseTuple(args, "O!O!|s",
&SignatureType, &py_author,
@@ -53,12 +53,9 @@ Note_remove(Note *self, PyObject* args)
&ref))
return NULL;
err = git_oid_fromstr(&annotated_id, self->annotated_id);
if (err < 0)
return Error_set(err);
id = (Oid *) self->annotated_id;
err = git_note_remove(self->repo->repo, ref, py_author->signature,
py_committer->signature, &annotated_id);
py_committer->signature, &id->oid);
if (err < 0)
return Error_set(err);
@@ -90,7 +87,7 @@ static void
Note_dealloc(Note *self)
{
Py_CLEAR(self->repo);
free(self->annotated_id);
Py_CLEAR(self->annotated_id);
git_note_free(self->note);
PyObject_Del(self);
}
@@ -102,7 +99,7 @@ PyMethodDef Note_methods[] = {
};
PyMemberDef Note_members[] = {
MEMBER(Note, annotated_id, T_STRING, "id of the annotated object."),
MEMBER(Note, annotated_id, T_OBJECT, "id of the annotated object."),
{NULL}
};
@@ -211,7 +208,7 @@ PyTypeObject NoteIterType = {
};
PyObject*
PyObject *
wrap_note(Repository* repo, git_oid* annotated_id, const char* ref)
{
Note* py_note = NULL;
@@ -229,7 +226,7 @@ wrap_note(Repository* repo, git_oid* annotated_id, const char* ref)
py_note->repo = repo;
Py_INCREF(repo);
py_note->annotated_id = git_oid_allocfmt(annotated_id);
py_note->annotated_id = git_oid_to_python(annotated_id);
return (PyObject*) py_note;
}

View File

@@ -149,7 +149,7 @@ PyMethodDef module_methods[] = {
{NULL}
};
PyObject*
PyObject *
moduleinit(PyObject* m)
{
if (m == NULL)
@@ -210,7 +210,6 @@ moduleinit(PyObject* m)
ADD_CONSTANT_INT(m, GIT_OBJ_BLOB)
ADD_CONSTANT_INT(m, GIT_OBJ_TAG)
/* Valid modes for index and tree entries. */
ADD_CONSTANT_INT(m, GIT_FILEMODE_NEW)
ADD_CONSTANT_INT(m, GIT_FILEMODE_TREE)
ADD_CONSTANT_INT(m, GIT_FILEMODE_BLOB)
ADD_CONSTANT_INT(m, GIT_FILEMODE_BLOB_EXECUTABLE)
@@ -347,7 +346,7 @@ moduleinit(PyObject* m)
ADD_CONSTANT_INT(m, GIT_MERGE_ANALYSIS_UNBORN)
/* Global initialization of libgit2 */
git_threads_init();
git_libgit2_init();
return m;
}

View File

@@ -60,8 +60,8 @@ RefLogIter_iternext(RefLogIter *self)
entry = git_reflog_entry_byindex(self->reflog, self->i);
py_entry = PyObject_New(RefLogEntry, &RefLogEntryType);
py_entry->oid_old = git_oid_allocfmt(git_reflog_entry_id_old(entry));
py_entry->oid_new = git_oid_allocfmt(git_reflog_entry_id_new(entry));
py_entry->oid_old = git_oid_to_python(git_reflog_entry_id_old(entry));
py_entry->oid_new = git_oid_to_python(git_reflog_entry_id_new(entry));
py_entry->message = strdup(git_reflog_entry_message(entry));
err = git_signature_dup(&py_entry->signature,
git_reflog_entry_committer(entry));
@@ -431,16 +431,16 @@ RefLogEntry_init(RefLogEntry *self, PyObject *args, PyObject *kwds)
static void
RefLogEntry_dealloc(RefLogEntry *self)
{
free(self->oid_old);
free(self->oid_new);
Py_CLEAR(self->oid_old);
Py_CLEAR(self->oid_new);
free(self->message);
git_signature_free(self->signature);
PyObject_Del(self);
}
PyMemberDef RefLogEntry_members[] = {
MEMBER(RefLogEntry, oid_new, T_STRING, "New oid."),
MEMBER(RefLogEntry, oid_old, T_STRING, "Old oid."),
MEMBER(RefLogEntry, oid_new, T_OBJECT, "New oid."),
MEMBER(RefLogEntry, oid_old, T_OBJECT, "Old oid."),
MEMBER(RefLogEntry, message, T_STRING, "Message."),
{NULL}
};

View File

@@ -55,6 +55,9 @@ extern PyTypeObject ReferenceType;
extern PyTypeObject NoteType;
extern PyTypeObject NoteIterType;
/* forward-declaration for Repsository._from_c() */
PyTypeObject RepositoryType;
git_otype
int_to_loose_object_type(int type_id)
{
@@ -88,19 +91,62 @@ Repository_init(Repository *self, PyObject *args, PyObject *kwds)
return -1;
}
self->owned = 1;
self->config = NULL;
self->index = NULL;
return 0;
}
PyDoc_STRVAR(Repository__from_c__doc__, "Init a Repository from a pointer. For internal use only.");
PyObject *
Repository__from_c(Repository *py_repo, PyObject *args)
{
PyObject *py_pointer, *py_free;
char *buffer;
Py_ssize_t len;
int err;
py_repo->repo = NULL;
py_repo->config = NULL;
py_repo->index = NULL;
if (!PyArg_ParseTuple(args, "OO!", &py_pointer, &PyBool_Type, &py_free))
return NULL;
err = PyBytes_AsStringAndSize(py_pointer, &buffer, &len);
if (err < 0)
return NULL;
if (len != sizeof(git_repository *)) {
PyErr_SetString(PyExc_TypeError, "invalid pointer length");
return NULL;
}
py_repo->repo = *((git_repository **) buffer);
py_repo->owned = py_free == Py_True;
Py_RETURN_NONE;
}
PyDoc_STRVAR(Repository__disown__doc__, "Mark the object as not-owned by us. For internal use only.");
PyObject *
Repository__disown(Repository *py_repo)
{
py_repo->owned = 0;
Py_RETURN_NONE;
}
void
Repository_dealloc(Repository *self)
{
PyObject_GC_UnTrack(self);
Py_CLEAR(self->index);
Py_CLEAR(self->config);
git_repository_free(self->repo);
if (self->owned)
git_repository_free(self->repo);
Py_TYPE(self)->tp_free(self);
}
@@ -526,7 +572,7 @@ Repository_merge_analysis(Repository *self, PyObject *py_id)
int err;
size_t len;
git_oid id;
git_merge_head *merge_head;
git_annotated_commit *commit;
git_merge_analysis_t analysis;
git_merge_preference_t preference;
@@ -534,12 +580,12 @@ Repository_merge_analysis(Repository *self, PyObject *py_id)
if (len == 0)
return NULL;
err = git_merge_head_from_id(&merge_head, self->repo, &id);
err = git_annotated_commit_lookup(&commit, self->repo, &id);
if (err < 0)
return Error_set(err);
err = git_merge_analysis(&analysis, &preference, self->repo, (const git_merge_head **) &merge_head, 1);
git_merge_head_free(merge_head);
err = git_merge_analysis(&analysis, &preference, self->repo, (const git_annotated_commit **) &commit, 1);
git_annotated_commit_free(commit);
if (err < 0)
return Error_set(err);
@@ -561,7 +607,7 @@ PyDoc_STRVAR(Repository_merge__doc__,
PyObject *
Repository_merge(Repository *self, PyObject *py_oid)
{
git_merge_head *oid_merge_head;
git_annotated_commit *commit;
git_oid oid;
int err;
size_t len;
@@ -572,16 +618,16 @@ Repository_merge(Repository *self, PyObject *py_oid)
if (len == 0)
return NULL;
err = git_merge_head_from_id(&oid_merge_head, self->repo, &oid);
err = git_annotated_commit_lookup(&commit, self->repo, &oid);
if (err < 0)
return Error_set(err);
checkout_opts.checkout_strategy = GIT_CHECKOUT_SAFE_CREATE;
err = git_merge(self->repo,
(const git_merge_head **)&oid_merge_head, 1,
(const git_annotated_commit **)&commit, 1,
&merge_opts, &checkout_opts);
git_merge_head_free(oid_merge_head);
git_annotated_commit_free(commit);
if (err < 0)
return Error_set(err);
@@ -936,10 +982,15 @@ out:
PyDoc_STRVAR(Repository_listall_branches__doc__,
"listall_branches([flags]) -> [str, ...]\n"
"listall_branches([flag]) -> [str, ...]\n"
"\n"
"Return a tuple with all the branches in the repository.\n"
"By default, it returns all local branches.");
"Return a list with all the branches in the repository.\n"
"\n"
"The *flag* may be:\n"
"\n"
"- GIT_BRANCH_LOCAL - return all local branches (set by default)\n"
"- GIT_BRANCH_REMOTE - return all remote-tracking branches\n"
"- GIT_BRANCH_ALL - return local branches and remote-tracking branches");
PyObject *
Repository_listall_branches(Repository *self, PyObject *args)
@@ -1220,7 +1271,7 @@ Repository_TreeBuilder(Repository *self, PyObject *args)
}
}
err = git_treebuilder_create(&bld, tree);
err = git_treebuilder_new(&bld, self->repo, tree);
if (must_free != NULL)
git_tree_free(must_free);
@@ -1313,8 +1364,8 @@ Repository_create_note(Repository *self, PyObject* args)
if (err < 0)
return Error_set(err);
err = git_note_create(&note_id, self->repo, py_author->signature,
py_committer->signature, ref,
err = git_note_create(&note_id, self->repo, ref, py_author->signature,
py_committer->signature,
&annotated_id, message, force);
if (err < 0)
return Error_set(err);
@@ -1375,7 +1426,7 @@ Repository_reset(Repository *self, PyObject* args)
err = git_object_lookup_prefix(&target, self->repo, &oid, len,
GIT_OBJ_ANY);
err = err < 0 ? err : git_reset(self->repo, target, reset_type, NULL, NULL);
err = err < 0 ? err : git_reset(self->repo, target, reset_type, NULL, NULL, NULL);
git_object_free(target);
if (err < 0)
return Error_set_oid(err, &oid, len);
@@ -1410,6 +1461,8 @@ PyMethodDef Repository_methods[] = {
METHOD(Repository, listall_branches, METH_VARARGS),
METHOD(Repository, create_branch, METH_VARARGS),
METHOD(Repository, reset, METH_VARARGS),
METHOD(Repository, _from_c, METH_VARARGS),
METHOD(Repository, _disown, METH_NOARGS),
{NULL}
};

View File

@@ -88,7 +88,7 @@ TreeBuilder_write(TreeBuilder *self)
int err;
git_oid oid;
err = git_treebuilder_write(&oid, self->repo->repo, self->bld);
err = git_treebuilder_write(&oid, self->bld);
if (err < 0)
return Error_set(err);

View File

@@ -32,8 +32,8 @@
#include <Python.h>
#include <git2.h>
#if !(LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR == 21)
#error You need a compatible libgit2 version (v0.21.x)
#if !(LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR == 22)
#error You need a compatible libgit2 version (v0.22.x)
#endif
/*
@@ -47,6 +47,7 @@ typedef struct {
git_repository *repo;
PyObject *index; /* It will be None for a bare repository */
PyObject *config; /* It will be None for a bare repository */
int owned; /* _from_c() sometimes means we don't own the C pointer */
} Repository;
@@ -79,7 +80,7 @@ typedef struct {
PyObject_HEAD
Repository *repo;
git_note *note;
char* annotated_id;
PyObject* annotated_id;
} Note;
typedef struct {
@@ -90,12 +91,12 @@ typedef struct {
} NoteIter;
/* git _diff */
SIMPLE_TYPE(Diff, git_diff, list)
/* git_diff */
SIMPLE_TYPE(Diff, git_diff, diff)
typedef struct {
PyObject_HEAD
Diff* diff;
Diff *diff;
size_t i;
size_t n;
} DiffIter;
@@ -103,10 +104,10 @@ typedef struct {
typedef struct {
PyObject_HEAD
PyObject* hunks;
const char * old_file_path;
const char * new_file_path;
char* old_id;
char* new_id;
char * old_file_path;
char * new_file_path;
PyObject* old_id;
PyObject* new_id;
char status;
unsigned similarity;
unsigned additions;
@@ -164,8 +165,8 @@ typedef Reference Branch;
typedef struct {
PyObject_HEAD
git_signature *signature;
char *oid_old;
char *oid_new;
PyObject *oid_old;
PyObject *oid_new;
char *message;
} RefLogEntry;

View File

@@ -261,9 +261,9 @@ class DiffTest(utils.BareRepoTestCase):
commit_a = self.repo[COMMIT_SHA1_1]
commit_b = self.repo[COMMIT_SHA1_2]
patch = commit_a.tree.diff_to_tree(commit_b.tree)[0]
self.assertEqual(patch.old_id,
self.assertEqual(patch.old_id.hex,
'7f129fd57e31e935c6d60a0c794efe4e6927664b')
self.assertEqual(patch.new_id,
self.assertEqual(patch.new_id.hex,
'af431f20fc541ed6d5afede3e2dc7160f6f01f16')
def test_hunk_content(self):

View File

@@ -70,7 +70,7 @@ class NotesTest(utils.BareRepoTestCase):
def test_iterate_notes(self):
for i, note in enumerate(self.repo.notes()):
entry = (note.id.hex, note.message, note.annotated_id)
entry = (note.id.hex, note.message, note.annotated_id.hex)
self.assertEqual(NOTES[i], entry)
def test_iterate_non_existing_ref(self):

View File

@@ -71,19 +71,19 @@ class RepositoryTest(utils.RepoTestCase):
remote = self.repo.remotes[1]
self.assertEqual(name, remote.name)
remote.delete()
self.repo.remotes.delete(remote.name)
self.assertEqual(1, len(self.repo.remotes))
def test_remote_rename(self):
remote = self.repo.remotes[0]
self.assertEqual(REMOTE_NAME, remote.name)
problems = remote.rename('new')
problems = self.repo.remotes.rename(remote.name, "new")
self.assertEqual([], problems)
self.assertEqual('new', remote.name)
self.assertNotEqual('new', remote.name)
self.assertRaises(ValueError, remote.rename, '')
self.assertRaises(ValueError, remote.rename, None)
self.assertRaises(ValueError, self.repo.remotes.rename, '', '')
self.assertRaises(ValueError, self.repo.remotes.rename, None, None)
def test_remote_set_url(self):
@@ -167,16 +167,25 @@ class RepositoryTest(utils.RepoTestCase):
remote = self.repo.create_remote(name, url)
self.assertTrue(remote.name in [x.name for x in self.repo.remotes])
def test_remote_collection(self):
remote = self.repo.remotes['origin']
self.assertEqual(REMOTE_NAME, remote.name)
self.assertEqual(REMOTE_URL, remote.url)
with self.assertRaises(KeyError):
self.repo.remotes['upstream']
name = 'upstream'
url = 'git://github.com/libgit2/pygit2.git'
remote = self.repo.remotes.create(name, url)
self.assertTrue(remote.name in [x.name for x in self.repo.remotes])
def test_remote_save(self):
remote = self.repo.remotes[0]
remote.rename('new-name')
remote.url = 'http://example.com/test.git'
remote.save()
self.assertEqual('new-name', self.repo.remotes[0].name)
self.assertEqual('http://example.com/test.git',
self.repo.remotes[0].url)
@@ -265,11 +274,11 @@ class PushTestCase(unittest.TestCase):
'refs/heads/master', tip.author, tip.author, 'empty commit',
tip.tree.id, [tip.id]
)
self.remote.push('refs/heads/master')
self.remote.push(['refs/heads/master'])
self.assertEqual(self.origin[self.origin.head.target].id, oid)
def test_push_when_up_to_date_succeeds(self):
self.remote.push('refs/heads/master')
self.remote.push(['refs/heads/master'])
origin_tip = self.origin[self.origin.head.target].id
clone_tip = self.clone[self.clone.head.target].id
self.assertEqual(origin_tip, clone_tip)
@@ -285,7 +294,8 @@ class PushTestCase(unittest.TestCase):
'refs/heads/master', tip.author, tip.author, 'other commit',
tip.tree.id, [tip.id]
)
self.assertRaises(pygit2.GitError, self.remote.push, 'refs/heads/master')
self.assertRaises(pygit2.GitError, self.remote.push, ['refs/heads/master'])
if __name__ == '__main__':
unittest.main()

View File

@@ -41,7 +41,7 @@ import sys
# Import from pygit2
from pygit2 import GIT_OBJ_ANY, GIT_OBJ_BLOB, GIT_OBJ_COMMIT
from pygit2 import init_repository, clone_repository, clone_into, discover_repository
from pygit2 import init_repository, clone_repository, discover_repository
from pygit2 import Oid, Reference, hashfile
import pygit2
from . import utils
@@ -440,19 +440,22 @@ class CloneRepositoryTest(utils.NoRepoTestCase):
self.assertFalse(repo.is_empty)
self.assertTrue(repo.is_bare)
def test_clone_remote_name(self):
repo_path = "./test/data/testrepo.git/"
repo = clone_repository(
repo_path, self._temp_dir, remote_name="custom_remote")
self.assertFalse(repo.is_empty)
self.assertEqual(repo.remotes[0].name, "custom_remote")
def test_clone_repository_and_remote_callbacks(self):
src_repo_relpath = "./test/data/testrepo.git/"
repo_path = os.path.join(self._temp_dir, "clone-into")
url = 'file://' + os.path.realpath(src_repo_relpath)
def test_clone_into(self):
repo_path = "./test/data/testrepo.git/"
repo = init_repository(os.path.join(self._temp_dir, "clone-into"))
remote = repo.create_remote("origin", 'file://' + os.path.realpath(repo_path))
clone_into(repo, remote)
self.assertTrue('refs/remotes/origin/master' in repo.listall_references())
def create_repository(path, bare):
return init_repository(path, bare)
# here we override the name
def create_remote(repo, name, url):
return repo.remotes.create("custom_remote", url)
repo = clone_repository(url, repo_path, repository=create_repository, remote=create_remote)
self.assertFalse(repo.is_empty)
self.assertTrue('refs/remotes/custom_remote/master' in repo.listall_references())
self.assertIsNotNone(repo.remotes["custom_remote"])
def test_clone_with_credentials(self):
credentials = pygit2.UserPass("libgit2", "libgit2")