Merge remote-tracking branch 'origin/cffi-remote'

This commit is contained in:
J. David Ibáñez
2014-04-18 21:56:00 +02:00
21 changed files with 1034 additions and 1345 deletions

1
.gitignore vendored
View File

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

View File

@@ -11,6 +11,7 @@ env: LIBGIT2=~/libgit2/_install/ LD_LIBRARY_PATH=~/libgit2/_install/lib
before_install:
- sudo apt-get install cmake
- pip install cffi
- "./.travis.sh"
script:

View File

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

View File

@@ -37,6 +37,9 @@ from .repository import Repository
from .version import __version__
from .settings import Settings
from .credentials import *
from .remote import Remote, get_credentials
from .errors import check_error
from .ffi import ffi, C, to_str
def init_repository(path, bare=False):
"""
@@ -49,6 +52,19 @@ def init_repository(path, bare=False):
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(
url, path, bare=False, ignore_cert_errors=False,
remote_name="origin", checkout_branch=None, credentials=None):
@@ -74,8 +90,42 @@ def clone_repository(
"""
_pygit2.clone_repository(
url, path, bare, ignore_cert_errors, remote_name, checkout_branch, credentials)
opts = ffi.new('git_clone_options *')
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)
settings = Settings()

View File

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

214
pygit2/decl.h Normal file
View File

@@ -0,0 +1,214 @@
typedef ... git_repository;
typedef ... git_remote;
typedef ... git_refspec;
typedef ... git_push;
typedef ... git_cred;
typedef ... git_diff_file;
typedef ... git_tree;
#define GIT_OID_RAWSZ ...
typedef struct git_oid {
unsigned char id[20];
} git_oid;
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 struct git_remote_callbacks {
unsigned int version;
int (*progress)(const char *str, int len, void *data);
int (*completion)(git_remote_completion_type type, void *data);
int (*credentials)(git_cred **cred, const char *url, const char *username_from_url, unsigned int allowed_types, void *data);
int (*transfer_progress)(const git_transfer_progress *stats, void *data);
int (*update_tips)(const char *refname, const git_oid *a, const git_oid *b, void *data);
void *payload;
} 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_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);
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(char *out, size_t outlen, const git_refspec *spec, const char *name);
int git_refspec_rtransform(char *out, size_t outlen, 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_opts {
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 *our_label;
const char *their_label;
} git_checkout_opts;
typedef struct git_clone_options {
unsigned int version;
git_checkout_opts checkout_opts;
git_remote_callbacks remote_callbacks;
int bare;
int ignore_cert_errors;
const char *remote_name;
const char* checkout_branch;
} git_clone_options;
int git_clone(git_repository **out,
const char *url,
const char *local_path,
const git_clone_options *options);

55
pygit2/errors.py Normal file
View File

@@ -0,0 +1,55 @@
# -*- 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):
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:
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)

110
pygit2/refspec.py Normal file
View File

@@ -0,0 +1,110 @@
# -*- 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):
"""transform(str) -> str
Transform a reference name according to this refspec from the lhs to the rhs."""
alen = len(ref)
err = C.GIT_EBUFS
ptr = None
ref_str = to_str(ref)
while err == C.GIT_EBUFS:
alen *= 2
ptr = ffi.new('char []', alen)
err = C.git_refspec_transform(ptr, alen, self._refspec, ref_str)
check_error(err)
return ffi.string(ptr).decode()
def rtransform(self, ref):
"""transform(str) -> str
Transform a reference name according to this refspec from the lhs to the rhs"""
alen = len(ref)
err = C.GIT_EBUFS
ptr = None
ref_str = to_str(ref)
while err == C.GIT_EBUFS:
alen *= 2
ptr = ffi.new('char []', alen)
err = C.git_refspec_rtransform(ptr, alen, self._refspec, ref_str)
check_error(err)
return ffi.string(ptr).decode()

398
pygit2/remote.py Normal file
View File

@@ -0,0 +1,398 @@
# -*- 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 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.progress = self._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):
"""fetch() -> TransferProgress
Perform a fetch against this remote.
"""
self._stored_exception = None
err = C.git_remote_fetch(self._remote)
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):
"""push(refspec)
Push the given refspec to the remote. Raises ``GitError`` on error"""
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)
err = C.git_push_update_tips(push)
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('int (*transfer_progress)(const git_transfer_progress *stats, void *data)')
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('int (*progress)(const char *str, int len, void *data)')
def _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,21 @@ from _pygit2 import Oid, GIT_OID_HEXSZ, GIT_OID_MINPREFIXLEN
from _pygit2 import GIT_CHECKOUT_SAFE_CREATE, GIT_DIFF_NORMAL
from _pygit2 import Reference, Tree, Commit, Blob
from .ffi import ffi, C, to_str
from .errors import check_error
from .remote import Remote
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
#
@@ -59,6 +71,45 @@ class Repository(_Repository):
def __repr__(self):
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)
#
# References
#

View File

@@ -173,6 +173,11 @@ classifiers = [
with codecs.open('README.rst', 'r', 'utf-8') as readme:
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',
description='Python bindings for libgit2.',
keywords='git',
@@ -184,10 +189,13 @@ setup(name='pygit2',
maintainer_email='jdavid.ibp@gmail.com',
long_description=long_description,
packages=['pygit2'],
package_data={'pygit2': ['decl.h']},
install_requires=['cffi'],
ext_modules=[
Extension('_pygit2', pygit2_exts,
include_dirs=[libgit2_include, 'include'],
library_dirs=[libgit2_lib],
libraries=['git2']),
ffi_ext,
],
cmdclass=cmdclass)

View File

@@ -75,7 +75,6 @@ extern PyTypeObject BranchType;
extern PyTypeObject SignatureType;
extern PyTypeObject RemoteType;
extern PyTypeObject RefspecType;
extern PyTypeObject TransferProgressType;
extern PyTypeObject NoteType;
extern PyTypeObject NoteIterType;
extern PyTypeObject BlameType;
@@ -116,69 +115,6 @@ init_repository(PyObject *self, PyObject *args) {
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__,
"discover_repository(path[, across_fs[, ceiling_dirs]]) -> str\n"
"\n"
@@ -253,8 +189,6 @@ hash(PyObject *self, PyObject *args)
PyMethodDef module_methods[] = {
{"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__doc__},
{"hashfile", hashfile, METH_VARARGS, hashfile__doc__},
@@ -456,20 +390,6 @@ moduleinit(PyObject* m)
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 */
INIT_TYPE(BlameType, NULL, NULL)
INIT_TYPE(BlameIterType, NULL, NULL)

View File

@@ -1,297 +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)
{
const char *str;
char *trans;
int err, len, alen;
PyObject *py_trans, *tstr;
str = py_str_borrow_c_str(&tstr, py_str, NULL);
alen = len = strlen(str);
do {
alen *= alen;
trans = malloc(alen);
if (!trans) {
Py_DECREF(tstr);
return PyErr_NoMemory();
}
err = git_refspec_transform(trans, alen, self->refspec, str);
} while(err == GIT_EBUFS);
Py_DECREF(tstr);
if (err < 0) {
free(trans);
Error_set(err);
return NULL;
}
py_trans = to_unicode(trans, NULL, NULL);
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)
{
const char *str;
char *trans;
int err, len, alen;
PyObject *py_trans, *tstr;
str = py_str_borrow_c_str(&tstr, py_str, NULL);
alen = len = strlen(str);
do {
alen *= alen;
trans = malloc(alen);
if (!trans) {
Py_DECREF(tstr);
return PyErr_NoMemory();
}
err = git_refspec_rtransform(trans, alen, self->refspec, str);
} while(err == GIT_EBUFS);
Py_DECREF(tstr);
if (err < 0) {
free(trans);
Error_set(err);
return NULL;
}
py_trans = to_unicode(trans, NULL, NULL);
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,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);
/*
* 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);
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 "note.h"
#include "repository.h"
#include "remote.h"
#include "branch.h"
#include "blame.h"
#include "mergeresult.h"
@@ -54,7 +53,6 @@ extern PyTypeObject TreeType;
extern PyTypeObject TreeBuilderType;
extern PyTypeObject ConfigType;
extern PyTypeObject DiffType;
extern PyTypeObject RemoteType;
extern PyTypeObject ReferenceType;
extern PyTypeObject NoteType;
extern PyTypeObject NoteIterType;
@@ -1277,67 +1275,6 @@ Repository_TreeBuilder(Repository *self, PyObject *args)
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");
PyObject *
@@ -1352,6 +1289,14 @@ Repository_default_signature__get__(Repository *self)
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__,
"checkout_head(strategy)\n"
"\n"
@@ -1633,7 +1578,6 @@ PyMethodDef Repository_methods[] = {
METHOD(Repository, revparse_single, METH_O),
METHOD(Repository, status, METH_NOARGS),
METHOD(Repository, status_file, METH_O),
METHOD(Repository, create_remote, METH_VARARGS),
METHOD(Repository, checkout_head, METH_VARARGS),
METHOD(Repository, checkout_index, METH_VARARGS),
METHOD(Repository, checkout_tree, METH_VARARGS),
@@ -1659,8 +1603,8 @@ PyGetSetDef Repository_getseters[] = {
GETTER(Repository, is_bare),
GETTER(Repository, config),
GETTER(Repository, workdir),
GETTER(Repository, remotes),
GETTER(Repository, default_signature),
GETTER(Repository, _pointer),
{NULL}
};

View File

@@ -194,38 +194,6 @@ typedef struct {
char *encoding;
} 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 */
SIMPLE_TYPE(Blame, git_blame, blame)

View File

@@ -153,92 +153,3 @@ on_error:
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

@@ -119,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);
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) \
py_str_to_c_str(py_path, Py_FileSystemDefaultEncoding)

View File

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