Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement remotes via CFFI #360

Merged
merged 14 commits into from
Apr 18, 2014
Merged
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,4 @@ pygit2/__pycache__
*.egg-info
*.swp
docs/_build
__pycache__
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
6 changes: 3 additions & 3 deletions docs/remotes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
54 changes: 52 additions & 2 deletions pygit2/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@
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):
"""
Expand All @@ -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):
Expand All @@ -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()
6 changes: 4 additions & 2 deletions pygit2/credentials.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
214 changes: 214 additions & 0 deletions pygit2/decl.h
Original file line number Diff line number Diff line change
@@ -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 changes: 55 additions & 0 deletions pygit2/errors.py
Original file line number Diff line number Diff line change
@@ -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)

Loading