diff --git a/.gitignore b/.gitignore index d802242..79889fe 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,4 @@ pygit2/__pycache__ *.egg-info *.swp docs/_build +__pycache__ diff --git a/.travis.yml b/.travis.yml index f2ffa47..4fbf403 100644 --- a/.travis.yml +++ b/.travis.yml @@ -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: diff --git a/docs/remotes.rst b/docs/remotes.rst index 58ab8ef..445bef2 100644 --- a/docs/remotes.rst +++ b/docs/remotes.rst @@ -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 diff --git a/pygit2/__init__.py b/pygit2/__init__.py index fc2e693..2dbda72 100644 --- a/pygit2/__init__.py +++ b/pygit2/__init__.py @@ -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() diff --git a/pygit2/credentials.py b/pygit2/credentials.py index cad215a..9f060e2 100644 --- a/pygit2/credentials.py +++ b/pygit2/credentials.py @@ -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 diff --git a/pygit2/decl.h b/pygit2/decl.h new file mode 100644 index 0000000..a64c32f --- /dev/null +++ b/pygit2/decl.h @@ -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); diff --git a/pygit2/errors.py b/pygit2/errors.py new file mode 100644 index 0000000..b9f6c9b --- /dev/null +++ b/pygit2/errors.py @@ -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) + diff --git a/pygit2/ffi.py b/pygit2/ffi.py new file mode 100644 index 0000000..468b9bc --- /dev/null +++ b/pygit2/ffi.py @@ -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 ", libraries=["git2"], include_dirs=include_dirs, library_dirs=library_dirs) diff --git a/pygit2/refspec.py b/pygit2/refspec.py new file mode 100644 index 0000000..0097449 --- /dev/null +++ b/pygit2/refspec.py @@ -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() diff --git a/pygit2/remote.py b/pygit2/remote.py new file mode 100644 index 0000000..11bc86c --- /dev/null +++ b/pygit2/remote.py @@ -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 diff --git a/pygit2/repository.py b/pygit2/repository.py index e6c750f..107db91 100644 --- a/pygit2/repository.py +++ b/pygit2/repository.py @@ -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 # diff --git a/setup.py b/setup.py index eff54e1..27733f4 100644 --- a/setup.py +++ b/setup.py @@ -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) diff --git a/src/pygit2.c b/src/pygit2.c index bda9545..d88c0e4 100644 --- a/src/pygit2.c +++ b/src/pygit2.c @@ -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) diff --git a/src/refspec.c b/src/refspec.c deleted file mode 100644 index 3053fe6..0000000 --- a/src/refspec.c +++ /dev/null @@ -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 -#include -#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 */ -}; diff --git a/src/remote.c b/src/remote.c deleted file mode 100644 index dee9b5a..0000000 --- a/src/remote.c +++ /dev/null @@ -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 -#include -#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; -} diff --git a/src/remote.h b/src/remote.h deleted file mode 100644 index ce6ee47..0000000 --- a/src/remote.h +++ /dev/null @@ -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 -#include -#include - -PyObject* Remote_fetch(Remote *self, PyObject *args); -PyObject* wrap_remote(git_remote *c_remote, Repository *repo); - -#endif diff --git a/src/repository.c b/src/repository.c index bb2a11b..1713136 100644 --- a/src/repository.c +++ b/src/repository.c @@ -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} }; diff --git a/src/types.h b/src/types.h index cfd0009..e50f834 100644 --- a/src/types.h +++ b/src/types.h @@ -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) diff --git a/src/utils.c b/src/utils.c index 24b6bbd..44acf5c 100644 --- a/src/utils.c +++ b/src/utils.c @@ -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; -} diff --git a/src/utils.h b/src/utils.h index 26258d2..e321ea1 100644 --- a/src/utils.h +++ b/src/utils.h @@ -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) diff --git a/test/test_remote.py b/test/test_remote.py index 797474c..9c95f80 100644 --- a/test/test_remote.py +++ b/test/test_remote.py @@ -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]