Merge remote-tracking branch 'upstream/master' into development

Reconcile the changes between the ffi changes upstream with the changes
to libgit2 in the dev branch.

Conflicts:
	src/config.c
	src/options.c
	src/refspec.c
	src/remote.c
This commit is contained in:
Carlos Martín Nieto
2014-05-16 04:41:26 +02:00
35 changed files with 1562 additions and 2012 deletions

1
.gitignore vendored
View File

@@ -10,3 +10,4 @@ pygit2/__pycache__
*.egg-info *.egg-info
*.swp *.swp
docs/_build docs/_build
__pycache__

View File

@@ -1,7 +1,6 @@
language: python language: python
python: python:
- "2.6"
- "2.7" - "2.7"
- "3.2" - "3.2"
- "3.3" - "3.3"
@@ -11,6 +10,7 @@ env: LIBGIT2=~/libgit2/_install/ LD_LIBRARY_PATH=~/libgit2/_install/lib
before_install: before_install:
- sudo apt-get install cmake - sudo apt-get install cmake
- pip install cffi
- "./.travis.sh" - "./.travis.sh"
script: script:

View File

@@ -6,8 +6,12 @@ pygit2 - libgit2 bindings in Python
:target: http://travis-ci.org/libgit2/pygit2 :target: http://travis-ci.org/libgit2/pygit2
Pygit2 is a set of Python bindings to the libgit2 shared library, libgit2 Pygit2 is a set of Python bindings to the libgit2 shared library, libgit2
implements the core of Git. Pygit2 works with Python 2.6, 2.7, 3.1, 3.2 and implements the core of Git. Pygit2 works with Python 2.7, 3.2, 3.3, 3.4 and
3.3 pypy.
It is likely to work with Python 2.6 and 3.1, but these versions are not
officially supported.
Pygit2 links: Pygit2 links:
@@ -44,33 +48,35 @@ for the topic), send a pull request.
Authors Authors
============== ==============
57 developers have contributed at least 1 commit to pygit2:: 62 developers have contributed at least 1 commit to pygit2::
J. David Ibáñez Brodie Rao Adam Spiers J. David Ibáñez Rémi Duraffort András Veres-Szentkirályi
Nico von Geyso David Versmisse Alexander Bayandin Nico von Geyso Sebastian Thiel Benjamin Kircher
Carlos Martín Nieto Rémi Duraffort Andrew Chin Carlos Martín Nieto Fraser Tweedale Benjamin Pollack
W. Trevor King Sebastian Thiel András Veres-Szentkirályi W. Trevor King Han-Wen Nienhuys Bryan O'Sullivan
Dave Borowitz Fraser Tweedale Benjamin Kircher Dave Borowitz Leonardo Rhodes David Fischer
Daniel Rodríguez Troitiño Han-Wen Nienhuys Benjamin Pollack Daniel Rodríguez Troitiño Petr Viktorin David Sanders
Richo Healey Petr Viktorin Bryan O'Sullivan Richo Healey Alex Chamberlain Devaev Maxim
Christian Boos Alex Chamberlain David Fischer Christian Boos Amit Bakshi Eric Davis
Julien Miotte Amit Bakshi David Sanders Julien Miotte Andrey Devyatkin Erik Meusel
Xu Tao Andrey Devyatkin Eric Davis Xu Tao Ben Davis Erik van Zijst
Jose Plana Ben Davis Erik van Zijst Jose Plana Eric Schrijver Ferengee
Martin Lenders Eric Schrijver Ferengee Martin Lenders Hervé Cauwelier Gustavo Di Pietro
Petr Hosek Hervé Cauwelier Gustavo Di Pietro Petr Hosek Huang Huang Hugh Cole-Baker
Victor Garcia Huang Huang Hugh Cole-Baker Victor Garcia Jared Flatow Josh Bleecher Snyder
Xavier Delannoy Jared Flatow Josh Bleecher Snyder Xavier Delannoy Jiunn Haur Lim Jun Omae
Yonggang Luo Jiunn Haur Lim Jun Omae Yonggang Luo Sarath Lakshman Óscar San José
Valentin Haenel Sarath Lakshman Óscar San José Valentin Haenel Vicent Marti Ridge Kennedy
Bernardo Heynemann Vicent Marti Ridge Kennedy Bernardo Heynemann Zoran Zaric Rui Abreu Ferreira
John Szakmeister Zoran Zaric Rui Abreu Ferreira John Szakmeister Adam Spiers Thomas Kluyver
Brodie Rao Alexander Bayandin earl
David Versmisse Andrew Chin
Changelog Changelog
============== ==============
0.20.3 (2014-04-XX) 0.20.3 (2014-04-02)
------------------- -------------------
- A number of memory issues fixed - A number of memory issues fixed

View File

@@ -50,7 +50,7 @@ copyright = u'2010-2014 The pygit2 contributors'
# The short X.Y version. # The short X.Y version.
version = '0.20' version = '0.20'
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = '0.20.2' release = '0.20.3'
# The language for content autogenerated by Sphinx. Refer to documentation # The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages. # for a list of supported languages.

View File

@@ -22,4 +22,13 @@ The Config type
aware that this may return multiple versions of each entry if they are aware that this may return multiple versions of each entry if they are
set multiple times in the configuration files. set multiple times in the configuration files.
.. currentmodule:: pygit2
The :class:`Config` Mapping interface. The :class:`Config` Mapping interface.
When using the mapping interface, the value is returned as a
string. In order to apply the git-config parsing rules, you can use
:meth:`Config.get_bool` or :meth:`Config.get_int`.
.. automethod:: pygit2.Config.get_bool
.. automethod:: pygit2.Config.get_int

View File

@@ -10,8 +10,11 @@ Welcome to pygit2's documentation!
:target: http://travis-ci.org/libgit2/pygit2 :target: http://travis-ci.org/libgit2/pygit2
Pygit2 is a set of Python bindings to the libgit2 shared library, libgit2 Pygit2 is a set of Python bindings to the libgit2 shared library, libgit2
implements the core of Git. Pygit2 works with Python 2.6, 2.7, 3.1, 3.2 and implements the core of Git. Pygit2 works with Python 2.7, 3.2, 3.3, 3.4 and
3.3 pypy.
It is likely to work with Python 2.6 and 3.1, but these versions are not
officially supported.
Pygit2 links: Pygit2 links:

View File

@@ -13,7 +13,7 @@ website:
http://libgit2.github.com http://libgit2.github.com
Also, make sure you have Python 2.6+ installed together with the Python Also, make sure you have Python 2.7 or 3.2+ installed together with the Python
development headers. development headers.
When those are installed, you can install pygit2: When those are installed, you can install pygit2:

View File

@@ -16,9 +16,9 @@ The Remote type
.. autoattribute:: pygit2.Remote.refspec_count .. autoattribute:: pygit2.Remote.refspec_count
.. autoattribute:: pygit2.Remote.push_refspecs .. autoattribute:: pygit2.Remote.push_refspecs
.. autoattribute:: pygit2.Remote.fetch_refspecs .. autoattribute:: pygit2.Remote.fetch_refspecs
.. autoattribute:: pygit2.Remote.progress .. automethod:: pygit2.Remote.progress
.. autoattribute:: pygit2.Remote.transfer_progress .. automethod:: pygit2.Remote.transfer_progress
.. autoattribute:: pygit2.Remote.update_tips .. automethod:: pygit2.Remote.update_tips
.. automethod:: pygit2.Remote.get_refspec .. automethod:: pygit2.Remote.get_refspec
.. automethod:: pygit2.Remote.fetch .. automethod:: pygit2.Remote.fetch
.. automethod:: pygit2.Remote.push .. automethod:: pygit2.Remote.push

View File

@@ -37,6 +37,10 @@ from .repository import Repository
from .version import __version__ from .version import __version__
from .settings import Settings from .settings import Settings
from .credentials import * from .credentials import *
from .remote import Remote, get_credentials
from .config import Config
from .errors import check_error
from .ffi import ffi, C, to_str
def init_repository(path, bare=False): def init_repository(path, bare=False):
""" """
@@ -49,6 +53,19 @@ def init_repository(path, bare=False):
return Repository(path) return Repository(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['callback'], url, username_from_url, allowed)
cred_out[0] = ccred[0]
except Exception as e:
d['exception'] = e
return C.GIT_EUSER
return 0
def clone_repository( def clone_repository(
url, path, bare=False, ignore_cert_errors=False, url, path, bare=False, ignore_cert_errors=False,
remote_name="origin", checkout_branch=None, credentials=None): remote_name="origin", checkout_branch=None, credentials=None):
@@ -74,8 +91,42 @@ def clone_repository(
""" """
_pygit2.clone_repository( opts = ffi.new('git_clone_options *')
url, path, bare, ignore_cert_errors, remote_name, checkout_branch, credentials) crepo = ffi.new('git_repository **')
branch = checkout_branch or None
# Data, let's use a dict as we don't really want much more
d = {}
d['callback'] = credentials
d_handle = ffi.new_handle(d)
# We need to keep the ref alive ourselves
checkout_branch_ref = None
if branch:
checkout_branch_ref = ffi.new('char []', branch)
opts.checkout_branch = checkout_branch_ref
remote_name_ref = ffi.new('char []', to_str(remote_name))
opts.remote_name = remote_name_ref
opts.version = 1
opts.ignore_cert_errors = ignore_cert_errors
opts.bare = bare
opts.remote_callbacks.version = 1
opts.checkout_opts.version = 1
if credentials:
opts.remote_callbacks.credentials = _credentials_cb
opts.remote_callbacks.payload = d_handle
err = C.git_clone(crepo, to_str(url), to_str(path), opts)
C.git_repository_free(crepo[0])
if 'exception' in d:
raise d['exception']
check_error(err)
return Repository(path) return Repository(path)
settings = Settings() settings = Settings()

281
pygit2/config.py Normal file
View File

@@ -0,0 +1,281 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
# as published by the Free Software Foundation.
#
# In addition to the permissions in the GNU General Public License,
# the authors give you unlimited permission to link the compiled
# version of this file into combinations with other programs,
# and to distribute those combinations without any restriction
# coming from the use of this file. (The General Public License
# restrictions do apply in other respects; for example, they cover
# modification of the file, and distribution when not linked into
# a combined executable.)
#
# This file is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# Import from the future
from __future__ import absolute_import, unicode_literals
from _pygit2 import Oid
from .ffi import ffi, C, to_str, is_string
from .errors import check_error, GitError
from .refspec import Refspec
def assert_string(v, desc):
if not is_string(v):
raise TypeError("%s must be a string" % desc)
class ConfigIterator(object):
def __init__(self, config, ptr):
self._iter = ptr
self._config = config
def __del__(self):
C.git_config_iterator_free(self._iter)
def __iter__(self):
return self
def _next_entry(self):
centry = ffi.new('git_config_entry **')
err = C.git_config_next(centry, self._iter)
check_error(err)
return centry[0]
def next(self):
return self.__next__()
def __next__(self):
entry = self._next_entry()
name = ffi.string(entry.name).decode('utf-8')
value = ffi.string(entry.value).decode('utf-8')
return name, value
class ConfigMultivarIterator(ConfigIterator):
def __next__(self):
entry = self._next_entry()
return ffi.string(entry.value).decode('utf-8')
class Config(object):
"""Git configuration management"""
def __init__(self, path=None):
cconfig = ffi.new('git_config **')
if not path:
err = C.git_config_new(cconfig)
else:
assert_string(path, "path")
err = C.git_config_open_ondisk(cconfig, to_str(path))
check_error(err, True)
self._config = cconfig[0]
@classmethod
def from_c(cls, repo, ptr):
config = cls.__new__(cls)
config._repo = repo
config._config = ptr
return config
def __del__(self):
C.git_config_free(self._config)
def _get(self, key):
assert_string(key, "key")
cstr = ffi.new('char **')
err = C.git_config_get_string(cstr, self._config, to_str(key))
return err, cstr
def _get_string(self, key):
err, cstr = self._get(key)
if err == C.GIT_ENOTFOUND:
raise KeyError(key)
check_error(err)
return cstr[0]
def __contains__(self, key):
err, cstr = self._get(key)
if err == C.GIT_ENOTFOUND:
return False
check_error(err)
return True
def __getitem__(self, key):
val = self._get_string(key)
return ffi.string(val).decode()
def __setitem__(self, key, value):
assert_string(key, "key")
err = 0
if isinstance(value, bool):
err = C.git_config_set_bool(self._config, to_str(key), value)
elif isinstance(value, int):
err = C.git_config_set_int64(self._config, to_str(key), value)
else:
err = C.git_config_set_string(self._config, to_str(key), to_str(value))
check_error(err)
def __delitem__(self, key):
assert_string(key, "key")
err = C.git_config_delete_entry(self._config, to_str(key))
check_error(err)
def __iter__(self):
citer = ffi.new('git_config_iterator **')
err = C.git_config_iterator_new(citer, self._config)
check_error(err)
return ConfigIterator(self, citer[0])
def get_multivar(self, name, regex=None):
"""get_multivar(name[, regex]) -> [str, ...]
Get each value of a multivar ''name'' as a list. The optional ''regex''
parameter is expected to be a regular expression to filter the variables
we're interested in."""
assert_string(name, "name")
citer = ffi.new('git_config_iterator **')
err = C.git_config_multivar_iterator_new(citer, self._config, to_str(name), to_str(regex))
check_error(err)
return ConfigMultivarIterator(self, citer[0])
def set_multivar(self, name, regex, value):
"""set_multivar(name, regex, value)
Set a multivar ''name'' to ''value''. ''regexp'' is a regular expression
to indicate which values to replace"""
assert_string(name, "name")
assert_string(regex, "regex")
assert_string(value, "value")
err = C.git_config_set_multivar(self._config, to_str(name), to_str(regex), to_str(value))
check_error(err)
def get_bool(self, key):
"""get_bool(key) -> Bool
Look up *key* and parse its value as a boolean as per the git-config rules
Truthy values are: 'true', 1, 'on' or 'yes'. Falsy values are: 'false',
0, 'off' and 'no'"""
val = self._get_string(key)
res = ffi.new('int *')
err = C.git_config_parse_bool(res, val)
check_error(err)
return res[0] != 0
def get_int(self, key):
"""get_int(key) -> int
Look up *key* and parse its value as an integer as per the git-config rules.
A value can have a suffix 'k', 'm' or 'g' which stand for 'kilo', 'mega' and
'giga' respectively"""
val = self._get_string(key)
res = ffi.new('int64_t *')
err = C.git_config_parse_int64(res, val)
check_error(err)
return res[0]
def add_file(self, path, level=0, force=0):
"""add_file(path, level=0, force=0)
Add a config file instance to an existing config."""
err = C.git_config_add_file_ondisk(self._config, to_str(path), level, force)
check_error(err)
#
# Methods to parse a string according to the git-config rules
#
@staticmethod
def parse_bool(text):
res = ffi.new('int *')
err = C.git_config_parse_bool(res, to_str(text))
return res[0] != 0
@staticmethod
def parse_int(text):
res = ffi.new('int64_t *')
err = C.git_config_parse_int64(res, to_str(text))
check_error(err)
return res[0]
#
# Static methods to get specialized version of the config
#
@staticmethod
def _from_found_config(fn):
buf = ffi.new('git_buf *', (ffi.NULL, 0))
err = fn(buf)
check_error(err, True)
cpath = ffi.string(buf.ptr).decode()
C.git_buf_free(buf)
return Config(cpath)
@staticmethod
def get_system_config():
"""get_system_config() -> Config
Return an object representing the system configuration file."""
return Config._from_found_config(C.git_config_find_system)
@staticmethod
def get_global_config():
"""get_global_config() -> Config
Return an object representing the global configuration file."""
return Config._from_found_config(C.git_config_find_global)
@staticmethod
def get_xdg_config():
"""get_xdg_config() -> Config
Return an object representing the global configuration file."""
return Config._from_found_config(C.git_config_find_xdg)

View File

@@ -25,8 +25,10 @@
# the Free Software Foundation, 51 Franklin Street, Fifth Floor, # the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA. # Boston, MA 02110-1301, USA.
# Import from pygit2 from .ffi import ffi, C
from _pygit2 import GIT_CREDTYPE_USERPASS_PLAINTEXT, GIT_CREDTYPE_SSH_KEY
GIT_CREDTYPE_USERPASS_PLAINTEXT = C.GIT_CREDTYPE_USERPASS_PLAINTEXT
GIT_CREDTYPE_SSH_KEY = C.GIT_CREDTYPE_SSH_KEY
class UserPass(object): class UserPass(object):
"""Username/Password credentials """Username/Password credentials

285
pygit2/decl.h Normal file
View File

@@ -0,0 +1,285 @@
typedef ... git_repository;
typedef ... git_remote;
typedef ... git_refspec;
typedef ... git_push;
typedef ... git_cred;
typedef ... git_diff_file;
typedef ... git_tree;
typedef ... git_config;
typedef ... git_config_iterator;
typedef ... git_signature;
#define GIT_OID_RAWSZ ...
#define GIT_PATH_MAX ...
typedef struct git_oid {
unsigned char id[20];
} git_oid;
typedef struct {
char *ptr;
size_t asize, size;
} git_buf;
void git_buf_free(git_buf *buffer);
typedef struct git_strarray {
char **strings;
size_t count;
} git_strarray;
typedef enum {
GIT_OK = 0,
GIT_ERROR = -1,
GIT_ENOTFOUND = -3,
GIT_EEXISTS = -4,
GIT_EAMBIGUOUS = -5,
GIT_EBUFS = -6,
GIT_EUSER = -7,
GIT_EBAREREPO = -8,
GIT_EUNBORNBRANCH = -9,
GIT_EUNMERGED = -10,
GIT_ENONFASTFORWARD = -11,
GIT_EINVALIDSPEC = -12,
GIT_EMERGECONFLICT = -13,
GIT_ELOCKED = -14,
GIT_PASSTHROUGH = -30,
GIT_ITEROVER = -31,
} git_error_code;
typedef struct {
char *message;
int klass;
} git_error;
const git_error * giterr_last(void);
void git_strarray_free(git_strarray *array);
void git_repository_free(git_repository *repo);
typedef struct git_transfer_progress {
unsigned int total_objects;
unsigned int indexed_objects;
unsigned int received_objects;
unsigned int local_objects;
unsigned int total_deltas;
unsigned int indexed_deltas;
size_t received_bytes;
} git_transfer_progress;
typedef enum git_remote_completion_type {
GIT_REMOTE_COMPLETION_DOWNLOAD,
GIT_REMOTE_COMPLETION_INDEXING,
GIT_REMOTE_COMPLETION_ERROR,
} git_remote_completion_type;
typedef enum {
GIT_DIRECTION_FETCH = 0,
GIT_DIRECTION_PUSH = 1
} git_direction;
typedef enum {
GIT_CREDTYPE_USERPASS_PLAINTEXT = ...,
GIT_CREDTYPE_SSH_KEY = ...,
GIT_CREDTYPE_SSH_CUSTOM = ...,
GIT_CREDTYPE_DEFAULT = ...,
} git_credtype_t;
typedef int (*git_transport_message_cb)(const char *str, int len, void *data);
typedef int (*git_cred_acquire_cb)(
git_cred **cred,
const char *url,
const char *username_from_url,
unsigned int allowed_types,
void *payload);
typedef int (*git_transfer_progress_cb)(const git_transfer_progress *stats, 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_transfer_progress_cb transfer_progress;
int (*update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data);
void *payload;
};
typedef struct git_remote_callbacks git_remote_callbacks;
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_create(git_remote **out,
git_repository *repo,
const char *name,
const char *url);
const char * git_remote_name(const git_remote *remote);
typedef int (*git_remote_rename_problem_cb)(const char *problematic_refspec, void *payload);
int git_remote_rename(git_remote *remote,
const char *new_name,
git_remote_rename_problem_cb callback,
void *payload);
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);
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);
int git_remote_save(const git_remote *remote);
int git_remote_set_callbacks(git_remote *remote, const git_remote_callbacks *callbacks);
size_t git_remote_refspec_count(git_remote *remote);
const git_refspec * git_remote_get_refspec(git_remote *remote, size_t n);
int git_remote_get_fetch_refspecs(git_strarray *array, git_remote *remote);
int git_remote_set_fetch_refspecs(git_remote *remote, git_strarray *array);
int git_remote_get_push_refspecs(git_strarray *array, git_remote *remote);
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);
const char * git_refspec_string(const git_refspec *refspec);
git_direction git_refspec_direction(const git_refspec *spec);
int git_refspec_src_matches(const git_refspec *refspec, const char *refname);
int git_refspec_dst_matches(const git_refspec *refspec, const char *refname);
int git_refspec_transform(git_buf *buf, const git_refspec *spec, const char *name);
int git_refspec_rtransform(git_buf *buf, const git_refspec *spec, const char *name);
int git_cred_userpass_plaintext_new(
git_cred **out,
const char *username,
const char *password);
int git_cred_ssh_key_new(
git_cred **out,
const char *username,
const char *publickey,
const char *privatekey,
const char *passphrase);
typedef enum { ... } git_checkout_notify_t;
typedef int (*git_checkout_notify_cb)(
git_checkout_notify_t why,
const char *path,
const git_diff_file *baseline,
const git_diff_file *target,
const git_diff_file *workdir,
void *payload);
typedef void (*git_checkout_progress_cb)(
const char *path,
size_t completed_steps,
size_t total_steps,
void *payload);
typedef struct git_checkout_options {
unsigned int version;
unsigned int checkout_strategy;
int disable_filters;
unsigned int dir_mode;
unsigned int file_mode;
int file_open_flags;
unsigned int notify_flags;
git_checkout_notify_cb notify_cb;
void *notify_payload;
git_checkout_progress_cb progress_cb;
void *progress_payload;
git_strarray paths;
git_tree *baseline;
const char *target_directory;
const char *ancestor_label;
const char *our_label;
const char *their_label;
} git_checkout_options;
typedef struct git_clone_options {
unsigned int version;
git_checkout_options checkout_opts;
git_remote_callbacks remote_callbacks;
int bare;
int ignore_cert_errors;
const char *remote_name;
const char* checkout_branch;
git_signature *signature;
} git_clone_options;
int git_clone(git_repository **out,
const char *url,
const char *local_path,
const git_clone_options *options);
typedef enum {
GIT_CONFIG_LEVEL_SYSTEM = 1,
GIT_CONFIG_LEVEL_XDG = 2,
GIT_CONFIG_LEVEL_GLOBAL = 3,
GIT_CONFIG_LEVEL_LOCAL = 4,
GIT_CONFIG_LEVEL_APP = 5,
GIT_CONFIG_HIGHEST_LEVEL = -1,
} git_config_level_t;
typedef struct {
const char *name;
const char *value;
git_config_level_t level;
} git_config_entry;
int git_repository_config(git_config **out, git_repository *repo);
void git_config_free(git_config *cfg);
int git_config_get_string(const char **out, const git_config *cfg, const char *name);
int git_config_set_string(git_config *cfg, const char *name, const char *value);
int git_config_set_bool(git_config *cfg, const char *name, int value);
int git_config_set_int64(git_config *cfg, const char *name, int64_t value);
int git_config_parse_bool(int *out, const char *value);
int git_config_parse_int64(int64_t *out, const char *value);
int git_config_delete_entry(git_config *cfg, const char *name);
int git_config_add_file_ondisk(git_config *cfg,
const char *path,
git_config_level_t level,
int force);
int git_config_iterator_new(git_config_iterator **out, const git_config *cfg);
int git_config_next(git_config_entry **entry, git_config_iterator *iter);
void git_config_iterator_free(git_config_iterator *iter);
int git_config_multivar_iterator_new(git_config_iterator **out, const git_config *cfg, const char *name, const char *regexp);
int git_config_set_multivar(git_config *cfg, const char *name, const char *regexp, const char *value);
int git_config_new(git_config **out);
int git_config_open_ondisk(git_config **out, const char *path);
int git_config_find_system(git_buf *out);
int git_config_find_global(git_buf *out);
int git_config_find_xdg(git_buf *out);

58
pygit2/errors.py Normal file
View File

@@ -0,0 +1,58 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
# as published by the Free Software Foundation.
#
# In addition to the permissions in the GNU General Public License,
# the authors give you unlimited permission to link the compiled
# version of this file into combinations with other programs,
# and to distribute those combinations without any restriction
# coming from the use of this file. (The General Public License
# restrictions do apply in other respects; for example, they cover
# modification of the file, and distribution when not linked into
# a combined executable.)
#
# This file is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# Import from the Standard Library
from string import hexdigits
# ffi
from .ffi import ffi, C
from _pygit2 import GitError
def check_error(err, io=False):
if err >= 0:
return
message = "(no message provided)"
giterr = C.giterr_last()
if giterr != ffi.NULL:
message = ffi.string(giterr.message).decode()
if err in [C.GIT_EEXISTS, C.GIT_EINVALIDSPEC, C.GIT_EEXISTS, C.GIT_EAMBIGUOUS]:
raise ValueError(message)
elif err == C.GIT_ENOTFOUND:
if io:
raise IOError(message)
else:
raise KeyError(message)
elif err == C.GIT_EINVALIDSPEC:
raise ValueError(message)
elif err == C.GIT_ITEROVER:
raise StopIteration()
raise GitError(message)

121
pygit2/ffi.py Normal file
View File

@@ -0,0 +1,121 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
# as published by the Free Software Foundation.
#
# In addition to the permissions in the GNU General Public License,
# the authors give you unlimited permission to link the compiled
# version of this file into combinations with other programs,
# and to distribute those combinations without any restriction
# coming from the use of this file. (The General Public License
# restrictions do apply in other respects; for example, they cover
# modification of the file, and distribution when not linked into
# a combined executable.)
#
# This file is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# Import from the future
from __future__ import absolute_import
import inspect
import codecs
from os import path, getenv
from cffi import FFI
import sys
(major_version, _, _, _, _) = sys.version_info
if major_version < 3:
def to_str(s, encoding='utf-8', errors='strict'):
if s == ffi.NULL or s == None:
return ffi.NULL
if isinstance(s, unicode):
encoding = encoding or 'utf-8'
return s.encode(encoding, errors)
return s
else:
def to_str(s, encoding='utf-8', errors='strict'):
if s == ffi.NULL or s == None:
return ffi.NULL
if isinstance(s, bytes):
return s
return s.encode(encoding, errors)
if major_version < 3:
def is_string(s):
return isinstance(s, basestring)
else:
def is_string(s):
return isinstance(s, str)
ffi = FFI()
def strarray_to_strings(arr):
l = [None] * arr.count
for i in range(arr.count):
l[i] = ffi.string(arr.strings[i]).decode()
return l
def strings_to_strarray(l):
"""Convert a list of strings to a git_strarray
We return first the git_strarray* you can pass to libgit2 and a
list of references to the memory, which we must keep around for as
long as the git_strarray must live.
"""
if not isinstance(l, list):
raise TypeError("Value must be a list")
arr = ffi.new('git_strarray *')
strings = ffi.new('char *[]', len(l))
# We need refs in order to keep a reference to the value returned
# by the ffi.new(). Otherwise, they will be freed and the memory
# re-used, with less than great consequences.
refs = [None] * len(l)
for i in range(len(l)):
if not is_string(l[i]):
raise TypeError("Value must be a string")
s = ffi.new('char []', to_str(l[i]))
refs[i] = s
strings[i] = s
arr.strings = strings
arr.count = len(l)
return arr, refs
dir_path = path.dirname(path.abspath(inspect.getfile(inspect.currentframe())))
decl_path = path.join(dir_path, 'decl.h')
with codecs.open(decl_path, 'r', 'utf-8') as header:
ffi.cdef(header.read())
# if LIBGIT2 exists, set build and link against that version
libgit2_path = getenv('LIBGIT2')
include_dirs = []
library_dirs = []
if libgit2_path:
include_dirs = [path.join(libgit2_path, 'include')]
library_dirs = [path.join(libgit2_path, 'lib')]
C = ffi.verify("#include <git2.h>", libraries=["git2"], include_dirs=include_dirs, library_dirs=library_dirs)

96
pygit2/refspec.py Normal file
View File

@@ -0,0 +1,96 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
# as published by the Free Software Foundation.
#
# In addition to the permissions in the GNU General Public License,
# the authors give you unlimited permission to link the compiled
# version of this file into combinations with other programs,
# and to distribute those combinations without any restriction
# coming from the use of this file. (The General Public License
# restrictions do apply in other respects; for example, they cover
# modification of the file, and distribution when not linked into
# a combined executable.)
#
# This file is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# Import from the future
from __future__ import absolute_import
from .ffi import ffi, C, to_str
from .errors import check_error
class Refspec(object):
def __init__(self, owner, ptr):
self._owner = owner
self._refspec = ptr
@property
def src(self):
"""Source or lhs of the refspec"""
return ffi.string(C.git_refspec_src(self._refspec)).decode()
@property
def dst(self):
"""Destinaton or rhs of the refspec"""
return ffi.string(C.git_refspec_dst(self._refspec)).decode()
@property
def force(self):
"""Whether this refspeca llows non-fast-forward updates"""
return bool(C.git_refspec_force(self._refspec))
@property
def string(self):
"""String which was used to create this refspec"""
return ffi.string(C.git_refspec_string(self._refspec)).decode()
@property
def direction(self):
"""Direction of this refspec (fetch or push)"""
return C.git_refspec_direction(self._refspec)
def src_matches(self, ref):
"""src_matches(str) -> Bool
Returns whether the given string matches the source of this refspec"""
return bool(C.git_refspec_src_matches(self._refspec, to_str(ref)))
def dst_matches(self, ref):
"""dst_matches(str) -> Bool
Returns whether the given string matches the destination of this refspec"""
return bool(C.git_refspec_dst_matches(self._refspec, to_str(ref)))
def _transform(self, ref, fn):
buf = ffi.new('git_buf *', (ffi.NULL, 0))
err = fn(buf, self._refspec, to_str(ref))
check_error(err)
try:
return ffi.string(buf.ptr).decode()
finally:
C.git_buf_free(buf)
def transform(self, ref):
"""transform(str) -> str
Transform a reference name according to this refspec from the lhs to the rhs."""
return self._transform(ref, C.git_refspec_transform)
def rtransform(self, ref):
"""transform(str) -> str
Transform a reference name according to this refspec from the lhs to the rhs"""
return self._transform(ref, C.git_refspec_rtransform)

413
pygit2/remote.py Normal file
View File

@@ -0,0 +1,413 @@
# -*- coding: utf-8 -*-
#
# Copyright 2010-2014 The pygit2 contributors
#
# This file is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License, version 2,
# as published by the Free Software Foundation.
#
# In addition to the permissions in the GNU General Public License,
# the authors give you unlimited permission to link the compiled
# version of this file into combinations with other programs,
# and to distribute those combinations without any restriction
# coming from the use of this file. (The General Public License
# restrictions do apply in other respects; for example, they cover
# modification of the file, and distribution when not linked into
# a combined executable.)
#
# This file is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; see the file COPYING. If not, write to
# the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA.
# Import from the future
from __future__ import absolute_import
from _pygit2 import Oid
from .ffi import ffi, C, to_str, strarray_to_strings, strings_to_strarray
from .errors import check_error, GitError
from .refspec import Refspec
def maybe_string(ptr):
if not ptr:
return None
return ffi.string(ptr).decode()
class TransferProgress(object):
"""Progress downloading and indexing data during a fetch"""
def __init__(self, tp):
self.total_objects = tp.total_objects
"""Total number objects to download"""
self.indexed_objects = tp.indexed_objects
"""Objects which have been indexed"""
self.received_objects = tp.received_objects
"""Objects which have been received up to now"""
self.local_objects = tp.local_objects
"""Local objects which were used to fix the thin pack"""
self.total_deltas = tp.total_deltas
"""Total number of deltas in the pack"""
self.indexed_deltas = tp.indexed_deltas
"""Deltas which have been indexed"""
self.received_bytes = tp.received_bytes
""""Number of bytes received up to now"""
class Remote(object):
def sideband_progress(self, string):
"""Progress output callback
Override this function with your own progress reporting function
:param str string: Progress otuput from the remote
"""
pass
def credentials(self, url, username_from_url, allowed_types):
"""Credentials callback
If the remote server requires authentication, this function will
be called and its return value used for authentication. Override
it if you want to be able to perform authentication.
:param str url: The url of the remote
:param username_from_url: Username extracted from the url, if any
:type username_from_url: str or None
:param int allowed_types: credential types supported by the remote
:rtype: credential
"""
pass
def transfer_progress(self, stats):
"""Transfer progress callback
Override with your own function to report transfer progress.
:param TransferProgress stats: The progress up to now
"""
pass
def update_tips(self, refname, old, new):
"""Update tips callabck
Override with your own function to report reference updates
:param str refname: the name of the reference that's being updated
:param Oid old: the reference's old value
:param Oid new: the reference's new value
"""
def __init__(self, repo, ptr):
"""The constructor is for internal use only"""
self._repo = repo
self._remote = ptr
self._stored_exception = None
# Build the callback structure
callbacks = ffi.new('git_remote_callbacks *')
callbacks.version = 1
callbacks.sideband_progress = self._sideband_progress_cb
callbacks.transfer_progress = self._transfer_progress_cb
callbacks.update_tips = self._update_tips_cb
callbacks.credentials = self._credentials_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)
check_error(err)
def __del__(self):
C.git_remote_free(self._remote)
@property
def name(self):
"""Name of the remote"""
return maybe_string(C.git_remote_name(self._remote))
@name.setter
def name(self, value):
if not value:
raise ValueError("New remote name must be a non-empty string")
err = C.git_remote_rename(self._remote, to_str(value), ffi.NULL, ffi.NULL)
check_error(err)
@property
def url(self):
"""Url of the remote"""
return maybe_string(C.git_remote_url(self._remote))
@url.setter
def url(self, value):
err = C.git_remote_set_url(self._remote, to_str(value))
@property
def push_url(self):
"""Push url of the remote"""
return maybe_string(C.git_remote_pushurl(self._remote))
@push_url.setter
def push_url(self, value):
err = C.git_remote_set_pushurl(self._remote, to_str(value))
check_error(err)
def save(self):
"""save()
Save a remote to its repository's configuration"""
err = C.git_remote_save(self._remote)
check_error(err)
def fetch(self, signature=None, message=None):
"""fetch(signature, message) -> TransferProgress
Perform a fetch against this remote.
"""
if signature:
ptr = signature._pointer[:]
else:
ptr = ffi.NULL
self._stored_exception = None
err = C.git_remote_fetch(self._remote, ptr, to_str(message))
if self._stored_exception:
raise self._stored_exception
check_error(err)
return TransferProgress(C.git_remote_stats(self._remote))
@property
def refspec_count(self):
"""Total number of refspecs in this remote"""
return C.git_remote_refspec_count(self._remote)
def get_refspec(self, n):
"""get_refspec(n) -> Refspec
Return the refspec at the given position
"""
spec = C.git_remote_get_refspec(self._remote, n)
return Refspec(self, spec)
@property
def fetch_refspecs(self):
"""Refspecs that will be used for fetching"""
specs = ffi.new('git_strarray *')
err = C.git_remote_get_fetch_refspecs(specs, self._remote)
check_error(err)
return strarray_to_strings(specs)
@fetch_refspecs.setter
def fetch_refspecs(self, l):
arr, refs = strings_to_strarray(l)
err = C.git_remote_set_fetch_refspecs(self._remote, arr)
check_error(err)
@property
def push_refspecs(self):
"""Refspecs that will be used for pushing"""
specs = ffi.new('git_strarray *')
err = C.git_remote_get_push_refspecs(specs, self._remote)
check_error(err)
return strarray_to_strings(specs)
@push_refspecs.setter
def push_refspecs(self, l):
arr, refs = strings_to_strarray(l)
err = C.git_remote_set_push_refspecs(self._remote, arr)
check_error(err)
def add_fetch(self, spec):
"""add_fetch(refspec)
Add a fetch refspec to the remote"""
err = C.git_remote_add_fetch(self._remote, to_str(spec))
def add_push(self, spec):
"""add_push(refspec)
Add a push refspec to the remote"""
err = C.git_remote_add_push(self._remote, to_str(spec))
@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, spec, signature=None, message=None):
"""push(refspec, signature, message)
Push the given refspec to the remote. Raises ``GitError`` on error
:param str spec: push refspec to use
:param Signature signature: signature to use when updating the tips
:param str message: message to use when updating the tips
"""
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_str(spec))
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_str(message))
check_error(err)
finally:
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
@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 (*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
def get_credentials(fn, url, username, allowed):
"""Call fn and return the credentials object"""
url_str = maybe_string(url)
username_str = maybe_string(username)
creds = fn(url_str, username_str, allowed)
if not hasattr(creds, 'credential_type') or not hasattr(creds, 'credential_tuple'):
raise TypeError("credential does not implement interface")
cred_type = creds.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
err = C.git_cred_userpass_plaintext_new(ccred, to_str(name), to_str(passwd))
elif cred_type == C.GIT_CREDTYPE_SSH_KEY:
name, pubkey, privkey, passphrase = creds.credential_tuple
err = C.git_cred_ssh_key_new(ccred, to_str(name),to_str(pubkey),
to_str(privkey), to_str(passphrase))
else:
raise TypeError("unsupported credential type")
check_error(err)
return ccred

View File

@@ -35,9 +35,22 @@ from _pygit2 import Oid, GIT_OID_HEXSZ, GIT_OID_MINPREFIXLEN
from _pygit2 import GIT_CHECKOUT_SAFE_CREATE, GIT_DIFF_NORMAL from _pygit2 import GIT_CHECKOUT_SAFE_CREATE, GIT_DIFF_NORMAL
from _pygit2 import Reference, Tree, Commit, Blob from _pygit2 import Reference, Tree, Commit, Blob
from .ffi import ffi, C, to_str
from .errors import check_error
from .remote import Remote
from .config import Config
class Repository(_Repository): class Repository(_Repository):
def __init__(self, *args, **kwargs):
super(Repository, self).__init__(*args, **kwargs)
# Get the pointer as the contents of a buffer and store it for
# later access
repo_cptr = ffi.new('git_repository **')
ffi.buffer(repo_cptr)[:] = self._pointer[:]
self._repo = repo_cptr[0]
# #
# Mapping interface # Mapping interface
# #
@@ -59,6 +72,62 @@ class Repository(_Repository):
def __repr__(self): def __repr__(self):
return "pygit2.Repository(%r)" % self.path return "pygit2.Repository(%r)" % self.path
#
# Remotes
#
def create_remote(self, name, url):
"""create_remote(name, url) -> Remote
Creates a new remote.
"""
cremote = ffi.new('git_remote **')
err = C.git_remote_create(cremote, self._repo, to_str(name), to_str(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)
#
# Configuration
#
@property
def config(self):
"""The configuration file for this repository
If a the configuration hasn't been set yet, the default config for
repository will be returned, including global and system configurations
(if they are available)."""
cconfig = ffi.new('git_config **')
err = C.git_repository_config(cconfig, self._repo)
check_error(err)
return Config.from_c(self, cconfig[0])
# #
# References # References
# #
@@ -78,7 +147,7 @@ class Repository(_Repository):
Examples:: Examples::
repo.create_reference('refs/heads/foo', repo.head.hex) repo.create_reference('refs/heads/foo', repo.head.target)
repo.create_reference('refs/tags/foo', 'refs/heads/master') repo.create_reference('refs/tags/foo', 'refs/heads/master')
repo.create_reference('refs/tags/foo', 'bbb78a9cec580') repo.create_reference('refs/tags/foo', 'bbb78a9cec580')
""" """

View File

@@ -23,4 +23,4 @@
# the Free Software Foundation, 51 Franklin Street, Fifth Floor, # the Free Software Foundation, 51 Franklin Street, Fifth Floor,
# Boston, MA 02110-1301, USA. # Boston, MA 02110-1301, USA.
__version__ = '0.20.2' __version__ = '0.20.3'

View File

@@ -173,6 +173,11 @@ classifiers = [
with codecs.open('README.rst', 'r', 'utf-8') as readme: with codecs.open('README.rst', 'r', 'utf-8') as readme:
long_description = readme.read() long_description = readme.read()
# This ffi is pygit2.ffi due to the path trick used in the beginning
# of the file
from ffi import ffi
ffi_ext = ffi.verifier.get_extension()
setup(name='pygit2', setup(name='pygit2',
description='Python bindings for libgit2.', description='Python bindings for libgit2.',
keywords='git', keywords='git',
@@ -184,10 +189,13 @@ setup(name='pygit2',
maintainer_email='jdavid.ibp@gmail.com', maintainer_email='jdavid.ibp@gmail.com',
long_description=long_description, long_description=long_description,
packages=['pygit2'], packages=['pygit2'],
package_data={'pygit2': ['decl.h']},
install_requires=['cffi'],
ext_modules=[ ext_modules=[
Extension('_pygit2', pygit2_exts, Extension('_pygit2', pygit2_exts,
include_dirs=[libgit2_include, 'include'], include_dirs=[libgit2_include, 'include'],
library_dirs=[libgit2_lib], library_dirs=[libgit2_lib],
libraries=['git2']), libraries=['git2']),
ffi_ext,
], ],
cmdclass=cmdclass) cmdclass=cmdclass)

View File

@@ -63,7 +63,7 @@ Blob_diff(Blob *self, PyObject *args, PyObject *kwds)
int err; int err;
char *keywords[] = {"blob", "flag", "old_as_path", "new_as_path", NULL}; char *keywords[] = {"blob", "flag", "old_as_path", "new_as_path", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!ssI", keywords, if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O!Iss", keywords,
&BlobType, &py_blob, &opts.flags, &BlobType, &py_blob, &opts.flags,
&old_as_path, &new_as_path)) &old_as_path, &new_as_path))
return NULL; return NULL;
@@ -106,7 +106,7 @@ Blob_diff_to_buffer(Blob *self, PyObject *args, PyObject *kwds)
char *keywords[] = {"buffer", "flag", "old_as_path", "buffer_as_path", char *keywords[] = {"buffer", "flag", "old_as_path", "buffer_as_path",
NULL}; NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwds, "|s#ssI", keywords, if (!PyArg_ParseTupleAndKeywords(args, kwds, "|s#Iss", keywords,
&buffer, &buffer_len, &opts.flags, &buffer, &buffer_len, &opts.flags,
&old_as_path, &buffer_as_path)) &old_as_path, &buffer_as_path))
return NULL; return NULL;
@@ -158,8 +158,58 @@ PyGetSetDef Blob_getseters[] = {
{NULL} {NULL}
}; };
static int
Blob_getbuffer(Blob *self, Py_buffer *view, int flags)
{
return PyBuffer_FillInfo(view, (PyObject *) self,
(void *) git_blob_rawcontent(self->blob),
git_blob_rawsize(self->blob), 1, flags);
}
PyDoc_STRVAR(Blob__doc__, "Blob objects."); #if PY_MAJOR_VERSION == 2
static Py_ssize_t
Blob_getreadbuffer(Blob *self, Py_ssize_t index, const void **ptr)
{
if (index != 0) {
PyErr_SetString(PyExc_SystemError,
"accessing non-existent blob segment");
return -1;
}
*ptr = (void *) git_blob_rawcontent(self->blob);
return git_blob_rawsize(self->blob);
}
static Py_ssize_t
Blob_getsegcount(Blob *self, Py_ssize_t *lenp)
{
if (lenp)
*lenp = git_blob_rawsize(self->blob);
return 1;
}
static PyBufferProcs Blob_as_buffer = {
(readbufferproc)Blob_getreadbuffer,
NULL, /* bf_getwritebuffer */
(segcountproc)Blob_getsegcount,
NULL, /* charbufferproc */
(getbufferproc)Blob_getbuffer,
};
#else
static PyBufferProcs Blob_as_buffer = {
(getbufferproc)Blob_getbuffer,
};
#endif /* python 2 vs python 3 buffers */
PyDoc_STRVAR(Blob__doc__, "Blob object.\n"
"\n"
"Blobs implement the buffer interface, which means you can get access\n"
"to its data via `memoryview(blob)` without the need to create a copy."
);
PyTypeObject BlobType = { PyTypeObject BlobType = {
PyVarObject_HEAD_INIT(NULL, 0) PyVarObject_HEAD_INIT(NULL, 0)
@@ -180,8 +230,14 @@ PyTypeObject BlobType = {
0, /* tp_str */ 0, /* tp_str */
0, /* tp_getattro */ 0, /* tp_getattro */
0, /* tp_setattro */ 0, /* tp_setattro */
0, /* tp_as_buffer */ &Blob_as_buffer, /* tp_as_buffer */
#if PY_MAJOR_VERSION == 2
Py_TPFLAGS_DEFAULT | /* tp_flags */
Py_TPFLAGS_HAVE_GETCHARBUFFER |
Py_TPFLAGS_HAVE_NEWBUFFER,
#else
Py_TPFLAGS_DEFAULT, /* tp_flags */ Py_TPFLAGS_DEFAULT, /* tp_flags */
#endif
Blob__doc__, /* tp_doc */ Blob__doc__, /* tp_doc */
0, /* tp_traverse */ 0, /* tp_traverse */
0, /* tp_clear */ 0, /* tp_clear */

View File

@@ -1,495 +0,0 @@
/*
* Copyright 2010-2014 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include "error.h"
#include "types.h"
#include "utils.h"
#include "config.h"
extern PyTypeObject ConfigType;
extern PyTypeObject ConfigIterType;
PyObject *
wrap_config(char *c_path) {
int err;
PyObject *py_path;
Config *py_config;
py_path = Py_BuildValue("(s)", c_path);
py_config = PyObject_New(Config, &ConfigType);
err = Config_init(py_config, py_path, NULL);
if (err < 0)
return NULL;
return (PyObject*) py_config;
}
int
Config_init(Config *self, PyObject *args, PyObject *kwds)
{
char *path = NULL;
int err;
if (kwds && PyDict_Size(kwds) > 0) {
PyErr_SetString(PyExc_TypeError,
"Config takes no keyword arguments");
return -1;
}
if (!PyArg_ParseTuple(args, "|s", &path))
return -1;
if (path == NULL)
err = git_config_new(&self->config);
else
err = git_config_open_ondisk(&self->config, path);
if (err < 0) {
git_config_free(self->config);
if (err == GIT_ENOTFOUND)
Error_set_exc(PyExc_IOError);
else
Error_set(err);
return -1;
}
return 0;
}
void
Config_dealloc(Config *self)
{
git_config_free(self->config);
Py_TYPE(self)->tp_free(self);
}
PyDoc_STRVAR(Config_get_global_config__doc__,
"get_global_config() -> Config\n"
"\n"
"Return an object representing the global configuration file.");
PyObject *
Config_get_global_config(void)
{
git_buf path = {NULL};
PyObject *py_config;
int err;
err = git_config_find_global(&path);
if (err < 0) {
if (err == GIT_ENOTFOUND) {
PyErr_SetString(PyExc_IOError, "Global config file not found.");
return NULL;
}
return Error_set(err);
}
py_config = wrap_config(path.ptr);
git_buf_free(&path);
return py_config;
}
PyDoc_STRVAR(Config_get_system_config__doc__,
"get_system_config() -> Config\n"
"\n"
"Return an object representing the system configuration file.");
PyObject *
Config_get_system_config(void)
{
git_buf path = {NULL};
PyObject *py_config;
int err;
err = git_config_find_system(&path);
if (err < 0) {
if (err == GIT_ENOTFOUND) {
PyErr_SetString(PyExc_IOError, "System config file not found.");
return NULL;
}
return Error_set(err);
}
py_config = wrap_config(path.ptr);
git_buf_free(&path);
return py_config;
}
int
Config_contains(Config *self, PyObject *py_key) {
int err;
const char *c_value, *c_key;
PyObject *tkey;
c_key = py_str_borrow_c_str(&tkey, py_key, NULL);
if (c_key == NULL)
return -1;
err = git_config_get_string(&c_value, self->config, c_key);
Py_DECREF(tkey);
if (err < 0) {
if (err == GIT_ENOTFOUND)
return 0;
Error_set(err);
return -1;
}
return 1;
}
PyObject *
Config_getitem(Config *self, PyObject *py_key)
{
int64_t value_int;
int err, value_bool;
const char *value_str;
const char *key;
PyObject* py_value, *tmp;
key = py_str_borrow_c_str(&tmp, py_key, NULL);
if (key == NULL)
return NULL;
err = git_config_get_string(&value_str, self->config, key);
Py_CLEAR(tmp);
if (err < 0)
goto cleanup;
if (git_config_parse_int64(&value_int, value_str) == 0)
py_value = PyLong_FromLongLong(value_int);
else if(git_config_parse_bool(&value_bool, value_str) == 0)
py_value = PyBool_FromLong(value_bool);
else
py_value = to_unicode(value_str, NULL, NULL);
cleanup:
if (err < 0) {
if (err == GIT_ENOTFOUND) {
PyErr_SetObject(PyExc_KeyError, py_key);
return NULL;
}
return Error_set(err);
}
return py_value;
}
int
Config_setitem(Config *self, PyObject *py_key, PyObject *py_value)
{
int err;
const char *key, *value;
PyObject *tkey, *tvalue;
key = py_str_borrow_c_str(&tkey, py_key, NULL);
if (key == NULL)
return -1;
if (py_value == NULL)
err = git_config_delete_entry(self->config, key);
else if (PyBool_Check(py_value)) {
err = git_config_set_bool(self->config, key,
(int)PyObject_IsTrue(py_value));
} else if (PyLong_Check(py_value)) {
err = git_config_set_int64(self->config, key,
(int64_t)PyLong_AsLong(py_value));
} else {
value = py_str_borrow_c_str(&tvalue, py_value, NULL);
err = git_config_set_string(self->config, key, value);
Py_DECREF(tvalue);
}
Py_DECREF(tkey);
if (err < 0) {
Error_set(err);
return -1;
}
return 0;
}
PyDoc_STRVAR(Config_add_file__doc__,
"add_file(path, level=0, force=0)\n"
"\n"
"Add a config file instance to an existing config.");
PyObject *
Config_add_file(Config *self, PyObject *args, PyObject *kwds)
{
char *keywords[] = {"path", "level", "force", NULL};
int err;
char *path;
unsigned int level = 0;
int force = 0;
if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|Ii", keywords,
&path, &level, &force))
return NULL;
err = git_config_add_file_ondisk(self->config, path, level, force);
if (err < 0)
return Error_set_str(err, path);
Py_RETURN_NONE;
}
PyDoc_STRVAR(Config_get_multivar__doc__,
"get_multivar(name[, regex]) -> [str, ...]\n"
"\n"
"Get each value of a multivar ''name'' as a list. The optional ''regex''\n"
"parameter is expected to be a regular expression to filter the variables\n"
"we're interested in.");
PyObject *
Config_get_multivar(Config *self, PyObject *args)
{
int err;
PyObject *list;
const char *name = NULL;
const char *regex = NULL;
git_config_iterator *iter;
git_config_entry *entry;
if (!PyArg_ParseTuple(args, "s|s", &name, &regex))
return NULL;
list = PyList_New(0);
err = git_config_multivar_iterator_new(&iter, self->config, name, regex);
if (err < 0)
return Error_set(err);
while ((err = git_config_next(&entry, iter)) == 0) {
PyObject *item;
item = to_unicode(entry->value, NULL, NULL);
if (item == NULL) {
git_config_iterator_free(iter);
return NULL;
}
PyList_Append(list, item);
Py_CLEAR(item);
}
git_config_iterator_free(iter);
if (err == GIT_ITEROVER)
err = 0;
if (err < 0)
return Error_set(err);
return list;
}
PyDoc_STRVAR(Config_set_multivar__doc__,
"set_multivar(name, regex, value)\n"
"\n"
"Set a multivar ''name'' to ''value''. ''regexp'' is a regular expression\n"
"to indicate which values to replace");
PyObject *
Config_set_multivar(Config *self, PyObject *args)
{
int err;
const char *name = NULL;
const char *regex = NULL;
const char *value = NULL;
if (!PyArg_ParseTuple(args, "sss", &name, &regex, &value))
return NULL;
err = git_config_set_multivar(self->config, name, regex, value);
if (err < 0) {
if (err == GIT_ENOTFOUND)
Error_set(err);
else
PyErr_SetNone(PyExc_TypeError);
return NULL;
}
Py_RETURN_NONE;
}
PyObject *
Config_iter(Config *self)
{
ConfigIter *iter;
int err;
iter = PyObject_New(ConfigIter, &ConfigIterType);
if (!iter)
return NULL;
if ((err = git_config_iterator_new(&iter->iter, self->config)) < 0)
return Error_set(err);
Py_INCREF(self);
iter->owner = self;
return (PyObject*)iter;
}
PyMethodDef Config_methods[] = {
METHOD(Config, get_system_config, METH_NOARGS | METH_STATIC),
METHOD(Config, get_global_config, METH_NOARGS | METH_STATIC),
METHOD(Config, add_file, METH_VARARGS | METH_KEYWORDS),
METHOD(Config, get_multivar, METH_VARARGS),
METHOD(Config, set_multivar, METH_VARARGS),
{NULL}
};
PySequenceMethods Config_as_sequence = {
0, /* sq_length */
0, /* sq_concat */
0, /* sq_repeat */
0, /* sq_item */
0, /* sq_slice */
0, /* sq_ass_item */
0, /* sq_ass_slice */
(objobjproc)Config_contains, /* sq_contains */
};
PyMappingMethods Config_as_mapping = {
0, /* mp_length */
(binaryfunc)Config_getitem, /* mp_subscript */
(objobjargproc)Config_setitem, /* mp_ass_subscript */
};
PyDoc_STRVAR(Config__doc__, "Configuration management.");
PyTypeObject ConfigType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.Config", /* tp_name */
sizeof(Config), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)Config_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
&Config_as_sequence, /* tp_as_sequence */
&Config_as_mapping, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
Config__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
(getiterfunc)Config_iter, /* tp_iter */
0, /* tp_iternext */
Config_methods, /* tp_methods */
0, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
(initproc)Config_init, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
void
ConfigIter_dealloc(ConfigIter *self)
{
Py_CLEAR(self->owner);
git_config_iterator_free(self->iter);
PyObject_Del(self);
}
PyObject *
ConfigIter_iternext(ConfigIter *self)
{
int err;
git_config_entry *entry;
if ((err = git_config_next(&entry, self->iter)) < 0)
return Error_set(err);
return Py_BuildValue("ss", entry->name, entry->value);
}
PyDoc_STRVAR(ConfigIter__doc__, "Configuration iterator.");
PyTypeObject ConfigIterType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.ConfigIter", /* tp_name */
sizeof(ConfigIter), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)ConfigIter_dealloc , /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
ConfigIter__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
PyObject_SelfIter, /* tp_iter */
(iternextfunc)ConfigIter_iternext, /* tp_iternext */
};

View File

@@ -1,45 +0,0 @@
/*
* Copyright 2010-2014 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef INCLUDE_pygit2_config_h
#define INCLUDE_pygit2_config_h
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <git2.h>
PyObject* wrap_config(char *c_path);
PyObject* Config_get_global_config(void);
PyObject* Config_get_system_config(void);
PyObject* Config_add_file(Config *self, PyObject *args, PyObject *kwds);
PyObject* Config_getitem(Config *self, PyObject *key);
PyObject* Config_foreach(Config *self, PyObject *args);
PyObject* Config_get_multivar(Config *self, PyObject *args);
PyObject* Config_set_multivar(Config *self, PyObject *args);
int Config_init(Config *self, PyObject *args, PyObject *kwds);
int Config_setitem(Config *self, PyObject *key, PyObject *value);
#endif

View File

@@ -56,8 +56,6 @@ extern PyTypeObject IndexType;
extern PyTypeObject IndexEntryType; extern PyTypeObject IndexEntryType;
extern PyTypeObject IndexIterType; extern PyTypeObject IndexIterType;
extern PyTypeObject WalkerType; extern PyTypeObject WalkerType;
extern PyTypeObject ConfigType;
extern PyTypeObject ConfigIterType;
extern PyTypeObject ReferenceType; extern PyTypeObject ReferenceType;
extern PyTypeObject RefLogIterType; extern PyTypeObject RefLogIterType;
extern PyTypeObject RefLogEntryType; extern PyTypeObject RefLogEntryType;
@@ -65,7 +63,6 @@ extern PyTypeObject BranchType;
extern PyTypeObject SignatureType; extern PyTypeObject SignatureType;
extern PyTypeObject RemoteType; extern PyTypeObject RemoteType;
extern PyTypeObject RefspecType; extern PyTypeObject RefspecType;
extern PyTypeObject TransferProgressType;
extern PyTypeObject NoteType; extern PyTypeObject NoteType;
extern PyTypeObject NoteIterType; extern PyTypeObject NoteIterType;
extern PyTypeObject BlameType; extern PyTypeObject BlameType;
@@ -105,69 +102,6 @@ init_repository(PyObject *self, PyObject *args) {
Py_RETURN_NONE; Py_RETURN_NONE;
}; };
static int
credentials_cb(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, void *data)
{
PyObject *credentials = (PyObject *) data;
return callable_to_credentials(out, url, username_from_url, allowed_types, credentials);
}
PyDoc_STRVAR(clone_repository__doc__,
"clone_repository(url, path, bare, remote_name, checkout_branch)\n"
"\n"
"Clones a Git repository in the given url to the given path "
"with the specified options.\n"
"\n"
"Arguments:\n"
"\n"
"url\n"
" Git repository remote url.\n"
"path\n"
" Path where to create the repository.\n"
"bare\n"
" If 'bare' is not 0, then a bare git repository will be created.\n"
"remote_name\n"
" The name given to the 'origin' remote. The default is 'origin'.\n"
"checkout_branch\n"
" The name of the branch to checkout. None means use the remote's "
"HEAD.\n");
PyObject *
clone_repository(PyObject *self, PyObject *args) {
git_repository *repo;
const char *url;
const char *path;
unsigned int bare, ignore_cert_errors;
const char *remote_name, *checkout_branch;
PyObject *credentials = NULL;
int err;
git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
if (!PyArg_ParseTuple(args, "zzIIzzO",
&url, &path, &bare, &ignore_cert_errors, &remote_name, &checkout_branch, &credentials))
return NULL;
opts.bare = bare;
opts.ignore_cert_errors = ignore_cert_errors;
opts.remote_name = remote_name;
opts.checkout_branch = checkout_branch;
if (credentials != Py_None) {
opts.remote_callbacks.credentials = credentials_cb;
opts.remote_callbacks.payload = credentials;
}
err = git_clone(&repo, url, path, &opts);
if (err < 0)
return Error_set(err);
git_repository_free(repo);
Py_RETURN_NONE;
};
PyDoc_STRVAR(discover_repository__doc__, PyDoc_STRVAR(discover_repository__doc__,
"discover_repository(path[, across_fs[, ceiling_dirs]]) -> str\n" "discover_repository(path[, across_fs[, ceiling_dirs]]) -> str\n"
"\n" "\n"
@@ -246,8 +180,6 @@ hash(PyObject *self, PyObject *args)
PyMethodDef module_methods[] = { PyMethodDef module_methods[] = {
{"init_repository", init_repository, METH_VARARGS, init_repository__doc__}, {"init_repository", init_repository, METH_VARARGS, init_repository__doc__},
{"clone_repository", clone_repository, METH_VARARGS,
clone_repository__doc__},
{"discover_repository", discover_repository, METH_VARARGS, {"discover_repository", discover_repository, METH_VARARGS,
discover_repository__doc__}, discover_repository__doc__},
{"hashfile", hashfile, METH_VARARGS, hashfile__doc__}, {"hashfile", hashfile, METH_VARARGS, hashfile__doc__},
@@ -444,25 +376,6 @@ moduleinit(PyObject* m)
ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_XDG); ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_XDG);
ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_SYSTEM); ADD_CONSTANT_INT(m, GIT_CONFIG_LEVEL_SYSTEM);
INIT_TYPE(ConfigType, NULL, PyType_GenericNew)
INIT_TYPE(ConfigIterType, NULL, NULL)
ADD_TYPE(m, Config)
ADD_TYPE(m, ConfigIter)
/* Remotes */
INIT_TYPE(RemoteType, NULL, NULL)
INIT_TYPE(RefspecType, NULL, NULL)
INIT_TYPE(TransferProgressType, NULL, NULL)
ADD_TYPE(m, Remote)
ADD_TYPE(m, Refspec)
ADD_TYPE(m, TransferProgress)
/* Direction for the refspec */
ADD_CONSTANT_INT(m, GIT_DIRECTION_FETCH)
ADD_CONSTANT_INT(m, GIT_DIRECTION_PUSH)
/* Credential types */
ADD_CONSTANT_INT(m, GIT_CREDTYPE_USERPASS_PLAINTEXT)
ADD_CONSTANT_INT(m, GIT_CREDTYPE_SSH_KEY)
/* Blame */ /* Blame */
INIT_TYPE(BlameType, NULL, NULL) INIT_TYPE(BlameType, NULL, NULL)
INIT_TYPE(BlameIterType, NULL, NULL) INIT_TYPE(BlameIterType, NULL, NULL)

View File

@@ -1,275 +0,0 @@
/*
* Copyright 2010-2014 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#include "error.h"
#include "types.h"
#include "utils.h"
#include "refspec.h"
extern PyTypeObject RefspecType;
Refspec *
wrap_refspec(const Remote *owner, const git_refspec *refspec)
{
Refspec *spec;
spec = PyObject_New(Refspec, &RefspecType);
if (!spec)
return NULL;
Py_INCREF(owner);
spec->owner = owner;
spec->refspec = refspec;
return spec;
}
PyDoc_STRVAR(Refspec_direction__doc__,
"The direction of this refspec (fetch or push)");
PyObject *
Refspec_direction__get__(Refspec *self)
{
return Py_BuildValue("i", git_refspec_direction(self->refspec));
}
PyDoc_STRVAR(Refspec_src__doc__, "Source or lhs of the refspec");
PyObject *
Refspec_src__get__(Refspec *self)
{
return to_unicode(git_refspec_src(self->refspec), NULL, NULL);
}
PyDoc_STRVAR(Refspec_dst__doc__, "Destination or rhs of the refspec");
PyObject *
Refspec_dst__get__(Refspec *self)
{
return to_unicode(git_refspec_dst(self->refspec), NULL, NULL);
}
PyDoc_STRVAR(Refspec_string__doc__, "String used to create this refspec");
PyObject *
Refspec_string__get__(Refspec *self)
{
return to_unicode(git_refspec_string(self->refspec), NULL, NULL);
}
PyDoc_STRVAR(Refspec_force__doc__,
"Whether this refspec allows non-fast-forward updates");
PyObject *
Refspec_force__get__(Refspec *self)
{
if (git_refspec_force(self->refspec))
Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
PyDoc_STRVAR(Refspec_src_matches__doc__,
"src_matches(str) -> Bool\n"
"\n"
"Returns whether the string matches the source refspec\n");
PyObject *
Refspec_src_matches(Refspec *self, PyObject *py_str)
{
const char *str;
PyObject *tstr;
int res;
str = py_str_borrow_c_str(&tstr, py_str, NULL);
if (!str)
return NULL;
res = git_refspec_src_matches(self->refspec, str);
Py_DECREF(tstr);
if (res)
Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
PyDoc_STRVAR(Refspec_dst_matches__doc__,
"dst_matches(str) -> Bool\n"
"\n"
"Returns whether the string matches the destination refspec\n");
PyObject *
Refspec_dst_matches(Refspec *self, PyObject *py_str)
{
const char *str;
PyObject *tstr;
int res;
str = py_str_borrow_c_str(&tstr, py_str, NULL);
if (!str)
return NULL;
res = git_refspec_dst_matches(self->refspec, str);
Py_DECREF(tstr);
if (res)
Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
PyDoc_STRVAR(Refspec_transform__doc__,
"transform(str) -> str\n"
"\n"
"Transform a reference according to the refspec\n");
PyObject *
Refspec_transform(Refspec *self, PyObject *py_str)
{
git_buf trans = {NULL};
const char *str;
int err;
PyObject *py_trans, *tstr;
str = py_str_borrow_c_str(&tstr, py_str, NULL);
err = git_refspec_transform(&trans, self->refspec, str);
Py_DECREF(tstr);
if (err < 0) {
Error_set(err);
return NULL;
}
py_trans = to_unicode(trans.ptr, NULL, NULL);
git_buf_free(&trans);
return py_trans;
}
PyDoc_STRVAR(Refspec_rtransform__doc__,
"rtransform(str) -> str\n"
"\n"
"Transform a reference according to the refspec in reverse\n");
PyObject *
Refspec_rtransform(Refspec *self, PyObject *py_str)
{
git_buf trans = {NULL};
const char *str;
int err;
PyObject *py_trans, *tstr;
str = py_str_borrow_c_str(&tstr, py_str, NULL);
err = git_refspec_rtransform(&trans, self->refspec, str);
Py_DECREF(tstr);
if (err < 0) {
Error_set(err);
return NULL;
}
py_trans = to_unicode(trans.ptr, NULL, NULL);
git_buf_free(&trans);
return py_trans;
}
PyMethodDef Refspec_methods[] = {
METHOD(Refspec, src_matches, METH_O),
METHOD(Refspec, dst_matches, METH_O),
METHOD(Refspec, transform, METH_O),
METHOD(Refspec, rtransform, METH_O),
{NULL}
};
PyGetSetDef Refspec_getseters[] = {
GETTER(Refspec, direction),
GETTER(Refspec, src),
GETTER(Refspec, dst),
GETTER(Refspec, string),
GETTER(Refspec, force),
{NULL}
};
static void
Refspec_dealloc(Refspec *self)
{
Py_CLEAR(self->owner);
PyObject_Del(self);
}
PyDoc_STRVAR(Refspec__doc__, "Refspec object.");
PyTypeObject RefspecType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.Refspec", /* tp_name */
sizeof(Refspec), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)Refspec_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
Refspec__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
Refspec_methods, /* tp_methods */
0, /* tp_members */
Refspec_getseters, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};

View File

@@ -1,38 +0,0 @@
/*
* Copyright 2010-2014 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef INCLUDE_pygit2_refspec_h
#define INCLUDE_pygit2_refspec_h
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <git2.h>
#include <git2/refspec.h>
Refspec* wrap_refspec(const Remote *owner, const git_refspec *refspec);
#endif

View File

@@ -1,728 +0,0 @@
/*
* Copyright 2010-2014 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <structmember.h>
#include "error.h"
#include "types.h"
#include "utils.h"
#include "oid.h"
#include "refspec.h"
#include "remote.h"
extern PyObject *GitError;
extern PyTypeObject RepositoryType;
extern PyTypeObject TransferProgressType;
PyObject *
wrap_transfer_progress(const git_transfer_progress *stats)
{
TransferProgress *py_stats;
py_stats = PyObject_New(TransferProgress, &TransferProgressType);
if (!py_stats)
return NULL;
py_stats->total_objects = stats->total_objects;
py_stats->indexed_objects = stats->indexed_objects;
py_stats->received_objects = stats->received_objects;
py_stats->local_objects = stats->local_objects;
py_stats->total_deltas = stats->total_deltas;
py_stats->indexed_deltas = stats->indexed_deltas;
py_stats->received_bytes = stats->received_bytes;
return (PyObject *) py_stats;
}
void
TransferProgress_dealloc(TransferProgress *self)
{
PyObject_Del(self);
}
PyMemberDef TransferProgress_members[] = {
RMEMBER(TransferProgress, total_objects, T_UINT,
"Total number objects to download"),
RMEMBER(TransferProgress, indexed_objects, T_UINT,
"Objects which have been indexed"),
RMEMBER(TransferProgress, received_objects, T_UINT,
"Objects which have been received up to now"),
RMEMBER(TransferProgress, local_objects, T_UINT,
"Local objects which were used to fix the thin pack"),
RMEMBER(TransferProgress, total_deltas, T_UINT,
"Total number of deltas in the pack"),
RMEMBER(TransferProgress, indexed_deltas, T_UINT,
"Deltas which have been indexed"),
/* FIXME: technically this is unsigned, but there's no value for size_t
* here. */
RMEMBER(TransferProgress, received_bytes, T_PYSSIZET,
"Number of bytes received up to now"),
{NULL},
};
PyDoc_STRVAR(TransferProgress__doc__,
"Progress downloading and indexing data during a fetch");
PyTypeObject TransferProgressType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.TransferProgress", /* tp_name */
sizeof(TransferProgress), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)TransferProgress_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
TransferProgress__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
0, /* tp_methods */
TransferProgress_members, /* tp_members */
0, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
static int
progress_cb(const char *str, int len, void *data)
{
Remote *remote = (Remote *) data;
PyObject *arglist, *ret;
if (remote->progress == NULL)
return 0;
if (!PyCallable_Check(remote->progress)) {
PyErr_SetString(PyExc_TypeError, "progress callback is not callable");
return -1;
}
arglist = Py_BuildValue("(s#)", str, len);
ret = PyObject_CallObject(remote->progress, arglist);
Py_DECREF(arglist);
if (!ret)
return -1;
Py_DECREF(ret);
return 0;
}
static int
credentials_cb(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, void *data)
{
Remote *remote = (Remote *) data;
return callable_to_credentials(out, url, username_from_url, allowed_types, remote->credentials);
}
static int
transfer_progress_cb(const git_transfer_progress *stats, void *data)
{
Remote *remote = (Remote *) data;
PyObject *py_stats, *ret;
if (remote->transfer_progress == NULL)
return 0;
if (!PyCallable_Check(remote->transfer_progress)) {
PyErr_SetString(PyExc_TypeError, "transfer progress callback is not callable");
return -1;
}
py_stats = wrap_transfer_progress(stats);
if (!py_stats)
return -1;
ret = PyObject_CallFunctionObjArgs(remote->transfer_progress, py_stats, NULL);
Py_DECREF(py_stats);
if (!ret)
return -1;
Py_DECREF(ret);
return 0;
}
static int
update_tips_cb(const char *refname, const git_oid *a, const git_oid *b, void *data)
{
Remote *remote = (Remote *) data;
PyObject *ret;
PyObject *old, *new;
if (remote->update_tips == NULL)
return 0;
if (!PyCallable_Check(remote->update_tips)) {
PyErr_SetString(PyExc_TypeError, "update tips callback is not callable");
return -1;
}
old = git_oid_to_python(a);
new = git_oid_to_python(b);
ret = PyObject_CallFunction(remote->update_tips, "(s,O,O)", refname, old ,new);
Py_DECREF(old);
Py_DECREF(new);
if (!ret)
return -1;
Py_DECREF(ret);
return 0;
}
static void
Remote_dealloc(Remote *self)
{
Py_CLEAR(self->repo);
Py_CLEAR(self->progress);
git_remote_free(self->remote);
PyObject_Del(self);
}
PyDoc_STRVAR(Remote_name__doc__, "Name of the remote refspec");
PyObject *
Remote_name__get__(Remote *self)
{
return to_unicode(git_remote_name(self->remote), NULL, NULL);
}
int
Remote_name__set__(Remote *self, PyObject* py_name)
{
int err;
const char* name;
PyObject *tname;
name = py_str_borrow_c_str(&tname, py_name, NULL);
if (name != NULL) {
err = git_remote_rename(self->remote, name, NULL, NULL);
Py_DECREF(tname);
if (err == GIT_OK)
return 0;
Error_set(err);
}
return -1;
}
PyDoc_STRVAR(Remote_fetch_refspecs__doc__, "Fetch refspecs");
PyObject *
Remote_fetch_refspecs__get__(Remote *self)
{
int err;
git_strarray refspecs;
PyObject *new_list;
err = git_remote_get_fetch_refspecs(&refspecs, self->remote);
if (err != GIT_OK)
return Error_set(err);
new_list = get_pylist_from_git_strarray(&refspecs);
git_strarray_free(&refspecs);
return new_list;
}
int
Remote_fetch_refspecs__set__(Remote *self, PyObject *py_list)
{
int err;
git_strarray fetch_refspecs;
if (get_strarraygit_from_pylist(&fetch_refspecs, py_list) < 0)
return -1;
err = git_remote_set_fetch_refspecs(self->remote, &fetch_refspecs);
git_strarray_free(&fetch_refspecs);
if (err < 0) {
Error_set(err);
return -1;
}
return 0;
}
PyDoc_STRVAR(Remote_push_refspecs__doc__, "Push refspecs");
PyObject *
Remote_push_refspecs__get__(Remote *self)
{
int err;
git_strarray refspecs;
PyObject *new_list;
err = git_remote_get_push_refspecs(&refspecs, self->remote);
if (err != GIT_OK)
return Error_set(err);
new_list = get_pylist_from_git_strarray(&refspecs);
git_strarray_free(&refspecs);
return new_list;
}
int
Remote_push_refspecs__set__(Remote *self, PyObject *py_list)
{
int err;
git_strarray push_refspecs;
if (get_strarraygit_from_pylist(&push_refspecs, py_list) != 0)
return -1;
err = git_remote_set_push_refspecs(self->remote, &push_refspecs);
git_strarray_free(&push_refspecs);
if (err < 0) {
Error_set(err);
return -1;
}
return 0;
}
PyDoc_STRVAR(Remote_url__doc__, "Url of the remote");
PyObject *
Remote_url__get__(Remote *self)
{
const char *url;
url = git_remote_url(self->remote);
if (!url)
Py_RETURN_NONE;
return to_unicode(url, NULL, NULL);
}
int
Remote_url__set__(Remote *self, PyObject* py_url)
{
int err;
const char* url = NULL;
PyObject *turl;
url = py_str_borrow_c_str(&turl, py_url, NULL);
if (url != NULL) {
err = git_remote_set_url(self->remote, url);
Py_DECREF(turl);
if (err == GIT_OK)
return 0;
Error_set(err);
}
return -1;
}
PyDoc_STRVAR(Remote_push_url__doc__, "Push url of the remote");
PyObject *
Remote_push_url__get__(Remote *self)
{
const char *url;
url = git_remote_pushurl(self->remote);
if (!url)
Py_RETURN_NONE;
return to_unicode(url, NULL, NULL);
}
int
Remote_push_url__set__(Remote *self, PyObject* py_url)
{
int err;
const char* url = NULL;
PyObject *turl;
url = py_str_borrow_c_str(&turl, py_url, NULL);
if (url != NULL) {
err = git_remote_set_pushurl(self->remote, url);
Py_DECREF(turl);
if (err == GIT_OK)
return 0;
Error_set(err);
}
return -1;
}
PyDoc_STRVAR(Remote_refspec_count__doc__, "Number of refspecs.");
PyObject *
Remote_refspec_count__get__(Remote *self)
{
size_t count;
count = git_remote_refspec_count(self->remote);
return PyLong_FromSize_t(count);
}
PyDoc_STRVAR(Remote_get_refspec__doc__,
"get_refspec(n) -> (str, str)\n"
"\n"
"Return the refspec at the given position.");
PyObject *
Remote_get_refspec(Remote *self, PyObject *value)
{
size_t n;
const git_refspec *refspec;
n = PyLong_AsSize_t(value);
if (PyErr_Occurred())
return NULL;
refspec = git_remote_get_refspec(self->remote, n);
if (refspec == NULL) {
PyErr_SetObject(PyExc_IndexError, value);
return NULL;
}
return (PyObject*) wrap_refspec(self, refspec);
}
PyDoc_STRVAR(Remote_fetch__doc__,
"fetch() -> {'indexed_objects': int, 'received_objects' : int,"
" 'received_bytesa' : int}\n"
"\n"
"Negotiate what objects should be downloaded and download the\n"
"packfile with those objects");
PyObject *
Remote_fetch(Remote *self, PyObject *args)
{
PyObject* py_stats = NULL;
const git_transfer_progress *stats;
int err;
PyErr_Clear();
err = git_remote_fetch(self->remote, NULL, NULL);
/*
* XXX: We should be checking for GIT_EUSER, but on v0.20, this does not
* make it all the way to us for update_tips
*/
if (err < 0 && PyErr_Occurred())
return NULL;
if (err < 0)
return Error_set(err);
stats = git_remote_stats(self->remote);
py_stats = Py_BuildValue("{s:I,s:I,s:n}",
"indexed_objects", stats->indexed_objects,
"received_objects", stats->received_objects,
"received_bytes", stats->received_bytes);
return (PyObject*) py_stats;
}
PyDoc_STRVAR(Remote_save__doc__,
"save()\n\n"
"Save a remote to its repository configuration.");
PyObject *
Remote_save(Remote *self, PyObject *args)
{
int err;
err = git_remote_save(self->remote);
if (err == GIT_OK) {
Py_RETURN_NONE;
}
else {
return Error_set(err);
}
}
int
push_status_foreach_callback(const char *ref, const char *msg, void *data)
{
const char **msg_dst = (const char **)data;
if (msg != NULL && *msg_dst == NULL)
*msg_dst = msg;
return 0;
}
PyDoc_STRVAR(Remote_push__doc__,
"push(refspec)\n"
"\n"
"Push the given refspec to the remote. Raises ``GitError`` on error.");
PyObject *
Remote_push(Remote *self, PyObject *args)
{
git_push *push = NULL;
const char *refspec = NULL;
const char *msg = NULL;
int err;
if (!PyArg_ParseTuple(args, "s", &refspec))
return NULL;
err = git_push_new(&push, self->remote);
if (err < 0)
return Error_set(err);
err = git_push_add_refspec(push, refspec);
if (err < 0)
goto error;
err = git_push_finish(push);
if (err < 0)
goto error;
if (!git_push_unpack_ok(push)) {
git_push_free(push);
PyErr_SetString(GitError, "Remote failed to unpack objects");
return NULL;
}
err = git_push_status_foreach(push, push_status_foreach_callback, &msg);
if (err < 0)
goto error;
if (msg != NULL) {
git_push_free(push);
PyErr_SetString(GitError, msg);
return NULL;
}
err = git_push_update_tips(push, NULL, NULL);
if (err < 0)
goto error;
git_push_free(push);
Py_RETURN_NONE;
error:
git_push_free(push);
return Error_set(err);
}
PyDoc_STRVAR(Remote_add_push__doc__,
"add_push(refspec)\n"
"\n"
"Add a push refspec to the remote.");
PyObject *
Remote_add_push(Remote *self, PyObject *args)
{
git_remote *remote;
char *refspec = NULL;
int err = 0;
if (!PyArg_ParseTuple(args, "s", &refspec))
return NULL;
remote = self->remote;
err = git_remote_add_push(remote, refspec);
if (err < 0)
return Error_set(err);
Py_RETURN_NONE;
}
PyDoc_STRVAR(Remote_add_fetch__doc__,
"add_fetch(refspec)\n"
"\n"
"Add a fetch refspec to the remote.");
PyObject *
Remote_add_fetch(Remote *self, PyObject *args)
{
git_remote *remote;
char *refspec = NULL;
int err = 0;
if (!PyArg_ParseTuple(args, "s", &refspec))
return NULL;
remote = self->remote;
err = git_remote_add_fetch(remote, refspec);
if (err < 0)
return Error_set(err);
Py_RETURN_NONE;
}
PyMethodDef Remote_methods[] = {
METHOD(Remote, fetch, METH_NOARGS),
METHOD(Remote, save, METH_NOARGS),
METHOD(Remote, get_refspec, METH_O),
METHOD(Remote, push, METH_VARARGS),
METHOD(Remote, add_push, METH_VARARGS),
METHOD(Remote, add_fetch, METH_VARARGS),
{NULL}
};
PyGetSetDef Remote_getseters[] = {
GETSET(Remote, name),
GETSET(Remote, url),
GETSET(Remote, push_url),
GETTER(Remote, refspec_count),
GETSET(Remote, fetch_refspecs),
GETSET(Remote, push_refspecs),
{NULL}
};
PyMemberDef Remote_members[] = {
MEMBER(Remote, progress, T_OBJECT_EX, "Progress output callback"),
MEMBER(Remote, credentials, T_OBJECT_EX,
"credentials(url, username_from_url, allowed_types) -> credential\n"
"\n"
"Credentials callback\n"
"\n"
"If the remote server requires authentication, this function will\n"
"be called and its return value used for authentication.\n"
"\n"
":param str url: The url of the remote\n"
":param username_from_url: Username extracted from the url, if any\n"
":type username_from_url: str or None\n"
":param int allowed_types: credential types supported by the remote "),
MEMBER(Remote, transfer_progress, T_OBJECT_EX, "Transfer progress callback"),
MEMBER(Remote, update_tips, T_OBJECT_EX, "update tips callback"),
{NULL},
};
PyDoc_STRVAR(Remote__doc__, "Remote object.");
PyTypeObject RemoteType = {
PyVarObject_HEAD_INIT(NULL, 0)
"_pygit2.Remote", /* tp_name */
sizeof(Remote), /* tp_basicsize */
0, /* tp_itemsize */
(destructor)Remote_dealloc, /* tp_dealloc */
0, /* tp_print */
0, /* tp_getattr */
0, /* tp_setattr */
0, /* tp_compare */
0, /* tp_repr */
0, /* tp_as_number */
0, /* tp_as_sequence */
0, /* tp_as_mapping */
0, /* tp_hash */
0, /* tp_call */
0, /* tp_str */
0, /* tp_getattro */
0, /* tp_setattro */
0, /* tp_as_buffer */
Py_TPFLAGS_DEFAULT, /* tp_flags */
Remote__doc__, /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
Remote_methods, /* tp_methods */
Remote_members, /* tp_members */
Remote_getseters, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
0, /* tp_init */
0, /* tp_alloc */
0, /* tp_new */
};
PyObject *
wrap_remote(git_remote *c_remote, Repository *repo)
{
Remote *py_remote = NULL;
git_remote_callbacks callbacks = GIT_REMOTE_CALLBACKS_INIT;
py_remote = PyObject_New(Remote, &RemoteType);
if (py_remote) {
Py_INCREF(repo);
py_remote->repo = repo;
py_remote->remote = c_remote;
py_remote->progress = NULL;
py_remote->credentials = NULL;
py_remote->transfer_progress = NULL;
py_remote->update_tips = NULL;
callbacks.progress = progress_cb;
callbacks.credentials = credentials_cb;
callbacks.transfer_progress = transfer_progress_cb;
callbacks.update_tips = update_tips_cb;
callbacks.payload = py_remote;
git_remote_set_callbacks(c_remote, &callbacks);
}
return (PyObject *)py_remote;
}

View File

@@ -1,39 +0,0 @@
/*
* Copyright 2010-2014 The pygit2 contributors
*
* This file is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License, version 2,
* as published by the Free Software Foundation.
*
* In addition to the permissions in the GNU General Public License,
* the authors give you unlimited permission to link the compiled
* version of this file into combinations with other programs,
* and to distribute those combinations without any restriction
* coming from the use of this file. (The General Public License
* restrictions do apply in other respects; for example, they cover
* modification of the file, and distribution when not linked into
* a combined executable.)
*
* This file is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#ifndef INCLUDE_pygit2_remote_h
#define INCLUDE_pygit2_remote_h
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <git2.h>
#include <git2/remote.h>
PyObject* Remote_fetch(Remote *self, PyObject *args);
PyObject* wrap_remote(git_remote *c_remote, Repository *repo);
#endif

View File

@@ -35,7 +35,6 @@
#include "oid.h" #include "oid.h"
#include "note.h" #include "note.h"
#include "repository.h" #include "repository.h"
#include "remote.h"
#include "branch.h" #include "branch.h"
#include "blame.h" #include "blame.h"
#include "signature.h" #include "signature.h"
@@ -53,7 +52,6 @@ extern PyTypeObject TreeType;
extern PyTypeObject TreeBuilderType; extern PyTypeObject TreeBuilderType;
extern PyTypeObject ConfigType; extern PyTypeObject ConfigType;
extern PyTypeObject DiffType; extern PyTypeObject DiffType;
extern PyTypeObject RemoteType;
extern PyTypeObject ReferenceType; extern PyTypeObject ReferenceType;
extern PyTypeObject NoteType; extern PyTypeObject NoteType;
extern PyTypeObject NoteIterType; extern PyTypeObject NoteIterType;
@@ -514,45 +512,6 @@ Repository_workdir__get__(Repository *self, void *closure)
return to_path(c_path); return to_path(c_path);
} }
PyDoc_STRVAR(Repository_config__doc__,
"Get the configuration file for this repository.\n"
"\n"
"If a configuration file has not been set, the default config set for the\n"
"repository will be returned, including global and system configurations\n"
"(if they are available).");
PyObject *
Repository_config__get__(Repository *self)
{
int err;
git_config *config;
Config *py_config;
assert(self->repo);
if (self->config == NULL) {
err = git_repository_config(&config, self->repo);
if (err < 0)
return Error_set(err);
py_config = PyObject_New(Config, &ConfigType);
if (py_config == NULL) {
git_config_free(config);
return NULL;
}
py_config->config = config;
self->config = (PyObject*)py_config;
/* We need 2 refs here. One is returned, one is kept internally. */
Py_INCREF(self->config);
} else {
Py_INCREF(self->config);
}
return self->config;
}
PyDoc_STRVAR(Repository_merge_base__doc__, PyDoc_STRVAR(Repository_merge_base__doc__,
"merge_base(oid, oid) -> Oid\n" "merge_base(oid, oid) -> Oid\n"
"\n" "\n"
@@ -948,7 +907,7 @@ PyDoc_STRVAR(Repository_create_branch__doc__,
"\n" "\n"
"Examples::\n" "Examples::\n"
"\n" "\n"
" repo.create_branch('foo', repo.head.hex, force=False)"); " repo.create_branch('foo', repo.head.get_object(), force=False)");
PyObject * PyObject *
Repository_create_branch(Repository *self, PyObject *args) Repository_create_branch(Repository *self, PyObject *args)
@@ -1097,7 +1056,7 @@ Repository_lookup_reference(Repository *self, PyObject *py_name)
} }
PyDoc_STRVAR(Repository_create_reference_direct__doc__, PyDoc_STRVAR(Repository_create_reference_direct__doc__,
"git_reference_create(name, target, force) -> Reference\n" "create_reference_direct(name, target, force) -> Reference\n"
"\n" "\n"
"Create a new reference \"name\" which points to an object.\n" "Create a new reference \"name\" which points to an object.\n"
"\n" "\n"
@@ -1109,7 +1068,7 @@ PyDoc_STRVAR(Repository_create_reference_direct__doc__,
"\n" "\n"
"Examples::\n" "Examples::\n"
"\n" "\n"
" repo.git_reference_create('refs/heads/foo', repo.head.hex, False)"); " repo.create_reference_direct('refs/heads/foo', repo.head.target, False)");
PyObject * PyObject *
Repository_create_reference_direct(Repository *self, PyObject *args, Repository_create_reference_direct(Repository *self, PyObject *args,
@@ -1136,7 +1095,7 @@ Repository_create_reference_direct(Repository *self, PyObject *args,
} }
PyDoc_STRVAR(Repository_create_reference_symbolic__doc__, PyDoc_STRVAR(Repository_create_reference_symbolic__doc__,
"git_reference_symbolic_create(name, source, force) -> Reference\n" "create_reference_symbolic(name, source, force) -> Reference\n"
"\n" "\n"
"Create a new reference \"name\" which points to another reference.\n" "Create a new reference \"name\" which points to another reference.\n"
"\n" "\n"
@@ -1148,7 +1107,7 @@ PyDoc_STRVAR(Repository_create_reference_symbolic__doc__,
"\n" "\n"
"Examples::\n" "Examples::\n"
"\n" "\n"
" repo.git_reference_symbolic_create('refs/tags/foo', 'refs/heads/master', False)"); " repo.create_reference_symbolic('refs/tags/foo', 'refs/heads/master', False)");
PyObject * PyObject *
Repository_create_reference_symbolic(Repository *self, PyObject *args, Repository_create_reference_symbolic(Repository *self, PyObject *args,
@@ -1309,67 +1268,6 @@ Repository_TreeBuilder(Repository *self, PyObject *args)
return (PyObject*)builder; return (PyObject*)builder;
} }
PyDoc_STRVAR(Repository_create_remote__doc__,
"create_remote(name, url) -> Remote\n"
"\n"
"Creates a new remote.");
PyObject *
Repository_create_remote(Repository *self, PyObject *args)
{
git_remote *remote;
char *name = NULL, *url = NULL;
int err;
if (!PyArg_ParseTuple(args, "ss", &name, &url))
return NULL;
err = git_remote_create(&remote, self->repo, name, url);
if (err < 0)
return Error_set(err);
return (PyObject*) wrap_remote(remote, self);
}
PyDoc_STRVAR(Repository_remotes__doc__, "Returns all configured remotes.");
PyObject *
Repository_remotes__get__(Repository *self)
{
git_strarray remotes;
git_remote *remote = NULL;
PyObject *py_list = NULL;
PyObject *py_remote = NULL;
size_t i;
int err;
git_remote_list(&remotes, self->repo);
py_list = PyList_New(remotes.count);
for (i=0; i < remotes.count; ++i) {
err = git_remote_load(&remote, self->repo, remotes.strings[i]);
if (err < 0)
goto cleanup;
py_remote = wrap_remote(remote, self);
if (py_remote == NULL)
goto cleanup;
PyList_SetItem(py_list, i, py_remote);
}
git_strarray_free(&remotes);
return (PyObject*) py_list;
cleanup:
git_strarray_free(&remotes);
if (py_list)
Py_DECREF(py_list);
if (err < 0)
return Error_set(err);
return NULL;
}
PyDoc_STRVAR(Repository_default_signature__doc__, "Return the signature according to the repository's configuration"); PyDoc_STRVAR(Repository_default_signature__doc__, "Return the signature according to the repository's configuration");
PyObject * PyObject *
@@ -1384,6 +1282,14 @@ Repository_default_signature__get__(Repository *self)
return build_signature(NULL, sig, "utf-8"); return build_signature(NULL, sig, "utf-8");
} }
PyDoc_STRVAR(Repository__pointer__doc__, "Get the repo's pointer. For internal use only.");
PyObject *
Repository__pointer__get__(Repository *self)
{
/* Bytes means a raw buffer */
return PyBytes_FromStringAndSize((char *) &self->repo, sizeof(git_repository *));
}
PyDoc_STRVAR(Repository_checkout_head__doc__, PyDoc_STRVAR(Repository_checkout_head__doc__,
"checkout_head(strategy)\n" "checkout_head(strategy)\n"
"\n" "\n"
@@ -1666,7 +1572,6 @@ PyMethodDef Repository_methods[] = {
METHOD(Repository, revparse_single, METH_O), METHOD(Repository, revparse_single, METH_O),
METHOD(Repository, status, METH_NOARGS), METHOD(Repository, status, METH_NOARGS),
METHOD(Repository, status_file, METH_O), METHOD(Repository, status_file, METH_O),
METHOD(Repository, create_remote, METH_VARARGS),
METHOD(Repository, checkout_head, METH_VARARGS), METHOD(Repository, checkout_head, METH_VARARGS),
METHOD(Repository, checkout_index, METH_VARARGS), METHOD(Repository, checkout_index, METH_VARARGS),
METHOD(Repository, checkout_tree, METH_VARARGS), METHOD(Repository, checkout_tree, METH_VARARGS),
@@ -1690,10 +1595,9 @@ PyGetSetDef Repository_getseters[] = {
GETTER(Repository, head_is_unborn), GETTER(Repository, head_is_unborn),
GETTER(Repository, is_empty), GETTER(Repository, is_empty),
GETTER(Repository, is_bare), GETTER(Repository, is_bare),
GETTER(Repository, config),
GETTER(Repository, workdir), GETTER(Repository, workdir),
GETTER(Repository, remotes),
GETTER(Repository, default_signature), GETTER(Repository, default_signature),
GETTER(Repository, _pointer),
{NULL} {NULL}
}; };

View File

@@ -94,6 +94,13 @@ Signature_dealloc(Signature *self)
PyObject_Del(self); PyObject_Del(self);
} }
PyDoc_STRVAR(Signature__pointer__doc__, "Get the signature's pointer. For internal use only.");
PyObject *
Signature__pointer__get__(Repository *self)
{
/* Bytes means a raw buffer */
return PyBytes_FromStringAndSize((char *) &self->repo, sizeof(git_repository *));
}
PyDoc_STRVAR(Signature__encoding__doc__, "Encoding."); PyDoc_STRVAR(Signature__encoding__doc__, "Encoding.");
@@ -171,6 +178,7 @@ PyGetSetDef Signature_getseters[] = {
GETTER(Signature, email), GETTER(Signature, email),
GETTER(Signature, time), GETTER(Signature, time),
GETTER(Signature, offset), GETTER(Signature, offset),
GETTER(Signature, _pointer),
{NULL} {NULL}
}; };

View File

@@ -32,6 +32,10 @@
#include <Python.h> #include <Python.h>
#include <git2.h> #include <git2.h>
#if !(LIBGIT2_VER_MAJOR == 0 && LIBGIT2_VER_MINOR == 20)
#error You need a compatible libgit2 version (v0.20.x)
#endif
/* /*
* Python objects * Python objects
* *
@@ -70,19 +74,6 @@ SIMPLE_TYPE(Tree, git_tree, tree)
SIMPLE_TYPE(Blob, git_blob, blob) SIMPLE_TYPE(Blob, git_blob, blob)
SIMPLE_TYPE(Tag, git_tag, tag) SIMPLE_TYPE(Tag, git_tag, tag)
/* git_config */
typedef struct {
PyObject_HEAD
git_config* config;
} Config;
typedef struct {
PyObject_HEAD
Config *owner;
git_config_iterator *iter;
} ConfigIter;
/* git_note */ /* git_note */
typedef struct { typedef struct {
PyObject_HEAD PyObject_HEAD
@@ -194,38 +185,6 @@ typedef struct {
char *encoding; char *encoding;
} Signature; } Signature;
/* git_remote */
typedef struct {
PyObject_HEAD
Repository *repo;
git_remote *remote;
/* Callbacks for network events */
PyObject *progress;
PyObject *credentials;
PyObject *transfer_progress;
PyObject *update_tips;
} Remote;
/* git_refspec */
typedef struct {
PyObject_HEAD
const Remote *owner;
const git_refspec *refspec;
} Refspec;
/* git_transfer_progress */
typedef struct {
PyObject_HEAD
unsigned int total_objects;
unsigned int indexed_objects;
unsigned int received_objects;
unsigned int local_objects;
unsigned int total_deltas;
unsigned int indexed_deltas;
size_t received_bytes;
} TransferProgress;
/* git_blame */ /* git_blame */
SIMPLE_TYPE(Blame, git_blame, blame) SIMPLE_TYPE(Blame, git_blame, blame)

View File

@@ -153,92 +153,3 @@ on_error:
return -1; return -1;
} }
static int
py_cred_to_git_cred(git_cred **out, PyObject *py_cred, unsigned int allowed)
{
PyObject *py_type, *py_tuple;
long type;
int err = -1;
py_type = PyObject_GetAttrString(py_cred, "credential_type");
py_tuple = PyObject_GetAttrString(py_cred, "credential_tuple");
if (!py_type || !py_tuple) {
printf("py_type %p, py_tuple %p\n", py_type, py_tuple);
PyErr_SetString(PyExc_TypeError, "credential doesn't implement the interface");
goto cleanup;
}
if (!PyLong_Check(py_type)) {
PyErr_SetString(PyExc_TypeError, "credential type is not a long");
goto cleanup;
}
type = PyLong_AsLong(py_type);
/* Sanity check, make sure we're given credentials we can use */
if (!(allowed & type)) {
PyErr_SetString(PyExc_TypeError, "invalid credential type");
goto cleanup;
}
switch (type) {
case GIT_CREDTYPE_USERPASS_PLAINTEXT:
{
const char *username, *password;
if (!PyArg_ParseTuple(py_tuple, "ss", &username, &password))
goto cleanup;
err = git_cred_userpass_plaintext_new(out, username, password);
break;
}
case GIT_CREDTYPE_SSH_KEY:
{
const char *username, *pubkey, *privkey, *passphrase;
if (!PyArg_ParseTuple(py_tuple, "ssss", &username, &pubkey, &privkey, &passphrase))
goto cleanup;
err = git_cred_ssh_key_new(out, username, pubkey, privkey, passphrase);
break;
}
default:
PyErr_SetString(PyExc_TypeError, "unsupported credential type");
break;
}
cleanup:
Py_XDECREF(py_type);
Py_XDECREF(py_tuple);
return err;
}
int
callable_to_credentials(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, PyObject *credentials)
{
int err;
PyObject *py_cred = NULL, *arglist = NULL;
if (credentials == NULL || credentials == Py_None)
return 0;
if (!PyCallable_Check(credentials)) {
PyErr_SetString(PyExc_TypeError, "credentials callback is not callable");
return -1;
}
arglist = Py_BuildValue("(szI)", url, username_from_url, allowed_types);
py_cred = PyObject_CallObject(credentials, arglist);
Py_DECREF(arglist);
if (!py_cred)
return -1;
err = py_cred_to_git_cred(out, py_cred, allowed_types);
Py_DECREF(py_cred);
return err;
}

View File

@@ -47,6 +47,7 @@
#undef PyLong_Check #undef PyLong_Check
#define PyLong_Check PyInt_Check #define PyLong_Check PyInt_Check
#define PyLong_FromLong PyInt_FromLong #define PyLong_FromLong PyInt_FromLong
#define PyInteger_Type PyInt_Type
#define PyBytes_AS_STRING PyString_AS_STRING #define PyBytes_AS_STRING PyString_AS_STRING
#define PyBytes_AsString PyString_AsString #define PyBytes_AsString PyString_AsString
#define PyBytes_AsStringAndSize PyString_AsStringAndSize #define PyBytes_AsStringAndSize PyString_AsStringAndSize
@@ -57,6 +58,7 @@
#define to_path(x) to_bytes(x) #define to_path(x) to_bytes(x)
#define to_encoding(x) to_bytes(x) #define to_encoding(x) to_bytes(x)
#else #else
#define PyInteger_Type PyLong_Type
#define to_path(x) to_unicode(x, Py_FileSystemDefaultEncoding, "strict") #define to_path(x) to_unicode(x, Py_FileSystemDefaultEncoding, "strict")
#define to_encoding(x) PyUnicode_DecodeASCII(x, strlen(x), "strict") #define to_encoding(x) PyUnicode_DecodeASCII(x, strlen(x), "strict")
#endif #endif
@@ -117,8 +119,6 @@ const char *py_str_borrow_c_str(PyObject **tvaue, PyObject *value, const char *e
PyObject * get_pylist_from_git_strarray(git_strarray *strarray); PyObject * get_pylist_from_git_strarray(git_strarray *strarray);
int get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist); int get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist);
int callable_to_credentials(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, PyObject *credentials);
#define py_path_to_c_str(py_path) \ #define py_path_to_c_str(py_path) \
py_str_to_c_str(py_path, Py_FileSystemDefaultEncoding) py_str_to_c_str(py_path, Py_FileSystemDefaultEncoding)

View File

@@ -74,6 +74,13 @@ class BlobTest(utils.RepoTestCase):
self.assertEqual(BLOB_NEW_CONTENT, blob.data) self.assertEqual(BLOB_NEW_CONTENT, blob.data)
self.assertEqual(len(BLOB_NEW_CONTENT), blob.size) self.assertEqual(len(BLOB_NEW_CONTENT), blob.size)
self.assertEqual(BLOB_NEW_CONTENT, blob.read_raw()) self.assertEqual(BLOB_NEW_CONTENT, blob.read_raw())
blob_buffer = memoryview(blob)
self.assertEqual(len(BLOB_NEW_CONTENT), len(blob_buffer))
self.assertEqual(BLOB_NEW_CONTENT, blob_buffer)
def set_content():
blob_buffer[:2] = b'hi'
self.assertRaises(TypeError, set_content)
def test_create_blob_fromworkdir(self): def test_create_blob_fromworkdir(self):

View File

@@ -74,7 +74,7 @@ class ConfigTest(utils.RepoTestCase):
config_read = Config(CONFIG_FILENAME) config_read = Config(CONFIG_FILENAME)
self.assertTrue('core.bare' in config_read) self.assertTrue('core.bare' in config_read)
self.assertFalse(config_read['core.bare']) self.assertFalse(config_read.get_bool('core.bare'))
self.assertTrue('core.editor' in config_read) self.assertTrue('core.editor' in config_read)
self.assertEqual(config_read['core.editor'], 'ed') self.assertEqual(config_read['core.editor'], 'ed')
@@ -88,9 +88,9 @@ class ConfigTest(utils.RepoTestCase):
config.add_file(CONFIG_FILENAME, 0) config.add_file(CONFIG_FILENAME, 0)
self.assertTrue('this.that' in config) self.assertTrue('this.that' in config)
self.assertTrue(config['this.that']) self.assertTrue(config.get_bool('this.that'))
self.assertTrue('something.other.here' in config) self.assertTrue('something.other.here' in config)
self.assertFalse(config['something.other.here']) self.assertFalse(config.get_bool('something.other.here'))
def test_read(self): def test_read(self):
config = self.repo.config config = self.repo.config
@@ -103,11 +103,11 @@ class ConfigTest(utils.RepoTestCase):
lambda: config['abc.def']) lambda: config['abc.def'])
self.assertTrue('core.bare' in config) self.assertTrue('core.bare' in config)
self.assertFalse(config['core.bare']) self.assertFalse(config.get_bool('core.bare'))
self.assertTrue('core.editor' in config) self.assertTrue('core.editor' in config)
self.assertEqual(config['core.editor'], 'ed') self.assertEqual(config['core.editor'], 'ed')
self.assertTrue('core.repositoryformatversion' in config) self.assertTrue('core.repositoryformatversion' in config)
self.assertEqual(config['core.repositoryformatversion'], 0) self.assertEqual(config.get_int('core.repositoryformatversion'), 0)
new_file = open(CONFIG_FILENAME, "w") new_file = open(CONFIG_FILENAME, "w")
new_file.write("[this]\n\tthat = foobar\n\tthat = foobeer\n") new_file.write("[this]\n\tthat = foobar\n\tthat = foobeer\n")
@@ -115,9 +115,10 @@ class ConfigTest(utils.RepoTestCase):
config.add_file(CONFIG_FILENAME, 0) config.add_file(CONFIG_FILENAME, 0)
self.assertTrue('this.that' in config) self.assertTrue('this.that' in config)
self.assertEqual(len(config.get_multivar('this.that')), 2)
l = config.get_multivar('this.that', 'bar') self.assertEqual(2, len(list(config.get_multivar('this.that'))))
self.assertEqual(len(l), 1) l = list(config.get_multivar('this.that', 'bar'))
self.assertEqual(1, len(l))
self.assertEqual(l[0], 'foobar') self.assertEqual(l[0], 'foobar')
def test_write(self): def test_write(self):
@@ -129,7 +130,7 @@ class ConfigTest(utils.RepoTestCase):
self.assertFalse('core.dummy1' in config) self.assertFalse('core.dummy1' in config)
config['core.dummy1'] = 42 config['core.dummy1'] = 42
self.assertTrue('core.dummy1' in config) self.assertTrue('core.dummy1' in config)
self.assertEqual(config['core.dummy1'], 42) self.assertEqual(config.get_int('core.dummy1'), 42)
self.assertFalse('core.dummy2' in config) self.assertFalse('core.dummy2' in config)
config['core.dummy2'] = 'foobar' config['core.dummy2'] = 'foobar'
@@ -155,16 +156,16 @@ class ConfigTest(utils.RepoTestCase):
config.add_file(CONFIG_FILENAME, 5) config.add_file(CONFIG_FILENAME, 5)
self.assertTrue('this.that' in config) self.assertTrue('this.that' in config)
l = config.get_multivar('this.that', 'foo.*') l = config.get_multivar('this.that', 'foo.*')
self.assertEqual(len(l), 2) self.assertEqual(2, len(list(l)))
config.set_multivar('this.that', '^.*beer', 'fool') config.set_multivar('this.that', '^.*beer', 'fool')
l = config.get_multivar('this.that', 'fool') l = list(config.get_multivar('this.that', 'fool'))
self.assertEqual(len(l), 1) self.assertEqual(len(l), 1)
self.assertEqual(l[0], 'fool') self.assertEqual(l[0], 'fool')
config.set_multivar('this.that', 'foo.*', 'foo-123456') config.set_multivar('this.that', 'foo.*', 'foo-123456')
l = config.get_multivar('this.that', 'foo.*') l = config.get_multivar('this.that', 'foo.*')
self.assertEqual(len(l), 2) self.assertEqual(2, len(list(l)))
for i in l: for i in l:
self.assertEqual(i, 'foo-123456') self.assertEqual(i, 'foo-123456')
@@ -178,5 +179,12 @@ class ConfigTest(utils.RepoTestCase):
self.assertTrue('core.bare' in lst) self.assertTrue('core.bare' in lst)
self.assertTrue(lst['core.bare']) self.assertTrue(lst['core.bare'])
def test_parsing(self):
self.assertTrue(Config.parse_bool("on"))
self.assertTrue(Config.parse_bool("1"))
self.assertEqual(5, Config.parse_int("5"))
self.assertEqual(1024, Config.parse_int("1k"))
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@@ -65,6 +65,7 @@ class RepositoryTest(utils.RepoTestCase):
self.assertEqual('new', remote.name) self.assertEqual('new', remote.name)
self.assertRaisesAssign(ValueError, remote, 'name', '') self.assertRaisesAssign(ValueError, remote, 'name', '')
self.assertRaisesAssign(ValueError, remote, 'name', None)
def test_remote_set_url(self): def test_remote_set_url(self):
@@ -189,9 +190,9 @@ class EmptyRepositoryTest(utils.EmptyRepoTestCase):
def test_fetch(self): def test_fetch(self):
remote = self.repo.remotes[0] remote = self.repo.remotes[0]
stats = remote.fetch() stats = remote.fetch()
self.assertEqual(stats['received_bytes'], REMOTE_REPO_BYTES) self.assertEqual(stats.received_bytes, REMOTE_REPO_BYTES)
self.assertEqual(stats['indexed_objects'], REMOTE_REPO_OBJECTS) self.assertEqual(stats.indexed_objects, REMOTE_REPO_OBJECTS)
self.assertEqual(stats['received_objects'], REMOTE_REPO_OBJECTS) self.assertEqual(stats.received_objects, REMOTE_REPO_OBJECTS)
def test_transfer_progress(self): def test_transfer_progress(self):
self.tp = None self.tp = None
@@ -201,9 +202,9 @@ class EmptyRepositoryTest(utils.EmptyRepoTestCase):
remote = self.repo.remotes[0] remote = self.repo.remotes[0]
remote.transfer_progress = tp_cb remote.transfer_progress = tp_cb
stats = remote.fetch() stats = remote.fetch()
self.assertEqual(stats['received_bytes'], self.tp.received_bytes) self.assertEqual(stats.received_bytes, self.tp.received_bytes)
self.assertEqual(stats['indexed_objects'], self.tp.indexed_objects) self.assertEqual(stats.indexed_objects, self.tp.indexed_objects)
self.assertEqual(stats['received_objects'], self.tp.received_objects) self.assertEqual(stats.received_objects, self.tp.received_objects)
def test_update_tips(self): def test_update_tips(self):
remote = self.repo.remotes[0] remote = self.repo.remotes[0]