Implement clone via CFFI as well
This lets us get rid of the last piece of C for anything related to remotes and credentials.
This commit is contained in:
@@ -37,7 +37,9 @@ from .repository import Repository
|
|||||||
from .version import __version__
|
from .version import __version__
|
||||||
from .settings import Settings
|
from .settings import Settings
|
||||||
from .credentials import *
|
from .credentials import *
|
||||||
from .remote import Remote
|
from .remote import Remote, get_credentials
|
||||||
|
from .errors import check_error
|
||||||
|
from .ffi import ffi, C, to_str
|
||||||
|
|
||||||
def init_repository(path, bare=False):
|
def init_repository(path, bare=False):
|
||||||
"""
|
"""
|
||||||
@@ -50,6 +52,19 @@ def init_repository(path, bare=False):
|
|||||||
return Repository(path)
|
return Repository(path)
|
||||||
|
|
||||||
|
|
||||||
|
@ffi.callback('int (*credentials)(git_cred **cred, const char *url, const char *username_from_url, unsigned int allowed_types, void *data)')
|
||||||
|
def _credentials_cb(cred_out, url, username_from_url, allowed, data):
|
||||||
|
d = ffi.from_handle(data)
|
||||||
|
|
||||||
|
try:
|
||||||
|
ccred = get_credentials(d['callback'], url, username_from_url, allowed)
|
||||||
|
cred_out[0] = ccred[0]
|
||||||
|
except Exception, e:
|
||||||
|
d['exception'] = e
|
||||||
|
return C.GIT_EUSER
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
def clone_repository(
|
def clone_repository(
|
||||||
url, path, bare=False, ignore_cert_errors=False,
|
url, path, bare=False, ignore_cert_errors=False,
|
||||||
remote_name="origin", checkout_branch=None, credentials=None):
|
remote_name="origin", checkout_branch=None, credentials=None):
|
||||||
@@ -75,8 +90,42 @@ def clone_repository(
|
|||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
_pygit2.clone_repository(
|
opts = ffi.new('git_clone_options *')
|
||||||
url, path, bare, ignore_cert_errors, remote_name, checkout_branch, credentials)
|
crepo = ffi.new('git_repository **')
|
||||||
|
|
||||||
|
branch = checkout_branch or None
|
||||||
|
|
||||||
|
# Data, let's use a dict as we don't really want much more
|
||||||
|
d = {}
|
||||||
|
d['callback'] = credentials
|
||||||
|
d_handle = ffi.new_handle(d)
|
||||||
|
|
||||||
|
# We need to keep the ref alive ourselves
|
||||||
|
checkout_branch_ref = None
|
||||||
|
if branch:
|
||||||
|
checkout_branch_ref = ffi.new('char []', branch)
|
||||||
|
opts.checkout_branch = checkout_branch_ref
|
||||||
|
|
||||||
|
remote_name_ref = ffi.new('char []', to_str(remote_name))
|
||||||
|
opts.remote_name = remote_name_ref
|
||||||
|
|
||||||
|
opts.version = 1
|
||||||
|
opts.ignore_cert_errors = ignore_cert_errors
|
||||||
|
opts.bare = bare
|
||||||
|
opts.remote_callbacks.version = 1
|
||||||
|
opts.checkout_opts.version = 1
|
||||||
|
if credentials:
|
||||||
|
opts.remote_callbacks.credentials = _credentials_cb
|
||||||
|
opts.remote_callbacks.payload = d_handle
|
||||||
|
|
||||||
|
err = C.git_clone(crepo, to_str(url), to_str(path), opts)
|
||||||
|
C.git_repository_free(crepo[0])
|
||||||
|
|
||||||
|
if 'exception' in d:
|
||||||
|
raise d['exception']
|
||||||
|
|
||||||
|
check_error(err)
|
||||||
|
|
||||||
return Repository(path)
|
return Repository(path)
|
||||||
|
|
||||||
settings = Settings()
|
settings = Settings()
|
||||||
|
@@ -3,6 +3,8 @@ typedef ... git_remote;
|
|||||||
typedef ... git_refspec;
|
typedef ... git_refspec;
|
||||||
typedef ... git_push;
|
typedef ... git_push;
|
||||||
typedef ... git_cred;
|
typedef ... git_cred;
|
||||||
|
typedef ... git_diff_file;
|
||||||
|
typedef ... git_tree;
|
||||||
|
|
||||||
#define GIT_OID_RAWSZ ...
|
#define GIT_OID_RAWSZ ...
|
||||||
|
|
||||||
@@ -43,6 +45,7 @@ typedef struct {
|
|||||||
const git_error * giterr_last(void);
|
const git_error * giterr_last(void);
|
||||||
|
|
||||||
void git_strarray_free(git_strarray *array);
|
void git_strarray_free(git_strarray *array);
|
||||||
|
void git_repository_free(git_repository *repo);
|
||||||
|
|
||||||
typedef struct git_transfer_progress {
|
typedef struct git_transfer_progress {
|
||||||
unsigned int total_objects;
|
unsigned int total_objects;
|
||||||
@@ -148,3 +151,64 @@ int git_cred_ssh_key_new(
|
|||||||
const char *publickey,
|
const char *publickey,
|
||||||
const char *privatekey,
|
const char *privatekey,
|
||||||
const char *passphrase);
|
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);
|
||||||
|
@@ -352,33 +352,7 @@ class Remote(object):
|
|||||||
return 0
|
return 0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
url_str = maybe_string(url)
|
ccred = get_credentials(self.credentials, url, username, allowed)
|
||||||
username_str = maybe_string(username)
|
|
||||||
|
|
||||||
creds = self.credentials(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)
|
|
||||||
cred_out[0] = ccred[0]
|
cred_out[0] = ccred[0]
|
||||||
|
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
@@ -386,3 +360,36 @@ class Remote(object):
|
|||||||
return C.GIT_EUSER
|
return C.GIT_EUSER
|
||||||
|
|
||||||
return 0
|
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
|
||||||
|
65
src/pygit2.c
65
src/pygit2.c
@@ -115,69 +115,6 @@ init_repository(PyObject *self, PyObject *args) {
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int
|
|
||||||
credentials_cb(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, void *data)
|
|
||||||
{
|
|
||||||
PyObject *credentials = (PyObject *) data;
|
|
||||||
|
|
||||||
return callable_to_credentials(out, url, username_from_url, allowed_types, credentials);
|
|
||||||
}
|
|
||||||
|
|
||||||
PyDoc_STRVAR(clone_repository__doc__,
|
|
||||||
"clone_repository(url, path, bare, remote_name, checkout_branch)\n"
|
|
||||||
"\n"
|
|
||||||
"Clones a Git repository in the given url to the given path "
|
|
||||||
"with the specified options.\n"
|
|
||||||
"\n"
|
|
||||||
"Arguments:\n"
|
|
||||||
"\n"
|
|
||||||
"url\n"
|
|
||||||
" Git repository remote url.\n"
|
|
||||||
"path\n"
|
|
||||||
" Path where to create the repository.\n"
|
|
||||||
"bare\n"
|
|
||||||
" If 'bare' is not 0, then a bare git repository will be created.\n"
|
|
||||||
"remote_name\n"
|
|
||||||
" The name given to the 'origin' remote. The default is 'origin'.\n"
|
|
||||||
"checkout_branch\n"
|
|
||||||
" The name of the branch to checkout. None means use the remote's "
|
|
||||||
"HEAD.\n");
|
|
||||||
|
|
||||||
|
|
||||||
PyObject *
|
|
||||||
clone_repository(PyObject *self, PyObject *args) {
|
|
||||||
git_repository *repo;
|
|
||||||
const char *url;
|
|
||||||
const char *path;
|
|
||||||
unsigned int bare, ignore_cert_errors;
|
|
||||||
const char *remote_name, *checkout_branch;
|
|
||||||
PyObject *credentials = NULL;
|
|
||||||
int err;
|
|
||||||
git_clone_options opts = GIT_CLONE_OPTIONS_INIT;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "zzIIzzO",
|
|
||||||
&url, &path, &bare, &ignore_cert_errors, &remote_name, &checkout_branch, &credentials))
|
|
||||||
return NULL;
|
|
||||||
|
|
||||||
opts.bare = bare;
|
|
||||||
opts.ignore_cert_errors = ignore_cert_errors;
|
|
||||||
opts.remote_name = remote_name;
|
|
||||||
opts.checkout_branch = checkout_branch;
|
|
||||||
|
|
||||||
if (credentials != Py_None) {
|
|
||||||
opts.remote_callbacks.credentials = credentials_cb;
|
|
||||||
opts.remote_callbacks.payload = credentials;
|
|
||||||
}
|
|
||||||
|
|
||||||
err = git_clone(&repo, url, path, &opts);
|
|
||||||
if (err < 0)
|
|
||||||
return Error_set(err);
|
|
||||||
|
|
||||||
git_repository_free(repo);
|
|
||||||
Py_RETURN_NONE;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
PyDoc_STRVAR(discover_repository__doc__,
|
PyDoc_STRVAR(discover_repository__doc__,
|
||||||
"discover_repository(path[, across_fs[, ceiling_dirs]]) -> str\n"
|
"discover_repository(path[, across_fs[, ceiling_dirs]]) -> str\n"
|
||||||
"\n"
|
"\n"
|
||||||
@@ -252,8 +189,6 @@ hash(PyObject *self, PyObject *args)
|
|||||||
|
|
||||||
PyMethodDef module_methods[] = {
|
PyMethodDef module_methods[] = {
|
||||||
{"init_repository", init_repository, METH_VARARGS, init_repository__doc__},
|
{"init_repository", init_repository, METH_VARARGS, init_repository__doc__},
|
||||||
{"clone_repository", clone_repository, METH_VARARGS,
|
|
||||||
clone_repository__doc__},
|
|
||||||
{"discover_repository", discover_repository, METH_VARARGS,
|
{"discover_repository", discover_repository, METH_VARARGS,
|
||||||
discover_repository__doc__},
|
discover_repository__doc__},
|
||||||
{"hashfile", hashfile, METH_VARARGS, hashfile__doc__},
|
{"hashfile", hashfile, METH_VARARGS, hashfile__doc__},
|
||||||
|
89
src/utils.c
89
src/utils.c
@@ -153,92 +153,3 @@ on_error:
|
|||||||
|
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
|
||||||
py_cred_to_git_cred(git_cred **out, PyObject *py_cred, unsigned int allowed)
|
|
||||||
{
|
|
||||||
PyObject *py_type, *py_tuple;
|
|
||||||
long type;
|
|
||||||
int err = -1;
|
|
||||||
|
|
||||||
py_type = PyObject_GetAttrString(py_cred, "credential_type");
|
|
||||||
py_tuple = PyObject_GetAttrString(py_cred, "credential_tuple");
|
|
||||||
|
|
||||||
if (!py_type || !py_tuple) {
|
|
||||||
printf("py_type %p, py_tuple %p\n", py_type, py_tuple);
|
|
||||||
PyErr_SetString(PyExc_TypeError, "credential doesn't implement the interface");
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!PyLong_Check(py_type)) {
|
|
||||||
PyErr_SetString(PyExc_TypeError, "credential type is not a long");
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
type = PyLong_AsLong(py_type);
|
|
||||||
|
|
||||||
/* Sanity check, make sure we're given credentials we can use */
|
|
||||||
if (!(allowed & type)) {
|
|
||||||
PyErr_SetString(PyExc_TypeError, "invalid credential type");
|
|
||||||
goto cleanup;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case GIT_CREDTYPE_USERPASS_PLAINTEXT:
|
|
||||||
{
|
|
||||||
const char *username, *password;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(py_tuple, "ss", &username, &password))
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
err = git_cred_userpass_plaintext_new(out, username, password);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case GIT_CREDTYPE_SSH_KEY:
|
|
||||||
{
|
|
||||||
const char *username, *pubkey, *privkey, *passphrase;
|
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(py_tuple, "ssss", &username, &pubkey, &privkey, &passphrase))
|
|
||||||
goto cleanup;
|
|
||||||
|
|
||||||
err = git_cred_ssh_key_new(out, username, pubkey, privkey, passphrase);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
PyErr_SetString(PyExc_TypeError, "unsupported credential type");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
cleanup:
|
|
||||||
Py_XDECREF(py_type);
|
|
||||||
Py_XDECREF(py_tuple);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
|
||||||
callable_to_credentials(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, PyObject *credentials)
|
|
||||||
{
|
|
||||||
int err;
|
|
||||||
PyObject *py_cred = NULL, *arglist = NULL;
|
|
||||||
|
|
||||||
if (credentials == NULL || credentials == Py_None)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
if (!PyCallable_Check(credentials)) {
|
|
||||||
PyErr_SetString(PyExc_TypeError, "credentials callback is not callable");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
arglist = Py_BuildValue("(szI)", url, username_from_url, allowed_types);
|
|
||||||
py_cred = PyObject_CallObject(credentials, arglist);
|
|
||||||
Py_DECREF(arglist);
|
|
||||||
|
|
||||||
if (!py_cred)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
err = py_cred_to_git_cred(out, py_cred, allowed_types);
|
|
||||||
Py_DECREF(py_cred);
|
|
||||||
|
|
||||||
return err;
|
|
||||||
}
|
|
||||||
|
@@ -117,8 +117,6 @@ const char *py_str_borrow_c_str(PyObject **tvaue, PyObject *value, const char *e
|
|||||||
PyObject * get_pylist_from_git_strarray(git_strarray *strarray);
|
PyObject * get_pylist_from_git_strarray(git_strarray *strarray);
|
||||||
int get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist);
|
int get_strarraygit_from_pylist(git_strarray *array, PyObject *pylist);
|
||||||
|
|
||||||
int callable_to_credentials(git_cred **out, const char *url, const char *username_from_url, unsigned int allowed_types, PyObject *credentials);
|
|
||||||
|
|
||||||
#define py_path_to_c_str(py_path) \
|
#define py_path_to_c_str(py_path) \
|
||||||
py_str_to_c_str(py_path, Py_FileSystemDefaultEncoding)
|
py_str_to_c_str(py_path, Py_FileSystemDefaultEncoding)
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user